Skip to content

Commit fa35955

Browse files
committed
io_uring: get rid of alloc cache init_once handling
init_once is called when an object doesn't come from the cache, and hence needs initial clearing of certain members. While the whole struct could get cleared by memset() in that case, a few of the cache members are large enough that this may cause unnecessary overhead if the caches used aren't large enough to satisfy the workload. For those cases, some churn of kmalloc+kfree is to be expected. Ensure that the 3 users that need clearing put the members they need cleared at the start of the struct, and wrap the rest of the struct in a struct group so the offset is known. While at it, improve the interaction with KASAN such that when/if KASAN writes to members inside the struct that should be retained over caching, it won't trip over itself. For rw and net, the retaining of the iovec over caching is disabled if KASAN is enabled. A helper will free and clear those members in that case. Signed-off-by: Jens Axboe <[email protected]>
1 parent eaf72f7 commit fa35955

File tree

12 files changed

+91
-93
lines changed

12 files changed

+91
-93
lines changed

include/linux/io_uring/cmd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ struct io_uring_cmd {
1919
};
2020

2121
struct io_uring_cmd_data {
22-
struct io_uring_sqe sqes[2];
2322
void *op_data;
23+
struct io_uring_sqe sqes[2];
2424
};
2525

2626
static inline const void *io_uring_sqe_cmd(const struct io_uring_sqe *sqe)

include/linux/io_uring_types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ struct io_alloc_cache {
222222
void **entries;
223223
unsigned int nr_cached;
224224
unsigned int max_cached;
225-
size_t elem_size;
225+
unsigned int elem_size;
226+
unsigned int init_clear;
226227
};
227228

228229
struct io_ring_ctx {

io_uring/alloc_cache.h

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@
66
*/
77
#define IO_ALLOC_CACHE_MAX 128
88

9+
#if defined(CONFIG_KASAN)
10+
static inline void io_alloc_cache_kasan(struct iovec **iov, int *nr)
11+
{
12+
kfree(*iov);
13+
*iov = NULL;
14+
*nr = 0;
15+
}
16+
#else
17+
static inline void io_alloc_cache_kasan(struct iovec **iov, int *nr)
18+
{
19+
}
20+
#endif
21+
922
static inline bool io_alloc_cache_put(struct io_alloc_cache *cache,
1023
void *entry)
1124
{
@@ -23,35 +36,47 @@ static inline void *io_alloc_cache_get(struct io_alloc_cache *cache)
2336
if (cache->nr_cached) {
2437
void *entry = cache->entries[--cache->nr_cached];
2538

39+
/*
40+
* If KASAN is enabled, always clear the initial bytes that
41+
* must be zeroed post alloc, in case any of them overlap
42+
* with KASAN storage.
43+
*/
44+
#if defined(CONFIG_KASAN)
2645
kasan_mempool_unpoison_object(entry, cache->elem_size);
46+
if (cache->init_clear)
47+
memset(entry, 0, cache->init_clear);
48+
#endif
2749
return entry;
2850
}
2951

3052
return NULL;
3153
}
3254

33-
static inline void *io_cache_alloc(struct io_alloc_cache *cache, gfp_t gfp,
34-
void (*init_once)(void *obj))
55+
static inline void *io_cache_alloc(struct io_alloc_cache *cache, gfp_t gfp)
3556
{
36-
if (unlikely(!cache->nr_cached)) {
37-
void *obj = kmalloc(cache->elem_size, gfp);
57+
void *obj;
3858

39-
if (obj && init_once)
40-
init_once(obj);
59+
obj = io_alloc_cache_get(cache);
60+
if (obj)
4161
return obj;
42-
}
43-
return io_alloc_cache_get(cache);
62+
63+
obj = kmalloc(cache->elem_size, gfp);
64+
if (obj && cache->init_clear)
65+
memset(obj, 0, cache->init_clear);
66+
return obj;
4467
}
4568

4669
/* returns false if the cache was initialized properly */
4770
static inline bool io_alloc_cache_init(struct io_alloc_cache *cache,
48-
unsigned max_nr, size_t size)
71+
unsigned max_nr, unsigned int size,
72+
unsigned int init_bytes)
4973
{
5074
cache->entries = kvmalloc_array(max_nr, sizeof(void *), GFP_KERNEL);
5175
if (cache->entries) {
5276
cache->nr_cached = 0;
5377
cache->max_cached = max_nr;
5478
cache->elem_size = size;
79+
cache->init_clear = init_bytes;
5580
return false;
5681
}
5782
return true;

io_uring/futex.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct io_futex_data {
3636
bool io_futex_cache_init(struct io_ring_ctx *ctx)
3737
{
3838
return io_alloc_cache_init(&ctx->futex_cache, IO_FUTEX_ALLOC_CACHE_MAX,
39-
sizeof(struct io_futex_data));
39+
sizeof(struct io_futex_data), 0);
4040
}
4141

4242
void io_futex_cache_free(struct io_ring_ctx *ctx)
@@ -320,7 +320,7 @@ int io_futex_wait(struct io_kiocb *req, unsigned int issue_flags)
320320
}
321321

