]> sourceware.org Git - dm.git/commitdiff
Add a priority parameter to kcopyd so that snapshots can butt-in to mirror
authorPatrick Caulfield <pcaulfie@redhat.com>
Thu, 7 Feb 2002 15:20:59 +0000 (15:20 +0000)
committerPatrick Caulfield <pcaulfie@redhat.com>
Thu, 7 Feb 2002 15:20:59 +0000 (15:20 +0000)
operations.

Also keep the number of free-list allocated entries a fixed size and allocate
anuthing else from a slab.

kernel/common/dm-mirror.c
kernel/common/dm-snapshot.c
kernel/common/dm.h
kernel/common/kcopyd.c

index 621e861d8794eee7019ca395a7d3f65c64b88439..404127afeb79d2d14ae1c08a8e83ad43c05ec999 100644 (file)
@@ -10,6 +10,9 @@
 #include <linux/init.h>
 #include <linux/blkdev.h>
 
+/* 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: <dev_path1> <offset> <dev_path2> <offset> <chunk-size>
+ * Construct a mirror mapping: <dev_path1> <offset> <dev_path2> <offset>
  */
 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);
index 452f0b620e8208d41714fa9ecbbbd831ad2d0e82..19baacc86edebe27ba520206d820ddc04a29ba2f 100644 (file)
@@ -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);
                                }
                        }
index 9ae89c60f9cc85622a8c7d982c9eaf95aa504a2c..9dafd42c795da2f8126db01268f942846b0d80b7 100644 (file)
@@ -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)
index 08e352c929db92b6e399bab4ce3f6d740c19a87e..f4aa23167b6e6bed03b11f215a475f6588e3b0f0 100644 (file)
 #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; i<INITIAL_FREE_LIST_SIZE; i++) {
+       for (i=0; i<FREE_LIST_SIZE; i++) {
                newwork = kmalloc(sizeof(struct copy_work), GFP_KERNEL);
                if (!newwork)
                        return i;
@@ -366,9 +419,21 @@ static int __init kcopyd_init(void)
                return -1;
        }
 
+       entry_cachep = kmem_cache_create("kcopyd",
+                                        sizeof(struct copy_work),
+                                        __alignof__(struct copy_work),
+                                        0, NULL, NULL);
+       if (!entry_cachep) {
+               unmap_kiobuf(iobuf);
+               free_kiovec(1, &iobuf);
+               DMERR("Unable to allocate slab cache for kcopyd\n");
+               return -1;
+       }
+
        if (allocate_free_list() == 0) {
                unmap_kiobuf(iobuf);
                free_kiovec(1, &iobuf);
+               kmem_cache_destroy(entry_cachep);
                DMERR("Unable to allocate any work structures for the free list\n");
                return -1;
        }
@@ -387,13 +452,16 @@ static void kcopyd_exit(void)
        down(&run_lock);
        up(&run_lock);
 
-       /* Free the free list */
+        /* Free the free list */
        list_for_each_safe(entry, temp, &free_list) {
                struct copy_work *cw;
                cw = list_entry(entry, struct copy_work, list);
                list_del(&cw->list);
                kfree(cw);
        }
+
+       if (entry_cachep)
+               kmem_cache_destroy(entry_cachep);
 }
 
 /* These are only here until it gets added to dm.c */
This page took 0.042743 seconds and 5 git commands to generate.