keventd is inappropriate for running block request queues because keventd itself can get blocked on disk I/O. Via call_usermodehelper()'s vfork and, presumably, GFP_KERNEL allocations. So create a new gang of kernel threads whose mandate is for running low-level disk operations. It must ever block on disk IO, so any memory allocations should be GFP_NOIO. 25-akpm/drivers/block/Makefile | 5 +++++ 25-akpm/drivers/block/ll_rw_blk.c | 21 ++++++++++++++++++++- 25-akpm/include/linux/blkdev.h | 4 ++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff -puN drivers/block/Makefile~kblockd drivers/block/Makefile --- 25/drivers/block/Makefile~kblockd Mon Apr 7 12:21:23 2003 +++ 25-akpm/drivers/block/Makefile Mon Apr 7 12:21:23 2003 @@ -8,6 +8,11 @@ # In the future, some of these should be built conditionally. # +# +# NOTE that ll_rw_blk.c must come early in linkage order - it starts the +# kblockd threads +# + obj-y := elevator.o ll_rw_blk.o ioctl.o genhd.o scsi_ioctl.o deadline-iosched.o obj-$(CONFIG_MAC_FLOPPY) += swim3.o diff -puN drivers/block/ll_rw_blk.c~kblockd drivers/block/ll_rw_blk.c --- 25/drivers/block/ll_rw_blk.c~kblockd Mon Apr 7 12:21:23 2003 +++ 25-akpm/drivers/block/ll_rw_blk.c Mon Apr 7 12:21:23 2003 @@ -57,6 +57,11 @@ static int batch_requests; unsigned long blk_max_low_pfn, blk_max_pfn; int blk_nohighio = 0; +/* + * Controlling structure to kblockd + */ +static struct workqueue_struct *kblockd_workqueue; + static wait_queue_head_t congestion_wqh[2]; /* @@ -2181,11 +2186,25 @@ void end_that_request_last(struct reques __blk_put_request(req->q, req); } +int kblockd_schedule_work(struct work_struct *work) +{ + return queue_work(kblockd_workqueue, work); +} + +void kblockd_flush(void) +{ + flush_workqueue(kblockd_workqueue); +} + int __init blk_dev_init(void) { int total_ram = nr_free_pages() << (PAGE_SHIFT - 10); int i; + kblockd_workqueue = create_workqueue("kblockd"); + if (!kblockd_workqueue) + panic("Failed to create kblockd\n"); + request_cachep = kmem_cache_create("blkdev_requests", sizeof(struct request), 0, 0, NULL, NULL); if (!request_cachep) @@ -2218,7 +2237,7 @@ int __init blk_dev_init(void) for (i = 0; i < ARRAY_SIZE(congestion_wqh); i++) init_waitqueue_head(&congestion_wqh[i]); return 0; -}; +} EXPORT_SYMBOL(end_that_request_first); EXPORT_SYMBOL(end_that_request_chunk); diff -puN include/linux/blkdev.h~kblockd include/linux/blkdev.h --- 25/include/linux/blkdev.h~kblockd Mon Apr 7 12:21:23 2003 +++ 25-akpm/include/linux/blkdev.h Mon Apr 7 12:21:23 2003 @@ -459,6 +459,10 @@ static inline void put_dev_sector(Sector page_cache_release(p.v); } +struct work_struct; +int kblockd_schedule_work(struct work_struct *work); +void kblockd_flush(void); + #ifdef CONFIG_LBD # include # define sector_div(a, b) do_div(a, b) _