Introduce the necessary infrastructure for performing writeback operations asynchronously. It adds a dedicated kernel thread (`zram_wb_thread`), a request queue for managing pending writebacks, and helper functions to deal with the writeback requests. This patch lays the foundation for enabling asynchronous writeback in a subsequent change. Signed-off-by: Richard Chang <richardycc@xxxxxxxxxx> --- drivers/block/zram/zram_drv.c | 4 ++ drivers/block/zram/zram_wb.c | 114 ++++++++++++++++++++++++++++++++++ drivers/block/zram/zram_wb.h | 22 +++++++ 3 files changed, 140 insertions(+) diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index ec8649cad21e..6098c0bb773c 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -2871,6 +2871,9 @@ static int __init zram_init(void) num_devices--; } + if (setup_zram_writeback()) + goto out_error; + return 0; out_error: @@ -2880,6 +2883,7 @@ static int __init zram_init(void) static void __exit zram_exit(void) { + destroy_zram_writeback(); destroy_devices(); } diff --git a/drivers/block/zram/zram_wb.c b/drivers/block/zram/zram_wb.c index 0bc10f725939..63a16dae5796 100644 --- a/drivers/block/zram/zram_wb.c +++ b/drivers/block/zram/zram_wb.c @@ -5,9 +5,16 @@ #include <linux/module.h> #include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/wait.h> +#include <linux/freezer.h> #include "zram_wb.h" +static struct task_struct *wb_thread; +static DECLARE_WAIT_QUEUE_HEAD(wb_wq); +static struct zram_wb_request_list wb_req_list; + unsigned long alloc_block_bdev(struct zram *zram) { unsigned long blk_idx = 1; @@ -33,3 +40,110 @@ void free_block_bdev(struct zram *zram, unsigned long blk_idx) atomic64_dec(&zram->stats.bd_count); } +static void complete_wb_request(struct zram_wb_request *req) +{ + struct zram *zram = req->zram; + unsigned long blk_idx = req->blk_idx; + + free_block_bdev(zram, blk_idx); + free_wb_request(req); +} + +void enqueue_wb_request(struct zram_wb_request_list *req_list, + struct zram_wb_request *req) +{ + spin_lock_bh(&req_list->lock); + list_add_tail(&req->node, &req_list->head); + req_list->count++; + spin_unlock_bh(&req_list->lock); +} + +static struct zram_wb_request *dequeue_wb_request( + struct zram_wb_request_list *req_list) +{ + struct zram_wb_request *req = NULL; + + spin_lock_bh(&req_list->lock); + if (!list_empty(&req_list->head)) { + req = list_first_entry(&req_list->head, + struct zram_wb_request, + node); + list_del(&req->node); + req_list->count--; + } + spin_unlock_bh(&req_list->lock); + + return req; +} + +static void destroy_wb_request_list(struct zram_wb_request_list *req_list) +{ + struct zram_wb_request *req; + + while (!list_empty(&req_list->head)) { + req = dequeue_wb_request(req_list); + free_block_bdev(req->zram, req->blk_idx); + free_wb_request(req); + } +} + +static bool wb_ready_to_run(void) +{ + int count; + + spin_lock_bh(&wb_req_list.lock); + count = wb_req_list.count; + spin_unlock_bh(&wb_req_list.lock); + + return count > 0; +} + +static int wb_thread_func(void *data) +{ + set_freezable(); + + while (!kthread_should_stop()) { + wait_event_freezable(wb_wq, wb_ready_to_run()); + + while (1) { + struct zram_wb_request *req; + + req = dequeue_wb_request(&wb_req_list); + if (!req) + break; + complete_wb_request(req); + } + } + return 0; +} + +void free_wb_request(struct zram_wb_request *req) +{ + struct bio *bio = req->bio; + struct page *page = bio_first_page_all(bio); + + __free_page(page); + bio_put(bio); + kfree(req); +} + +int setup_zram_writeback(void) +{ + spin_lock_init(&wb_req_list.lock); + INIT_LIST_HEAD(&wb_req_list.head); + wb_req_list.count = 0; + + wb_thread = kthread_run(wb_thread_func, NULL, "zram_wb_thread"); + if (IS_ERR(wb_thread)) { + pr_err("Unable to create zram_wb_thread\n"); + return -1; + } + return 0; +} + +void destroy_zram_writeback(void) +{ + kthread_stop(wb_thread); + destroy_wb_request_list(&wb_req_list); +} + diff --git a/drivers/block/zram/zram_wb.h b/drivers/block/zram/zram_wb.h index c2f5984e7aa2..b86de0398346 100644 --- a/drivers/block/zram/zram_wb.h +++ b/drivers/block/zram/zram_wb.h @@ -6,12 +6,34 @@ #include <linux/bio.h> #include "zram_drv.h" +struct zram_wb_request { + struct zram *zram; + unsigned long blk_idx; + struct zram_pp_slot *pps; + struct zram_pp_ctl *ppctl; + struct bio *bio; + struct list_head node; +}; + +struct zram_wb_request_list { + struct list_head head; + int count; + spinlock_t lock; +}; + #if IS_ENABLED(CONFIG_ZRAM_WRITEBACK) unsigned long alloc_block_bdev(struct zram *zram); void free_block_bdev(struct zram *zram, unsigned long blk_idx); +int setup_zram_writeback(void); +void destroy_zram_writeback(void); +void free_wb_request(struct zram_wb_request *req); +void enqueue_wb_request(struct zram_wb_request_list *req_list, + struct zram_wb_request *req); #else inline unsigned long alloc_block_bdev(struct zram *zram) { return 0; } inline void free_block_bdev(struct zram *zram, unsigned long blk_idx) {}; +inline int setup_zram_writeback(void) { return 0; } +inline void destroy_zram_writeback(void) {} #endif #endif /* _ZRAM_WRITEBACK_H_ */ -- 2.50.1.565.gc32cd1483b-goog