# This patch changes the ubd I/O submission process to avoid some sleeping. # When the host returns -EAGAIN from io_submit, do_ubd_request returns to # its caller, saving the current state of the request submission in the # struct ubd. This state consists of the request structure and the range # of sg entries which have not yet been submitted. If the request queue is # drained, then this state is reset to indicate that, the next time it is # called, a new request needs to be pulled from the request queue. # When do_ubd_request returns because the host can handle no more requests, # it is necessary to rerun the queue after some completions have been handled. # This is done by adding the device to the restart list. ubd_intr walks # this list before returning, calling do_ubd_request for each device. # In addition, the queues and queue locks are now per-device, rather than # having a single queue and lock for all devices. # Note that kmalloc is still called, and can sleep. This is fixed in a # future patch. Index: test/arch/um/drivers/ubd_kern.c =================================================================== --- test.orig/arch/um/drivers/ubd_kern.c 2005-09-27 11:33:43.000000000 -0400 +++ test/arch/um/drivers/ubd_kern.c 2005-09-27 12:02:00.000000000 -0400 @@ -82,7 +82,7 @@ unsigned long *bitmap_len_out, int *data_offset_out); extern int read_cow_bitmap(int fd, void *buf, int offset, int len); -extern void do_io(struct io_thread_req *req, struct request *r, +static int do_io(struct io_thread_req *req, struct request *r, unsigned long *bitmap); static inline int ubd_test_bit(__u64 bit, void *data) @@ -112,7 +112,6 @@ #define DRIVER_NAME "uml-blkdev" -static DEFINE_SPINLOCK(ubd_io_lock); static DEFINE_SPINLOCK(ubd_lock); static int ubd_open(struct inode * inode, struct file * filp); @@ -129,9 +128,6 @@ .ioctl = ubd_ioctl, }; -/* Protected by the queue_lock */ -static request_queue_t *ubd_queue; - /* Protected by ubd_lock */ static int fake_major = MAJOR_NR; @@ -164,6 +160,7 @@ #define MAX_SG 64 struct ubd { + struct list_head restart; char *file; int count; int fd; @@ -174,6 +171,10 @@ struct cow cow; struct platform_device pdev; struct scatterlist sg[MAX_SG]; + struct request_queue *queue; + spinlock_t lock; + struct request *request; + int start_sg, end_sg; }; #define DEFAULT_COW { \ @@ -193,6 +194,10 @@ .openflags = OPEN_FLAGS, \ .no_cow = 0, \ .cow = DEFAULT_COW, \ + .lock = SPIN_LOCK_UNLOCKED, \ + .request = NULL, \ + .start_sg = 0, \ + .end_sg = 0, \ } struct ubd ubd_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD }; @@ -466,7 +471,6 @@ ); static void do_ubd_request(request_queue_t * q); -static int in_ubd; /* Changed by ubd_handler, which is serialized because interrupts only * happen on CPU 0. @@ -494,9 +498,11 @@ static inline void ubd_finish(struct request *req, int bytes) { - spin_lock(&ubd_io_lock); + struct ubd *dev = req->rq_disk->private_data; + + spin_lock(&dev->lock); __ubd_finish(req, bytes); - spin_unlock(&ubd_io_lock); + spin_unlock(&dev->lock); } struct bitmap_io { @@ -513,12 +519,16 @@ }; static int ubd_reply_fd = -1; +static struct list_head restart = LIST_HEAD_INIT(restart); static irqreturn_t ubd_intr(int irq, void *dev, struct pt_regs *unused) { struct aio_thread_reply reply; struct ubd_aio *aio; struct request *req; + struct ubd *ubd; + struct list_head *list, *next; + unsigned long flags; int err, n, fd = (int) (long) dev; while(1){ @@ -532,10 +542,10 @@ } aio = container_of(reply.data, struct ubd_aio, aio); + req = aio->req; n = reply.err; if(n == 0){ - req = aio->req; req->nr_sectors -= aio->len >> 9; if((aio->bitmap != NULL) && @@ -559,7 +569,7 @@ } } else if(n < 0){ - ubd_finish(aio->req, n); + ubd_finish(req, n); if(aio->bitmap != NULL) kfree(aio->bitmap); if(aio->bitmap_buf != NULL) @@ -567,10 +577,15 @@ kfree(aio); } } - reactivate_fd(fd, UBD_IRQ); - - do_ubd_request(ubd_queue); + list_for_each_safe(list, next, &restart){ + ubd = container_of(list, struct ubd, restart); + list_del_init(&ubd->restart); + spin_lock_irqsave(&ubd->lock, flags); + do_ubd_request(ubd->queue); + spin_unlock_irqrestore(&ubd->lock, flags); + } + reactivate_fd(fd, UBD_IRQ); return(IRQ_HANDLED); } @@ -693,7 +708,7 @@ } disk->private_data = &ubd_dev[unit]; - disk->queue = ubd_queue; + disk->queue = ubd_dev[unit].queue; add_disk(disk); *disk_out = disk; @@ -719,10 +734,19 @@ goto out_close; dev->size = ROUND_BLOCK(dev->size); + INIT_LIST_HEAD(&dev->restart); + + err = -ENOMEM; + dev->queue = blk_init_queue(do_ubd_request, &dev->lock); + if (!dev->queue) + goto out_close; + blk_queue_max_hw_segments(dev->queue, MAX_SG); + dev->queue->queuedata = dev; + err = ubd_new_disk(MAJOR_NR, dev->size, n, &ubd_gendisk[n]); if(err) - goto out_close; + goto out_cleanup; if(fake_major != MAJOR_NR) ubd_new_disk(fake_major, dev->size, n, @@ -738,6 +762,10 @@ ubd_close(dev); out: return err; + +out_cleanup: + blk_cleanup_queue(dev->queue); + goto out_close; } static int ubd_config(char *str) @@ -878,13 +906,6 @@ if (register_blkdev(MAJOR_NR, "ubd")) return -1; - ubd_queue = blk_init_queue(do_ubd_request, &ubd_io_lock); - if (!ubd_queue) { - unregister_blkdev(MAJOR_NR, "ubd"); - return -1; - } - - blk_queue_max_hw_segments(ubd_queue, MAX_SG); if (fake_major != MAJOR_NR) { char name[sizeof("ubd_nnn\0")]; @@ -956,22 +977,13 @@ } } -/* Called with ubd_io_lock held */ -static int prepare_request(struct request *req, struct io_thread_req *io_req, - unsigned long long offset, int page_offset, - int len, struct page *page) +static void prepare_request(struct request *req, struct io_thread_req *io_req, + unsigned long long offset, int page_offset, + int len, struct page *page) { struct gendisk *disk = req->rq_disk; struct ubd *dev = disk->private_data; - /* This should be impossible now */ - if((rq_data_dir(req) == WRITE) && !dev->openflags.w){ - printk("Write attempted on readonly ubd device %s\n", - disk->disk_name); - ubd_end_request(req, 0, 0); - return(1); - } - io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd; io_req->fds[1] = dev->fd; io_req->offset = offset; @@ -988,44 +1000,51 @@ if((dev->cow.file != NULL) && (io_req->op == UBD_WRITE)) cowify_bitmap(io_req, dev->cow.bitmap); - return(0); } -/* Called with ubd_io_lock held */ +/* Called with dev->lock held */ static void do_ubd_request(request_queue_t *q) { struct io_thread_req io_req; struct request *req; - __u64 sector; - int err; - if(in_ubd) - return; - in_ubd = 1; - while((req = elv_next_request(q)) != NULL){ - struct gendisk *disk = req->rq_disk; - struct ubd *dev = disk->private_data; - int n, i; + while(1){ + struct ubd *dev = q->queuedata; - blkdev_dequeue_request(req); + if(dev->end_sg == 0){ + struct request *req = elv_next_request(q); + if(req == NULL) + return; + + dev->request = req; + blkdev_dequeue_request(req); + dev->start_sg = 0; + dev->end_sg = blk_rq_map_sg(q, req, dev->sg); + } - sector = req->sector; - n = blk_rq_map_sg(q, req, dev->sg); + req = dev->request; - for(i = 0; i < n; i++){ - struct scatterlist *sg = &dev->sg[i]; + while(dev->start_sg < dev->end_sg){ + struct scatterlist *sg = &dev->sg[dev->start_sg]; - err = prepare_request(req, &io_req, sector << 9, + err = prepare_request(req, &io_req, req->sector << 9, sg->offset, sg->length, sg->page); if(err) continue; - sector += sg->length >> 9; - do_io(&io_req, req, dev->cow.bitmap); + if(do_io(&io_req, req, dev->cow.bitmap) == -EAGAIN){ + if(list_empty(&dev->restart)) + list_add(&dev->restart, &restart); + return; + } + + req->sector += sg->length >> 9; + dev->start_sg++; } + dev->end_sg = 0; + dev->request = NULL; } - in_ubd = 0; } static int ubd_ioctl(struct inode * inode, struct file * file, @@ -1241,7 +1260,8 @@ return(err); } -void do_io(struct io_thread_req *req, struct request *r, unsigned long *bitmap) +static int do_io(struct io_thread_req *req, struct request *r, + unsigned long *bitmap) { struct ubd_aio *aio; struct bitmap_io *bitmap_io = NULL; @@ -1265,7 +1285,7 @@ if(bitmap_io == NULL){ printk("Failed to kmalloc bitmap IO\n"); req->error = 1; - return; + return -ENOMEM; } bitmap_buf = kmalloc(len, GFP_KERNEL); @@ -1274,7 +1294,7 @@ "failed\n"); kfree(bitmap_io); req->error = 1; - return; + return -ENOMEM; } memcpy(bitmap_buf, &bitmap[off / sizeof(bitmap[0])], len); @@ -1308,7 +1328,7 @@ aio = kmalloc(sizeof(*aio), GFP_KERNEL); if(aio == NULL){ req->error = 1; - return; + return -ENOMEM; } *aio = ((struct ubd_aio) @@ -1322,14 +1342,18 @@ if(aio->bitmap != NULL) atomic_inc(&aio->bitmap->count); - err = submit_aio(&aio->aio); + err = submit_aio(&aio->aio); if(err){ - printk("do_io - submit_aio failed, " - "err = %d\n", err); - req->error = 1; - return; + if(err != -EAGAIN){ + printk("do_io - submit_aio failed, " + "err = %d\n", err); + req->error = 1; + } + return err; } start = end; } while(start < nsectors); + + return 0; } Index: test/arch/um/os-Linux/aio.c =================================================================== --- test.orig/arch/um/os-Linux/aio.c 2005-09-27 11:33:43.000000000 -0400 +++ test/arch/um/os-Linux/aio.c 2005-09-27 12:02:00.000000000 -0400 @@ -296,6 +296,9 @@ int err; err = do_aio(ctx, aio); + if(err == -EAGAIN) + return err; + if(err){ reply = ((struct aio_thread_reply) { .data = aio, .err = err });