322322
io_ring_submit_lock(ctx, issue_flags);
323-
ifd = io_cache_alloc(&ctx->futex_cache, GFP_NOWAIT, NULL);
323+
ifd = io_cache_alloc(&ctx->futex_cache, GFP_NOWAIT);
324324
if (!ifd) {
325325
ret = -ENOMEM;
326326
goto done_unlock;

io_uring/io_uring.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -315,16 +315,18 @@ static __cold struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
315315
INIT_LIST_HEAD(&ctx->cq_overflow_list);
316316
INIT_LIST_HEAD(&ctx->io_buffers_cache);
317317
ret = io_alloc_cache_init(&ctx->apoll_cache, IO_POLL_ALLOC_CACHE_MAX,
318-
sizeof(struct async_poll));
318+
sizeof(struct async_poll), 0);
319319
ret |= io_alloc_cache_init(&ctx->netmsg_cache, IO_ALLOC_CACHE_MAX,
320-
sizeof(struct io_async_msghdr));
320+
sizeof(struct io_async_msghdr),
321+
offsetof(struct io_async_msghdr, clear));
321322
ret |= io_alloc_cache_init(&ctx->rw_cache, IO_ALLOC_CACHE_MAX,
322-
sizeof(struct io_async_rw));
323+
sizeof(struct io_async_rw),
324+
offsetof(struct io_async_rw, clear));
323325
ret |= io_alloc_cache_init(&ctx->uring_cache, IO_ALLOC_CACHE_MAX,
324-
sizeof(struct io_uring_cmd_data));
326+
sizeof(struct io_uring_cmd_data), 0);
325327
spin_lock_init(&ctx->msg_lock);
326328
ret |= io_alloc_cache_init(&ctx->msg_cache, IO_ALLOC_CACHE_MAX,
327-
sizeof(struct io_kiocb));
329+
sizeof(struct io_kiocb), 0);
328330
ret |= io_futex_cache_init(ctx);
329331
if (ret)
330332
goto free_ref;

io_uring/io_uring.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,9 @@ static inline void io_req_set_res(struct io_kiocb *req, s32 res, u32 cflags)
226226
}
227227

