mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 02:09:42 +00:00
io-wq: shrink io_wq_work a bit
Currently we're using 40 bytes for the io_wq_work structure, and 16 of those is the doubly link list node. We don't need doubly linked lists, we always add to tail to keep things ordered, and any other use case is list traversal with deletion. For the deletion case, we can easily support any node deletion by keeping track of the previous entry. This shrinks io_wq_work to 32 bytes, and subsequently io_kiock from io_uring to 216 to 208 bytes. Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
3fc50ab559
commit
6206f0e180
2 changed files with 67 additions and 15 deletions
36
fs/io-wq.c
36
fs/io-wq.c
|
@ -84,7 +84,7 @@ enum {
|
||||||
struct io_wqe {
|
struct io_wqe {
|
||||||
struct {
|
struct {
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct list_head work_list;
|
struct io_wq_work_list work_list;
|
||||||
unsigned long hash_map;
|
unsigned long hash_map;
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
} ____cacheline_aligned_in_smp;
|
} ____cacheline_aligned_in_smp;
|
||||||
|
@ -236,7 +236,8 @@ static void io_worker_exit(struct io_worker *worker)
|
||||||
static inline bool io_wqe_run_queue(struct io_wqe *wqe)
|
static inline bool io_wqe_run_queue(struct io_wqe *wqe)
|
||||||
__must_hold(wqe->lock)
|
__must_hold(wqe->lock)
|
||||||
{
|
{
|
||||||
if (!list_empty(&wqe->work_list) && !(wqe->flags & IO_WQE_FLAG_STALLED))
|
if (!wq_list_empty(&wqe->work_list) &&
|
||||||
|
!(wqe->flags & IO_WQE_FLAG_STALLED))
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -375,12 +376,15 @@ static bool __io_worker_idle(struct io_wqe *wqe, struct io_worker *worker)
|
||||||
static struct io_wq_work *io_get_next_work(struct io_wqe *wqe, unsigned *hash)
|
static struct io_wq_work *io_get_next_work(struct io_wqe *wqe, unsigned *hash)
|
||||||
__must_hold(wqe->lock)
|
__must_hold(wqe->lock)
|
||||||
{
|
{
|
||||||
|
struct io_wq_work_node *node, *prev;
|
||||||
struct io_wq_work *work;
|
struct io_wq_work *work;
|
||||||
|
|
||||||
list_for_each_entry(work, &wqe->work_list, list) {
|
wq_list_for_each(node, prev, &wqe->work_list) {
|
||||||
|
work = container_of(node, struct io_wq_work, list);
|
||||||
|
|
||||||
/* not hashed, can run anytime */
|
/* not hashed, can run anytime */
|
||||||
if (!(work->flags & IO_WQ_WORK_HASHED)) {
|
if (!(work->flags & IO_WQ_WORK_HASHED)) {
|
||||||
list_del(&work->list);
|
wq_node_del(&wqe->work_list, node, prev);
|
||||||
return work;
|
return work;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,7 +392,7 @@ static struct io_wq_work *io_get_next_work(struct io_wqe *wqe, unsigned *hash)
|
||||||
*hash = work->flags >> IO_WQ_HASH_SHIFT;
|
*hash = work->flags >> IO_WQ_HASH_SHIFT;
|
||||||
if (!(wqe->hash_map & BIT_ULL(*hash))) {
|
if (!(wqe->hash_map & BIT_ULL(*hash))) {
|
||||||
wqe->hash_map |= BIT_ULL(*hash);
|
wqe->hash_map |= BIT_ULL(*hash);
|
||||||
list_del(&work->list);
|
wq_node_del(&wqe->work_list, node, prev);
|
||||||
return work;
|
return work;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -416,7 +420,7 @@ static void io_worker_handle_work(struct io_worker *worker)
|
||||||
work = io_get_next_work(wqe, &hash);
|
work = io_get_next_work(wqe, &hash);
|
||||||
if (work)
|
if (work)
|
||||||
__io_worker_busy(wqe, worker, work);
|
__io_worker_busy(wqe, worker, work);
|
||||||
else if (!list_empty(&wqe->work_list))
|
else if (!wq_list_empty(&wqe->work_list))
|
||||||
wqe->flags |= IO_WQE_FLAG_STALLED;
|
wqe->flags |= IO_WQE_FLAG_STALLED;
|
||||||
|
|
||||||
spin_unlock_irq(&wqe->lock);
|
spin_unlock_irq(&wqe->lock);
|
||||||
|
@ -526,7 +530,7 @@ static int io_wqe_worker(void *data)
|
||||||
|
|
||||||
if (test_bit(IO_WQ_BIT_EXIT, &wq->state)) {
|
if (test_bit(IO_WQ_BIT_EXIT, &wq->state)) {
|
||||||
spin_lock_irq(&wqe->lock);
|
spin_lock_irq(&wqe->lock);
|
||||||
if (!list_empty(&wqe->work_list))
|
if (!wq_list_empty(&wqe->work_list))
|
||||||
io_worker_handle_work(worker);
|
io_worker_handle_work(worker);
|
||||||
else
|
else
|
||||||
spin_unlock_irq(&wqe->lock);
|
spin_unlock_irq(&wqe->lock);
|
||||||
|
@ -714,7 +718,7 @@ static void io_wqe_enqueue(struct io_wqe *wqe, struct io_wq_work *work)
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&wqe->lock, flags);
|
spin_lock_irqsave(&wqe->lock, flags);
|
||||||
list_add_tail(&work->list, &wqe->work_list);
|
wq_list_add_tail(&work->list, &wqe->work_list);
|
||||||
wqe->flags &= ~IO_WQE_FLAG_STALLED;
|
wqe->flags &= ~IO_WQE_FLAG_STALLED;
|
||||||
spin_unlock_irqrestore(&wqe->lock, flags);
|
spin_unlock_irqrestore(&wqe->lock, flags);
|
||||||
|
|
||||||
|
@ -829,14 +833,17 @@ static enum io_wq_cancel io_wqe_cancel_cb_work(struct io_wqe *wqe,
|
||||||
.cancel = cancel,
|
.cancel = cancel,
|
||||||
.caller_data = cancel_data,
|
.caller_data = cancel_data,
|
||||||
};
|
};
|
||||||
|
struct io_wq_work_node *node, *prev;
|
||||||
struct io_wq_work *work;
|
struct io_wq_work *work;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
spin_lock_irqsave(&wqe->lock, flags);
|
spin_lock_irqsave(&wqe->lock, flags);
|
||||||
list_for_each_entry(work, &wqe->work_list, list) {
|
wq_list_for_each(node, prev, &wqe->work_list) {
|
||||||
|
work = container_of(node, struct io_wq_work, list);
|
||||||
|
|
||||||
if (cancel(work, cancel_data)) {
|
if (cancel(work, cancel_data)) {
|
||||||
list_del(&work->list);
|
wq_node_del(&wqe->work_list, node, prev);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -894,6 +901,7 @@ static bool io_wq_worker_cancel(struct io_worker *worker, void *data)
|
||||||
static enum io_wq_cancel io_wqe_cancel_work(struct io_wqe *wqe,
|
static enum io_wq_cancel io_wqe_cancel_work(struct io_wqe *wqe,
|
||||||
struct io_wq_work *cwork)
|
struct io_wq_work *cwork)
|
||||||
{
|
{
|
||||||
|
struct io_wq_work_node *node, *prev;
|
||||||
struct io_wq_work *work;
|
struct io_wq_work *work;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
@ -906,9 +914,11 @@ static enum io_wq_cancel io_wqe_cancel_work(struct io_wqe *wqe,
|
||||||
* no completion will be posted for it.
|
* no completion will be posted for it.
|
||||||
*/
|
*/
|
||||||
spin_lock_irqsave(&wqe->lock, flags);
|
spin_lock_irqsave(&wqe->lock, flags);
|
||||||
list_for_each_entry(work, &wqe->work_list, list) {
|
wq_list_for_each(node, prev, &wqe->work_list) {
|
||||||
|
work = container_of(node, struct io_wq_work, list);
|
||||||
|
|
||||||
if (work == cwork) {
|
if (work == cwork) {
|
||||||
list_del(&work->list);
|
wq_node_del(&wqe->work_list, node, prev);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1023,7 +1033,7 @@ struct io_wq *io_wq_create(unsigned bounded, struct io_wq_data *data)
|
||||||
wqe->node = node;
|
wqe->node = node;
|
||||||
wqe->wq = wq;
|
wqe->wq = wq;
|
||||||
spin_lock_init(&wqe->lock);
|
spin_lock_init(&wqe->lock);
|
||||||
INIT_LIST_HEAD(&wqe->work_list);
|
INIT_WQ_LIST(&wqe->work_list);
|
||||||
INIT_HLIST_NULLS_HEAD(&wqe->free_list, 0);
|
INIT_HLIST_NULLS_HEAD(&wqe->free_list, 0);
|
||||||
INIT_HLIST_NULLS_HEAD(&wqe->busy_list, 1);
|
INIT_HLIST_NULLS_HEAD(&wqe->busy_list, 1);
|
||||||
INIT_LIST_HEAD(&wqe->all_list);
|
INIT_LIST_HEAD(&wqe->all_list);
|
||||||
|
|
46
fs/io-wq.h
46
fs/io-wq.h
|
@ -22,18 +22,60 @@ enum io_wq_cancel {
|
||||||
IO_WQ_CANCEL_NOTFOUND, /* work not found */
|
IO_WQ_CANCEL_NOTFOUND, /* work not found */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct io_wq_work_node {
|
||||||
|
struct io_wq_work_node *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct io_wq_work_list {
|
||||||
|
struct io_wq_work_node *first;
|
||||||
|
struct io_wq_work_node *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void wq_list_add_tail(struct io_wq_work_node *node,
|
||||||
|
struct io_wq_work_list *list)
|
||||||
|
{
|
||||||
|
if (!list->first) {
|
||||||
|
list->first = list->last = node;
|
||||||
|
} else {
|
||||||
|
list->last->next = node;
|
||||||
|
list->last = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void wq_node_del(struct io_wq_work_list *list,
|
||||||
|
struct io_wq_work_node *node,
|
||||||
|
struct io_wq_work_node *prev)
|
||||||
|
{
|
||||||
|
if (node == list->first)
|
||||||
|
list->first = node->next;
|
||||||
|
if (node == list->last)
|
||||||
|
list->last = prev;
|
||||||
|
if (prev)
|
||||||
|
prev->next = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define wq_list_for_each(pos, prv, head) \
|
||||||
|
for (pos = (head)->first, prv = NULL; pos; prv = pos, pos = (pos)->next)
|
||||||
|
|
||||||
|
#define wq_list_empty(list) ((list)->first == NULL)
|
||||||
|
#define INIT_WQ_LIST(list) do { \
|
||||||
|
(list)->first = NULL; \
|
||||||
|
(list)->last = NULL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
struct io_wq_work {
|
struct io_wq_work {
|
||||||
union {
|
union {
|
||||||
struct list_head list;
|
struct io_wq_work_node list;
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
void (*func)(struct io_wq_work **);
|
void (*func)(struct io_wq_work **);
|
||||||
unsigned flags;
|
|
||||||
struct files_struct *files;
|
struct files_struct *files;
|
||||||
|
unsigned flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define INIT_IO_WORK(work, _func) \
|
#define INIT_IO_WORK(work, _func) \
|
||||||
do { \
|
do { \
|
||||||
|
(work)->list.next = NULL; \
|
||||||
(work)->func = _func; \
|
(work)->func = _func; \
|
||||||
(work)->flags = 0; \
|
(work)->flags = 0; \
|
||||||
(work)->files = NULL; \
|
(work)->files = NULL; \
|
||||||
|
|
Loading…
Reference in a new issue