From: Steven Smith Date: Tue, 30 Jun 2009 11:55:47 +0000 (+0100) Subject: patch CA-15999-blkback-pause-unpause X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=2579876014e75b262fc42235af43d5b3c018878f;p=people%2Fssmith%2Fnc2-2.6.27.git patch CA-15999-blkback-pause-unpause --- diff --git a/drivers/xen/blkback/blkback.c b/drivers/xen/blkback/blkback.c index fc9a02f9..1305cc86 100644 --- a/drivers/xen/blkback/blkback.c +++ b/drivers/xen/blkback/blkback.c @@ -185,6 +185,61 @@ static void fast_flush_area(pending_req_t *req) BUG_ON(ret); } +/****************************************************************** + * QUEUE MANAGEMENT FUNCTIONS + */ + +static inline int blkif_activate(blkif_t *blkif) +{ + mutex_lock(&blkif->queue_mutex); + + if (atomic_read(&blkif->requests_pending) == 0 && + blkif->queue_state == QUEUE_PAUSE_REQUESTED) { + blkif->queue_state = QUEUE_PAUSED; + blkback_signal_paused(blkif); + } + + mutex_unlock(&blkif->queue_mutex); + + if (blkif->remove_requested) + return 1; + + if (blkif->waiting_reqs) + return (!list_empty(&pending_free) && + blkif->queue_state == QUEUE_RUNNING); + + return 0; +} + +void blkback_pause(blkif_t *blkif) +{ + mutex_lock(&blkif->queue_mutex); + + if (atomic_read(&blkif->requests_pending) == 0) { + blkif->queue_state = QUEUE_PAUSED; + blkback_signal_paused(blkif); + } else + blkif->queue_state = QUEUE_PAUSE_REQUESTED; + + mutex_unlock(&blkif->queue_mutex); +} + +void blkback_resume(blkif_t *blkif) +{ + mutex_lock(&blkif->queue_mutex); + + if (blkif->queue_state == QUEUE_PAUSED || + blkif->queue_state == QUEUE_PAUSE_REQUESTED) { + blkif->queue_state = QUEUE_RUNNING; + if (blkif->xenblkd) + wake_up_process(blkif->xenblkd); + } + + blkback_signal_resumed(blkif); + + mutex_unlock(&blkif->queue_mutex); +} + /****************************************************************** * SCHEDULER FUNCTIONS */ @@ -213,12 +268,15 @@ int blkif_schedule(void *arg) if (try_to_freeze()) continue; - wait_event_interruptible( - blkif->wq, - blkif->waiting_reqs || blkif->remove_requested); - wait_event_interruptible( - pending_free_wq, - !list_empty(&pending_free) || blkif->remove_requested); + wait_event_interruptible(pending_free_wq, + blkif_activate(blkif)); + + mutex_lock(&blkif->queue_mutex); + + if (blkif->queue_state != QUEUE_RUNNING) { + mutex_unlock(&blkif->queue_mutex); + continue; + } blkif->waiting_reqs = 0; smp_mb(); /* clear flag *before* checking for work */ @@ -227,6 +285,8 @@ int blkif_schedule(void *arg) blkif->waiting_reqs = 1; unplug_queue(blkif); + mutex_unlock(&blkif->queue_mutex); + if (log_stats && time_after(jiffies, blkif->st_print)) print_stats(blkif); } @@ -261,11 +321,16 @@ static void __end_block_io_op(pending_req_t *pending_req, int error) } if (atomic_dec_and_test(&pending_req->pendcnt)) { + blkif_t *blkif = pending_req->blkif; + fast_flush_area(pending_req); make_response(pending_req->blkif, pending_req->id, pending_req->operation, pending_req->status); blkif_put(pending_req->blkif); free_req(pending_req); + + if (atomic_dec_and_test(&blkif->requests_pending)) + wake_up_process(blkif->xenblkd); } } @@ -283,7 +348,8 @@ static void end_block_io_op(struct bio *bio, int error) void blkif_notify_work(blkif_t *blkif) { blkif->waiting_reqs = 1; - wake_up(&blkif->wq); + if (blkif->xenblkd) + wake_up_process(blkif->xenblkd); } irqreturn_t blkif_be_int(int irq, void *dev_id) @@ -478,6 +544,7 @@ static void dispatch_rw_block_io(blkif_t *blkif, plug_queue(blkif, preq.bdev); atomic_set(&pending_req->pendcnt, 1); blkif_get(blkif); + atomic_inc(&blkif->requests_pending); for (i = 0; i < nseg; i++) { if (((int)preq.sector_number|(int)seg[i].nsec) & diff --git a/drivers/xen/blkback/common.h b/drivers/xen/blkback/common.h index da9f8223..039363f6 100644 --- a/drivers/xen/blkback/common.h +++ b/drivers/xen/blkback/common.h @@ -54,6 +54,10 @@ do { \ printk(KERN_WARNING "blk_back: " fmt, ##args); \ } while(0) +#define QUEUE_RUNNING 1 +#define QUEUE_PAUSE_REQUESTED 2 +#define QUEUE_PAUSED 3 + struct vbd { blkif_vdev_t handle; /* what the domain refers to this vbd as */ unsigned char readonly; /* Non-zero -> read-only */ @@ -83,11 +87,15 @@ typedef struct blkif_st { spinlock_t blk_ring_lock; atomic_t refcnt; - wait_queue_head_t wq; struct task_struct *xenblkd; unsigned int waiting_reqs; struct request_queue *plug; + /* queue management */ + int queue_state; + struct mutex queue_mutex; + atomic_t requests_pending; + /* statistics */ unsigned long st_print; int st_rd_req; @@ -146,6 +154,11 @@ void blkif_notify_work(blkif_t *blkif); int blkback_barrier(struct xenbus_transaction xbt, struct backend_info *be, int state); +void blkback_pause(blkif_t *blkif); +void blkback_signal_paused(blkif_t *blkif); +void blkback_resume(blkif_t *blkif); +void blkback_signal_resumed(blkif_t *blkif); + void blkback_close(blkif_t *blkif); #endif /* __BLKIF__BACKEND__COMMON_H__ */ diff --git a/drivers/xen/blkback/interface.c b/drivers/xen/blkback/interface.c index 47b0f408..238922cc 100644 --- a/drivers/xen/blkback/interface.c +++ b/drivers/xen/blkback/interface.c @@ -48,9 +48,11 @@ blkif_t *blkif_alloc(domid_t domid) blkif->domid = domid; spin_lock_init(&blkif->blk_ring_lock); atomic_set(&blkif->refcnt, 1); - init_waitqueue_head(&blkif->wq); blkif->st_print = jiffies; init_waitqueue_head(&blkif->waiting_to_free); + blkif->queue_state = QUEUE_RUNNING; + mutex_init(&blkif->queue_mutex); + atomic_set(&blkif->requests_pending, 0); return blkif; } diff --git a/drivers/xen/blkback/xenbus.c b/drivers/xen/blkback/xenbus.c index 0a31705a..5ded579e 100644 --- a/drivers/xen/blkback/xenbus.c +++ b/drivers/xen/blkback/xenbus.c @@ -33,6 +33,7 @@ struct backend_info blkif_t *blkif; struct xenbus_watch backend_watch; struct xenbus_watch shutdown_watch; + struct xenbus_watch pause_watch; unsigned major; unsigned minor; char *mode; @@ -279,6 +280,12 @@ static int blkback_remove(struct xenbus_device *dev) be->shutdown_watch.node = NULL; } + if (be->pause_watch.node) { + unregister_xenbus_watch(&be->pause_watch); + kfree(be->pause_watch.node); + be->pause_watch.node = NULL; + } + if (kthread_remove(be)) WPRINTK("BAD REMOVE REQUEST for %s\n", be->nodename); @@ -369,6 +376,59 @@ static void start_shutdown(struct xenbus_watch *watch, kfree(type); } +void blkback_signal_paused(blkif_t *blkif) +{ + int err; + struct backend_info *be = blkif->be; + + down(&blkback_dev_sem); + if (be->dev) { + err = xenbus_write(XBT_NIL, + be->dev->nodename, "pause-done", ""); + if (err) + xenbus_dev_error(be->dev, err, "writing pause-done"); + } + up(&blkback_dev_sem); +} + +void blkback_signal_resumed(blkif_t *blkif) +{ + int err; + struct backend_info *be = blkif->be; + + down(&blkback_dev_sem); + if (be->dev) { + connect(be); + err = xenbus_rm(XBT_NIL, be->dev->nodename, "pause-done"); + if (err) + xenbus_dev_error(be->dev, err, "removing pause-done"); + } + up(&blkback_dev_sem); +} + +static void backend_pause(struct xenbus_watch *watch, + const char **vec, unsigned int length) +{ + struct backend_info *be + = container_of(watch, struct backend_info, pause_watch); + struct xenbus_device *dev = be->dev; + + if (xenbus_exists(XBT_NIL, dev->nodename, "pause")) { + if (xenbus_exists(XBT_NIL, dev->nodename, "pause-done")) { + WPRINTK("got pause request for paused vbd %s\n", + dev->nodename); + return; + } + + WPRINTK("pausing %s\n", dev->nodename); + blkback_pause(be->blkif); + + } else if (xenbus_exists(XBT_NIL, dev->nodename, "pause-done")) { + WPRINTK("resuming %s\n", dev->nodename); + blkback_resume(be->blkif); + } +} + int blkback_barrier(struct xenbus_transaction xbt, struct backend_info *be, int state) { @@ -432,6 +492,11 @@ static int blkback_probe(struct xenbus_device *dev, if (err) goto fail; + err = xenbus_watch_path2(dev, dev->nodename, "pause", + &be->pause_watch, backend_pause); + if (err) + goto fail; + err = xenbus_switch_state(dev, XenbusStateInitWait); if (err) goto fail;