Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / md / dm-log.c
index 7a1c77d..d73779a 100644 (file)
 #include "dm-io.h"
 
 static LIST_HEAD(_log_types);
-static spinlock_t _lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(_lock);
 
 int dm_register_dirty_log_type(struct dirty_log_type *type)
 {
-       if (!try_module_get(type->module))
-               return -EINVAL;
-
        spin_lock(&_lock);
        type->use_count = 0;
        list_add(&type->list, &_log_types);
@@ -34,10 +31,8 @@ int dm_unregister_dirty_log_type(struct dirty_log_type *type)
 
        if (type->use_count)
                DMWARN("Attempt to unregister a log type that is still in use");
-       else {
+       else
                list_del(&type->list);
-               module_put(type->module);
-       }
 
        spin_unlock(&_lock);
 
@@ -51,6 +46,10 @@ static struct dirty_log_type *get_type(const char *type_name)
        spin_lock(&_lock);
        list_for_each_entry (type, &_log_types, list)
                if (!strcmp(type_name, type->name)) {
+                       if (!type->use_count && !try_module_get(type->module)){
+                               spin_unlock(&_lock);
+                               return NULL;
+                       }
                        type->use_count++;
                        spin_unlock(&_lock);
                        return type;
@@ -63,7 +62,8 @@ static struct dirty_log_type *get_type(const char *type_name)
 static void put_type(struct dirty_log_type *type)
 {
        spin_lock(&_lock);
-       type->use_count--;
+       if (!--type->use_count)
+               module_put(type->module);
        spin_unlock(&_lock);
 }
 
@@ -112,7 +112,7 @@ void dm_destroy_dirty_log(struct dirty_log *log)
 /*
  * The on-disk version of the metadata.
  */
-#define MIRROR_DISK_VERSION 1
+#define MIRROR_DISK_VERSION 2
 #define LOG_OFFSET 2
 
 struct log_header {
@@ -129,7 +129,7 @@ struct log_header {
 struct log_c {
        struct dm_target *ti;
        int touched;
-       sector_t region_size;
+       uint32_t region_size;
        unsigned int region_count;
        region_t sync_count;
 
@@ -140,6 +140,13 @@ struct log_c {
 
        int sync_search;
 
+       /* Resync flag */
+       enum sync {
+               DEFAULTSYNC,    /* Synchronize if necessary */
+               NOSYNC,         /* Devices known to be already in sync */
+               FORCESYNC,      /* Force a sync to happen */
+       } sync;
+
        /*
         * Disk log fields
         */
@@ -150,7 +157,6 @@ struct log_c {
        struct log_header *disk_header;
 
        struct io_region bits_location;
-       uint32_t *disk_bits;
 };
 
 /*
@@ -159,20 +165,20 @@ struct log_c {
  */
 static  inline int log_test_bit(uint32_t *bs, unsigned bit)
 {
-       return test_bit(bit, (unsigned long *) bs) ? 1 : 0;
+       return ext2_test_bit(bit, (unsigned long *) bs) ? 1 : 0;
 }
 
 static inline void log_set_bit(struct log_c *l,
                               uint32_t *bs, unsigned bit)
 {
-       set_bit(bit, (unsigned long *) bs);
+       ext2_set_bit(bit, (unsigned long *) bs);
        l->touched = 1;
 }
 
 static inline void log_clear_bit(struct log_c *l,
                                 uint32_t *bs, unsigned bit)
 {
-       clear_bit(bit, (unsigned long *) bs);
+       ext2_clear_bit(bit, (unsigned long *) bs);
        l->touched = 1;
 }
 
@@ -205,12 +211,18 @@ static int read_header(struct log_c *log)
 
        header_from_disk(&log->header, log->disk_header);
 
-       if (log->header.magic != MIRROR_MAGIC) {
+       /* New log required? */
+       if (log->sync != DEFAULTSYNC || log->header.magic != MIRROR_MAGIC) {
                log->header.magic = MIRROR_MAGIC;
                log->header.version = MIRROR_DISK_VERSION;
                log->header.nr_regions = 0;
        }
 
+#ifdef __LITTLE_ENDIAN
+       if (log->header.version == 1)
+               log->header.version = 2;
+#endif
+
        if (log->header.version != MIRROR_DISK_VERSION) {
                DMWARN("incompatible disk log version");
                return -EINVAL;
@@ -231,70 +243,65 @@ static inline int write_header(struct log_c *log)
 /*----------------------------------------------------------------
  * Bits IO
  *--------------------------------------------------------------*/
-static inline void bits_to_core(uint32_t *core, uint32_t *disk, unsigned count)
-{
-       unsigned i;
-
-       for (i = 0; i < count; i++)
-               core[i] = le32_to_cpu(disk[i]);
-}
-
-static inline void bits_to_disk(uint32_t *core, uint32_t *disk, unsigned count)
-{
-       unsigned i;
-
-       /* copy across the clean/dirty bitset */
-       for (i = 0; i < count; i++)
-               disk[i] = cpu_to_le32(core[i]);
-}
-
 static int read_bits(struct log_c *log)
 {
        int r;
        unsigned long ebits;
 
        r = dm_io_sync_vm(1, &log->bits_location, READ,
-                         log->disk_bits, &ebits);
+                         log->clean_bits, &ebits);
        if (r)
                return r;
 
-       bits_to_core(log->clean_bits, log->disk_bits,
-                    log->bitset_uint32_count);
        return 0;
 }
 
 static int write_bits(struct log_c *log)
 {
        unsigned long ebits;
-       bits_to_disk(log->clean_bits, log->disk_bits,
-                    log->bitset_uint32_count);
        return dm_io_sync_vm(1, &log->bits_location, WRITE,
-                            log->disk_bits, &ebits);
+                            log->clean_bits, &ebits);
 }
 
 /*----------------------------------------------------------------
- * constructor/destructor
+ * core log constructor/destructor
+ *
+ * argv contains region_size followed optionally by [no]sync
  *--------------------------------------------------------------*/
 #define BYTE_SHIFT 3
 static int core_ctr(struct dirty_log *log, struct dm_target *ti,
                    unsigned int argc, char **argv)
 {
+       enum sync sync = DEFAULTSYNC;
+
        struct log_c *lc;
-       sector_t region_size;
+       uint32_t region_size;
        unsigned int region_count;
        size_t bitset_size;
 
-       if (argc != 1) {
-               DMWARN("wrong number of arguments to log_c");
+       if (argc < 1 || argc > 2) {
+               DMWARN("wrong number of arguments to mirror log");
                return -EINVAL;
        }
 
-       if (sscanf(argv[0], SECTOR_FORMAT, &region_size) != 1) {
+       if (argc > 1) {
+               if (!strcmp(argv[1], "sync"))
+                       sync = FORCESYNC;
+               else if (!strcmp(argv[1], "nosync"))
+                       sync = NOSYNC;
+               else {
+                       DMWARN("unrecognised sync argument to mirror log: %s",
+                              argv[1]);
+                       return -EINVAL;
+               }
+       }
+
+       if (sscanf(argv[0], "%u", &region_size) != 1) {
                DMWARN("invalid region size string");
                return -EINVAL;
        }
 
-       region_count = dm_div_up(ti->len, region_size);
+       region_count = dm_sector_div_up(ti->len, region_size);
 
        lc = kmalloc(sizeof(*lc), GFP_KERNEL);
        if (!lc) {
@@ -306,12 +313,13 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti,
        lc->touched = 0;
        lc->region_size = region_size;
        lc->region_count = region_count;
+       lc->sync = sync;
 
        /*
-        * Work out how many words we need to hold the bitset.
+        * Work out how many "unsigned long"s we need to hold the bitset.
         */
        bitset_size = dm_round_up(region_count,
-                                 sizeof(*lc->clean_bits) << BYTE_SHIFT);
+                                 sizeof(unsigned long) << BYTE_SHIFT);
        bitset_size >>= BYTE_SHIFT;
 
        lc->bitset_uint32_count = bitset_size / 4;
@@ -330,8 +338,8 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti,
                kfree(lc);
                return -ENOMEM;
        }
-       memset(lc->sync_bits, 0, bitset_size);
-        lc->sync_count = 0;
+       memset(lc->sync_bits, (sync == NOSYNC) ? -1 : 0, bitset_size);
+       lc->sync_count = (sync == NOSYNC) ? region_count : 0;
 
        lc->recovering_bits = vmalloc(bitset_size);
        if (!lc->recovering_bits) {
@@ -356,6 +364,11 @@ static void core_dtr(struct dirty_log *log)
        kfree(lc);
 }
 
+/*----------------------------------------------------------------
+ * disk log constructor/destructor
+ *
+ * argv contains log_device region_size followed optionally by [no]sync
+ *--------------------------------------------------------------*/
 static int disk_ctr(struct dirty_log *log, struct dm_target *ti,
                    unsigned int argc, char **argv)
 {
@@ -364,8 +377,8 @@ static int disk_ctr(struct dirty_log *log, struct dm_target *ti,
        struct log_c *lc;
        struct dm_dev *dev;
 
-       if (argc != 2) {
-               DMWARN("wrong number of arguments to log_d");
+       if (argc < 2 || argc > 3) {
+               DMWARN("wrong number of arguments to disk mirror log");
                return -EINVAL;
        }
 
@@ -403,11 +416,6 @@ static int disk_ctr(struct dirty_log *log, struct dm_target *ti,
        size = dm_round_up(lc->bitset_uint32_count * sizeof(uint32_t),
                           1 << SECTOR_SHIFT);
        lc->bits_location.count = size >> SECTOR_SHIFT;
-       lc->disk_bits = vmalloc(size);
-       if (!lc->disk_bits) {
-               vfree(lc->disk_header);
-               goto bad;
-       }
        return 0;
 
  bad:
@@ -421,7 +429,6 @@ static void disk_dtr(struct dirty_log *log)
        struct log_c *lc = (struct log_c *) log->context;
        dm_put_device(lc->ti, lc->log_dev);
        vfree(lc->disk_header);
-       vfree(lc->disk_bits);
        core_dtr(log);
 }
 
@@ -452,10 +459,15 @@ static int disk_resume(struct dirty_log *log)
        if (r)
                return r;
 
-       /* zero any new bits if the mirror has grown */
-       for (i = lc->header.nr_regions; i < lc->region_count; i++)
-               /* FIXME: amazingly inefficient */
-               log_clear_bit(lc, lc->clean_bits, i);
+       /* set or clear any new bits */
+       if (lc->sync == NOSYNC)
+               for (i = lc->header.nr_regions; i < lc->region_count; i++)
+                       /* FIXME: amazingly inefficient */
+                       log_set_bit(lc, lc->clean_bits, i);
+       else
+               for (i = lc->header.nr_regions; i < lc->region_count; i++)
+                       /* FIXME: amazingly inefficient */
+                       log_clear_bit(lc, lc->clean_bits, i);
 
        /* copy clean across to sync */
        memcpy(lc->sync_bits, lc->clean_bits, size);
@@ -473,7 +485,7 @@ static int disk_resume(struct dirty_log *log)
        return write_header(lc);
 }
 
-static sector_t core_get_region_size(struct dirty_log *log)
+static uint32_t core_get_region_size(struct dirty_log *log)
 {
        struct log_c *lc = (struct log_c *) log->context;
        return lc->region_size;
@@ -533,12 +545,13 @@ static int core_get_resync_work(struct dirty_log *log, region_t *region)
                return 0;
 
        do {
-               *region = find_next_zero_bit((unsigned long *) lc->sync_bits,
+               *region = ext2_find_next_zero_bit(
+                                            (unsigned long *) lc->sync_bits,
                                             lc->region_count,
                                             lc->sync_search);
                lc->sync_search = *region + 1;
 
-               if (*region == lc->region_count)
+               if (*region >= lc->region_count)
                        return 0;
 
        } while (log_test_bit(lc->recovering_bits, *region));
@@ -566,6 +579,51 @@ static region_t core_get_sync_count(struct dirty_log *log)
         return lc->sync_count;
 }
 
+#define        DMEMIT_SYNC \
+       if (lc->sync != DEFAULTSYNC) \
+               DMEMIT("%ssync ", lc->sync == NOSYNC ? "no" : "")
+
+static int core_status(struct dirty_log *log, status_type_t status,
+                      char *result, unsigned int maxlen)
+{
+       int sz = 0;
+       struct log_c *lc = log->context;
+
+       switch(status) {
+       case STATUSTYPE_INFO:
+               break;
+
+       case STATUSTYPE_TABLE:
+               DMEMIT("%s %u %u ", log->type->name,
+                      lc->sync == DEFAULTSYNC ? 1 : 2, lc->region_size);
+               DMEMIT_SYNC;
+       }
+
+       return sz;
+}
+
+static int disk_status(struct dirty_log *log, status_type_t status,
+                      char *result, unsigned int maxlen)
+{
+       int sz = 0;
+       char buffer[16];
+       struct log_c *lc = log->context;
+
+       switch(status) {
+       case STATUSTYPE_INFO:
+               break;
+
+       case STATUSTYPE_TABLE:
+               format_dev_t(buffer, lc->log_dev->bdev->bd_dev);
+               DMEMIT("%s %u %s %u ", log->type->name,
+                      lc->sync == DEFAULTSYNC ? 2 : 3, buffer,
+                      lc->region_size);
+               DMEMIT_SYNC;
+       }
+
+       return sz;
+}
+
 static struct dirty_log_type _core_type = {
        .name = "core",
        .module = THIS_MODULE,
@@ -579,7 +637,8 @@ static struct dirty_log_type _core_type = {
        .clear_region = core_clear_region,
        .get_resync_work = core_get_resync_work,
        .complete_resync_work = core_complete_resync_work,
-        .get_sync_count = core_get_sync_count
+       .get_sync_count = core_get_sync_count,
+       .status = core_status,
 };
 
 static struct dirty_log_type _disk_type = {
@@ -597,7 +656,8 @@ static struct dirty_log_type _disk_type = {
        .clear_region = core_clear_region,
        .get_resync_work = core_get_resync_work,
        .complete_resync_work = core_complete_resync_work,
-        .get_sync_count = core_get_sync_count
+       .get_sync_count = core_get_sync_count,
+       .status = disk_status,
 };
 
 int __init dm_dirty_log_init(void)