228228
static inline void *io_uring_alloc_async_data(struct io_alloc_cache *cache,
229-
struct io_kiocb *req,
230-
void (*init_once)(void *obj))
229+
struct io_kiocb *req)
231230
{
232-
req->async_data = io_cache_alloc(cache, GFP_KERNEL, init_once);
231+
req->async_data = io_cache_alloc(cache, GFP_KERNEL);
233232
if (req->async_data)
234233
req->flags |= REQ_F_ASYNC_DATA;
235234
return req->async_data;

io_uring/net.c

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ static void io_netmsg_iovec_free(struct io_async_msghdr *kmsg)
137137
static void io_netmsg_recycle(struct io_kiocb *req, unsigned int issue_flags)
138138
{
139139
struct io_async_msghdr *hdr = req->async_data;
140-
struct iovec *iov;
141140

142141
/* can't recycle, ensure we free the iovec if we have one */
143142
if (unlikely(issue_flags & IO_URING_F_UNLOCKED)) {
@@ -146,39 +145,25 @@ static void io_netmsg_recycle(struct io_kiocb *req, unsigned int issue_flags)
146145
}
147146

148147
/* Let normal cleanup path reap it if we fail adding to the cache */
149-
iov = hdr->free_iov;
148+
io_alloc_cache_kasan(&hdr->free_iov, &hdr->free_iov_nr);
150149
if (io_alloc_cache_put(&req->ctx->netmsg_cache, hdr)) {
151-
if (iov)
152-
kasan_mempool_poison_object(iov);
153150
req->async_data = NULL;
154151
req->flags &= ~REQ_F_ASYNC_DATA;
155152
}
156153
}
157154

158-
static void io_msg_async_data_init(void *obj)
159-
{
160-
struct io_async_msghdr *hdr = (struct io_async_msghdr *)obj;
161-
162-
hdr->free_iov = NULL;
163-
hdr->free_iov_nr = 0;
164-
}
165-
166155
static struct io_async_msghdr *io_msg_alloc_async(struct io_kiocb *req)
167156
{
168157
struct io_ring_ctx *ctx = req->ctx;
169158
struct io_async_msghdr *hdr;
170159

171-
hdr = io_uring_alloc_async_data(&ctx->netmsg_cache, req,
172-
io_msg_async_data_init);
160+
hdr = io_uring_alloc_async_data(&ctx->netmsg_cache, req);
173161
if (!hdr)
174162
return NULL;
175163

176164
/* If the async data was cached, we might have an iov cached inside. */
177-
if (hdr->free_iov) {
178-
kasan_mempool_unpoison_object(hdr->free_iov,
179-
hdr->free_iov_nr * sizeof(struct iovec));
165+
if (hdr->free_iov)
180166
req->flags |= REQ_F_NEED_CLEANUP;
181-
}
182167
return hdr;
183168
}
184169

@@ -1813,11 +1798,10 @@ void io_netmsg_cache_free(const void *entry)
18131798
{
18141799
struct io_async_msghdr *kmsg = (struct io_async_msghdr *) entry;
18151800

1816-
if (kmsg->free_iov) {
1817-
kasan_mempool_unpoison_object(kmsg->free_iov,
1818-
kmsg->free_iov_nr * sizeof(struct iovec));
1801+
#if !defined(CONFIG_KASAN)
1802+
if (kmsg->free_iov)
18191803
io_netmsg_iovec_free(kmsg);
1820-
}
1804+
#endif
18211805
kfree(kmsg);
18221806
}
18231807
#endif

io_uring/net.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,20 @@
55

66
struct io_async_msghdr {
77
#if defined(CONFIG_NET)
8-
struct iovec fast_iov;
9-
/* points to an allocated iov, if NULL we use fast_iov instead */
108
struct iovec *free_iov;
9+
/* points to an allocated iov, if NULL we use fast_iov instead */
1110
int free_iov_nr;
12-
int namelen;
13-
__kernel_size_t controllen;
14-
__kernel_size_t payloadlen;
15-
struct sockaddr __user *uaddr;
16-
struct msghdr msg;
17-
struct sockaddr_storage addr;
11+
struct_group(clear,
12+
int namelen;
13+
struct iovec fast_iov;
14+
__kernel_size_t controllen;
15+
__kernel_size_t payloadlen;
16+
struct sockaddr __user *uaddr;
17+
struct msghdr msg;
18+
struct sockaddr_storage addr;
19+
);
20+
#else
21+
struct_group(clear);
1822
#endif
1923
};
2024

io_uring/poll.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,7 @@ static struct async_poll *io_req_alloc_apoll(struct io_kiocb *req,
650650
kfree(apoll->double_poll);
651651
} else {
652652
if (!(issue_flags & IO_URING_F_UNLOCKED))
653-
apoll = io_cache_alloc(&ctx->apoll_cache, GFP_ATOMIC, NULL);
653+
apoll = io_cache_alloc(&ctx->apoll_cache, GFP_ATOMIC);
654654
else
655655
apoll = kmalloc(sizeof(*apoll), GFP_ATOMIC);
656656
if (!apoll)

io_uring/rw.c

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,13 @@ static void io_rw_iovec_free(struct io_async_rw *rw)
158158
static void io_rw_recycle(struct io_kiocb *req, unsigned int issue_flags)
159159
{
160160
struct io_async_rw *rw = req->async_data;
161-
struct iovec *iov;
162161

163162
if (unlikely(issue_flags & IO_URING_F_UNLOCKED)) {
164163
io_rw_iovec_free(rw);
165164
return;
166165
}
167-
iov = rw->free_iovec;
166+
io_alloc_cache_kasan(&rw->free_iovec, &rw->free_iov_nr);
168167
if (io_alloc_cache_put(&req->ctx->rw_cache, rw)) {
169-
if (iov)
170-
kasan_mempool_poison_object(iov);
171168
req->async_data = NULL;
172169
req->flags &= ~REQ_F_ASYNC_DATA;
173170
}
@@ -208,27 +205,16 @@ static void io_req_rw_cleanup(struct io_kiocb *req, unsigned int issue_flags)
208205
}
209206
}
210207

211-
static void io_rw_async_data_init(void *obj)
212-
{
213-
struct io_async_rw *rw = (struct io_async_rw *)obj;
214-
215-
rw->free_iovec = NULL;
216-
rw->bytes_done = 0;
217-
}
218-
219208
static int io_rw_alloc_async(struct io_kiocb *req)
220209
{
221210
struct io_ring_ctx *ctx = req->ctx;
222211
struct io_async_rw *rw;
223212

224-
rw = io_uring_alloc_async_data(&ctx->rw_cache, req, io_rw_async_data_init);
213+
rw = io_uring_alloc_async_data(&ctx->rw_cache, req);
225214
if (!rw)
226215
return -ENOMEM;
227-
if (rw->free_iovec) {
228-
kasan_mempool_unpoison_object(rw->free_iovec,
229-
rw->free_iov_nr * sizeof(struct iovec));
216+
if (rw->free_iovec)
230217
req->flags |= REQ_F_NEED_CLEANUP;
231-
}
232218
rw->bytes_done = 0;
233219
return 0;
234220
}
@@ -1323,10 +1309,9 @@ void io_rw_cache_free(const void *entry)
13231309
{
13241310
struct io_async_rw *rw = (struct io_async_rw *) entry;
13251311

1326-
if (rw->free_iovec) {
1327-
kasan_mempool_unpoison_object(rw->free_iovec,
1328-
rw->free_iov_nr * sizeof(struct iovec));
1312+
#if !defined(CONFIG_KASAN)
1313+
if (rw->free_iovec)
13291314
io_rw_iovec_free(rw);
1330-
}
1315+
#endif
13311316
kfree(rw);
13321317
}

0 commit comments

Comments
 (0)