From: Patrick Caulfield Date: Thu, 7 Feb 2002 15:20:59 +0000 (+0000) Subject: Add a priority parameter to kcopyd so that snapshots can butt-in to mirror X-Git-Tag: beta2~72 X-Git-Url: https://sourceware.org/git/?a=commitdiff_plain;h=25e88e35c53b3e986e710b6343ad4c51a5405743;p=dm.git Add a priority parameter to kcopyd so that snapshots can butt-in to mirror operations. Also keep the number of free-list allocated entries a fixed size and allocate anuthing else from a slab. --- diff --git a/kernel/common/dm-mirror.c b/kernel/common/dm-mirror.c index 621e861..404127a 100644 --- a/kernel/common/dm-mirror.c +++ b/kernel/common/dm-mirror.c @@ -10,6 +10,9 @@ #include #include +/* kcopyd priority of mirror operations */ +#define MIRROR_COPY_PRIORITY 5 + /* * Mirror: maps a mirror range of a device. */ @@ -17,14 +20,13 @@ struct mirror_c { struct dm_dev *fromdev; struct dm_dev *todev; - unsigned long endsec; unsigned long from_delta; unsigned long to_delta; unsigned long frompos; unsigned long topos; - unsigned long chunk_size; + unsigned long got_to; struct rw_semaphore lock; int error; }; @@ -44,52 +46,41 @@ static void mirror_end_io(struct buffer_head *bh, int uptodate) } -/* Called when a chunk I/O has finished - we move onto the next one */ -static void copy_callback(int status, void *context) +/* Called when the copy I/O has finished */ +static void copy_callback(copy_cb_reason_t reason, void *context, long arg) { struct mirror_c *lc = (struct mirror_c *) context; - if (status != 0) { - DMERR("Mirror block %s on %s failed\n", status==1?"read":"write", - status==1?kdevname(lc->fromdev->dev):kdevname(lc->todev->dev)); - lc->error = 1; + printk("PJC: callback: reason %d, block = %ld\n", reason, arg); + if (reason == COPY_CB_PROGRESS) { + lc->got_to = arg; return; } - down_write(&lc->lock); - - if (lc->frompos < lc->endsec && !lc->error) { - int chunks = min(lc->chunk_size, lc->endsec - lc->frompos); - - /* Move onto the next block */ - lc->frompos += lc->chunk_size; - lc->topos += lc->chunk_size; - - if (dm_blockcopy(lc->frompos, lc->topos, chunks, lc->fromdev->dev, lc->todev->dev, 0, copy_callback, lc)) { - DMERR("Mirror block copy to %s failed\n", kdevname(lc->todev->dev)); - - dm_notify(lc); /* TODO: interface ?? */ - } + if (reason == COPY_CB_FAILED_READ || + reason == COPY_CB_FAILED_WRITE) { + DMERR("Mirror block %s on %s failed, sector %ld\n", reason==COPY_CB_FAILED_READ?"read":"write", + reason==COPY_CB_FAILED_READ?kdevname(lc->fromdev->dev):kdevname(lc->todev->dev), arg); + lc->error = 1; } - else { + + if (reason == COPY_CB_COMPLETE) { /* Say we've finished */ dm_notify(lc); /* TODO: interface ?? */ } - up_write(&lc->lock); } /* - * Construct a mirror mapping: + * Construct a mirror mapping: */ static int mirror_ctr(struct dm_table *t, offset_t b, offset_t l, int argc, char **argv, void **context) { struct mirror_c *lc; - unsigned int chunk_size; unsigned long offset1, offset2; char *value; - if (argc != 5) { + if (argc != 4) { *context = "dm-mirror: Not enough arguments"; return -EINVAL; } @@ -122,28 +113,22 @@ static int mirror_ctr(struct dm_table *t, offset_t b, offset_t l, goto bad; } - chunk_size = simple_strtoul(argv[4], &value, 10); - if (chunk_size == 0 || value == NULL) { - *context = "Invalid chunk size"; - goto bad; - } - lc->from_delta = (int) offset1 - (int) b; lc->to_delta = (int) offset2 - (int) b; lc->frompos = offset1; lc->topos = offset2; - lc->endsec = l; lc->error = 0; - lc->chunk_size = chunk_size; init_rwsem(&lc->lock); *context = lc; - if (dm_blockcopy(offset1, offset2, chunk_size, lc->fromdev->dev, lc->todev->dev, 0, copy_callback, lc)) { - DMERR("Initial mirror block copy failed\n"); + if (dm_blockcopy(offset1, offset2, + l - offset1, + lc->fromdev->dev, lc->todev->dev, + MIRROR_COPY_PRIORITY, 0, copy_callback, lc)) { + DMERR("block copy call failed\n"); dm_table_put_device(t, lc->fromdev); dm_table_put_device(t, lc->todev); - kfree(lc); - return -EINVAL; + goto bad; } return 0; @@ -171,11 +156,11 @@ static int mirror_map(struct buffer_head *bh, int rw, void *context) bh->b_rsector = bh->b_rsector + lc->from_delta; /* If we've already copied this block then duplicated it to the mirror device */ - if (rw == WRITE && bh->b_rsector < lc->frompos+lc->chunk_size) { + if (rw == WRITE && bh->b_rsector < lc->got_to) { /* Schedule copy of I/O to other target */ /* TODO: kmalloc is naff here */ - struct buffer_head *dbh = kmalloc(sizeof(struct buffer_head), GFP_KERNEL); + struct buffer_head *dbh = kmalloc(sizeof(struct buffer_head), GFP_NOIO); if (dbh) { *dbh = *bh; dbh->b_rdev = lc->todev->dev; @@ -188,7 +173,6 @@ static int mirror_map(struct buffer_head *bh, int rw, void *context) else { DMERR("kmalloc failed for mirror bh\n"); lc->error = 1; - dm_notify(lc); /* TODO: interface ?? */ } } up_read(&lc->lock); diff --git a/kernel/common/dm-snapshot.c b/kernel/common/dm-snapshot.c index 452f0b6..19baacc 100644 --- a/kernel/common/dm-snapshot.c +++ b/kernel/common/dm-snapshot.c @@ -23,6 +23,9 @@ /* Hard sector size used all over the kernel */ #define SECTOR_SIZE 512 +/* kcopyd priority of snapshot operations */ +#define SNAPSHOT_COPY_PRIORITY 2 + /* The on-disk version of the metadata. Only applicable to persistent snapshots. There is no backward or forward compatibility implemented, snapshots @@ -978,7 +981,8 @@ static int snapshot_map(struct buffer_head *bh, int rw, void *context) /* Get kcopyd to do the work */ dm_blockcopy(read_start, reloc_sector, lc->chunk_size, - lc->origin_dev->dev, lc->cow_dev->dev, 0, + lc->origin_dev->dev, lc->cow_dev->dev, + SNAPSHOT_COPY_PRIORITY, 0, copy_callback, ex); /* Update the bh bits so the write goes to the newly remapped volume... @@ -1081,7 +1085,8 @@ int dm_do_snapshot(struct dm_dev *origin, struct buffer_head *bh) /* Get kcopyd to do the copy */ dm_blockcopy(read_start, reloc_sector, snap->chunk_size, - snap->origin_dev->dev, snap->cow_dev->dev, 0, + snap->origin_dev->dev, snap->cow_dev->dev, + SNAPSHOT_COPY_PRIORITY, 0, copy_callback, ex); } } diff --git a/kernel/common/dm.h b/kernel/common/dm.h index 9ae89c6..9dafd42 100644 --- a/kernel/common/dm.h +++ b/kernel/common/dm.h @@ -168,7 +168,7 @@ typedef enum {COPY_CB_COMPLETE, COPY_CB_FAILED_READ, COPY_CB_FAILED_WRITE, COPY_ int dm_blockcopy(unsigned long fromsec, unsigned long tosec, unsigned long nr_sectors, kdev_t fromdev, kdev_t todev, - int throttle, void (*callback)(copy_cb_reason_t, void *, long), void *context); + int priority, int throttle, void (*callback)(copy_cb_reason_t, void *, long), void *context); #define DMWARN(f, x...) printk(KERN_WARNING DM_NAME ": " f "\n" , ## x) diff --git a/kernel/common/kcopyd.c b/kernel/common/kcopyd.c index 08e352c..f4aa231 100644 --- a/kernel/common/kcopyd.c +++ b/kernel/common/kcopyd.c @@ -21,19 +21,25 @@ #define SECTOR_SIZE 512 /* Number of entries in the free list to start with */ -#define INITIAL_FREE_LIST_SIZE 10 +#define FREE_LIST_SIZE 10 + +/* Slab cache for work/freelist entries */ +static kmem_cache_t *entry_cachep; /* Structure of work to do in the list */ struct copy_work { unsigned long fromsec; unsigned long tosec; + unsigned long nr_sectors; + unsigned long done_sectors; kdev_t fromdev; kdev_t todev; - unsigned long nr_sectors; - int throttle; - void (*callback)(copy_cb_reason_t, void *, long); - void *context; + int throttle; + int priority; /* 0=highest */ + void (*callback)(copy_cb_reason_t, void *, long); + void *context; + int freelist; /* Whether we came from the free list */ struct list_head list; }; @@ -53,7 +59,7 @@ static long last_jiffies = 0; /* Find a free entry from the free-list */ -struct copy_work *get_work_struct(void) +static struct copy_work *get_work_struct(void) { struct copy_work *entry = NULL; @@ -66,7 +72,12 @@ struct copy_work *get_work_struct(void) /* Nothing on the free-list - try to allocate one without doing IO */ if (!entry) { - entry = kmalloc(sizeof(struct copy_work), GFP_NOIO); + entry = kmem_cache_alloc(entry_cachep, GFP_NOIO); + + /* Make sure know it didn't come from the free list */ + if (entry) { + entry->freelist = 0; + } /* Failed...wait for IO to finish */ while (!entry) { @@ -127,6 +138,25 @@ out: return err; } + +/* Add a new entry to the work list - in priority+FIFO order. + The work_list_lock semaphore must be held */ +static void add_to_work_list(struct copy_work *item) +{ + struct list_head *entry; + + list_for_each(entry, &work_list) { + struct copy_work *cw; + + cw = list_entry(entry, struct copy_work, list); + if (cw->priority > item->priority) { + __list_add(&item->list, cw->list.prev, &cw->list); + return; + } + } + list_add_tail(&item->list, &work_list); +} + /* Read in a chunk from the origin device */ static int read_blocks(struct kiobuf *iobuf, kdev_t dev, unsigned long start, int nr_sectors) { @@ -189,17 +219,19 @@ static int copy_kthread(void *unused) struct copy_work *work_item = list_entry(work_list.next, struct copy_work, list); int done_sps; - long done_sectors = 0; copy_cb_reason_t callback_reason = COPY_CB_COMPLETE; + int preempted = 0; list_del(&work_item->list); up_write(&work_list_lock); - while (done_sectors < work_item->nr_sectors) { - long nr_sectors = min((long)KIO_MAX_SECTORS, (long)work_item->nr_sectors - done_sectors); + while (!preempted && work_item->done_sectors < work_item->nr_sectors) { + long nr_sectors = min((unsigned long)KIO_MAX_SECTORS, + work_item->nr_sectors - work_item->done_sectors); /* Read original blocks */ - if (read_blocks(iobuf, work_item->fromdev, work_item->fromsec + done_sectors, nr_sectors)) { + if (read_blocks(iobuf, work_item->fromdev, work_item->fromsec + work_item->done_sectors, + nr_sectors)) { DMERR("Read blocks from device %s failed\n", kdevname(work_item->fromdev)); /* Callback error */ @@ -208,14 +240,15 @@ static int copy_kthread(void *unused) } /* Write them out again */ - if (write_blocks(iobuf, work_item->todev, work_item->tosec + done_sectors, nr_sectors)) { + if (write_blocks(iobuf, work_item->todev, work_item->tosec + work_item->done_sectors, + nr_sectors)) { DMERR("Write blocks to %s failed\n", kdevname(work_item->todev)); /* Callback error */ callback_reason = COPY_CB_FAILED_WRITE; goto done_copy; } - done_sectors += nr_sectors; + work_item->done_sectors += nr_sectors; /* If we have exceeded the throttle value (in sectors/second) then sleep for a while */ @@ -228,24 +261,42 @@ static int copy_kthread(void *unused) } /* Do a progress callback */ - if (work_item->callback && done_sectors < work_item->nr_sectors) - work_item->callback(COPY_CB_PROGRESS, work_item->context, done_sectors); + if (work_item->callback && work_item->done_sectors < work_item->nr_sectors) + work_item->callback(COPY_CB_PROGRESS, work_item->context, work_item->done_sectors); + + /* Look for higher priority work */ + down_write(&work_list_lock); + if (!list_empty(&work_list)) { + struct copy_work *peek_item = list_entry(work_list.next, struct copy_work, list); + if (peek_item->priority < work_item->priority) { + + /* Put this back on the list and restart to get the new one */ + add_to_work_list(work_item); + preempted = 1; + goto restart; + } + } + up_write(&work_list_lock); } done_copy: /* Call the callback */ if (work_item->callback) - work_item->callback(callback_reason, work_item->context, done_sectors); - - /* Add it back to the free list and notify anybody waiting for an entry */ - down_write(&free_list_lock); - list_add(&work_item->list, &free_list); - up_write(&free_list_lock); + work_item->callback(callback_reason, work_item->context, work_item->done_sectors); + + /* Add it back to the free list (if it came from there) + and notify anybody waiting for an entry */ + if (work_item->freelist) { + down_write(&free_list_lock); + list_add(&work_item->list, &free_list); + up_write(&free_list_lock); + } wake_up_interruptible(&freelist_waitq); /* Get the work lock again for the top of the while loop */ down_write(&work_list_lock); + restart: } up_write(&work_list_lock); @@ -271,7 +322,7 @@ static int copy_kthread(void *unused) /* API entry point */ int dm_blockcopy(unsigned long fromsec, unsigned long tosec, unsigned long nr_sectors, kdev_t fromdev, kdev_t todev, - int throttle, void (*callback)(copy_cb_reason_t, void *, long), void *context) + int priority, int throttle, void (*callback)(copy_cb_reason_t, void *, long), void *context) { struct copy_work *newwork; static pid_t thread_pid = 0; @@ -315,17 +366,19 @@ int dm_blockcopy(unsigned long fromsec, unsigned long tosec, unsigned long nr_se /* This will wait until one is available */ newwork = get_work_struct(); - newwork->fromsec = fromsec; - newwork->tosec = tosec; - newwork->fromdev = fromdev; - newwork->todev = todev; - newwork->nr_sectors = nr_sectors; - newwork->throttle = throttle; - newwork->callback = callback; - newwork->context = context; + newwork->fromsec = fromsec; + newwork->tosec = tosec; + newwork->fromdev = fromdev; + newwork->todev = todev; + newwork->nr_sectors = nr_sectors; + newwork->done_sectors = 0; + newwork->throttle = throttle; + newwork->priority = priority; + newwork->callback = callback; + newwork->context = context; down_write(&work_list_lock); - list_add_tail(&newwork->list, &work_list); + add_to_work_list(newwork); up_write(&work_list_lock); wake_up_interruptible(&work_waitq); @@ -339,7 +392,7 @@ static int allocate_free_list(void) int i; struct copy_work *newwork; - for (i=0; ilist); kfree(cw); } + + if (entry_cachep) + kmem_cache_destroy(entry_cachep); } /* These are only here until it gets added to dm.c */