X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmd%2Fdm-log.c;h=6a9261351848ba7e3272de48a51d100345a45a7f;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=43a12285d81ba0bf18d997810f5b09148aabb8b7;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c index 43a12285d..6a9261351 100644 --- a/drivers/md/dm-log.c +++ b/drivers/md/dm-log.c @@ -12,14 +12,13 @@ #include "dm-log.h" #include "dm-io.h" +#define DM_MSG_PREFIX "mirror log" + 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 +33,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 +48,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 +64,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 +114,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 +131,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; @@ -155,9 +157,6 @@ struct log_c { struct io_region header_location; struct log_header *disk_header; - - struct io_region bits_location; - uint32_t *disk_bits; }; /* @@ -166,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; } @@ -219,6 +218,11 @@ static int read_header(struct log_c *log) 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; @@ -236,65 +240,22 @@ static inline int write_header(struct log_c *log) log->disk_header, &ebits); } -/*---------------------------------------------------------------- - * 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); - 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); -} - /*---------------------------------------------------------------- * 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) +static int create_log_context(struct dirty_log *log, struct dm_target *ti, + unsigned int argc, char **argv, + struct dm_dev *dev) { enum sync sync = DEFAULTSYNC; struct log_c *lc; - sector_t region_size; + uint32_t region_size; unsigned int region_count; - size_t bitset_size; + size_t bitset_size, buf_size; if (argc < 1 || argc > 2) { DMWARN("wrong number of arguments to mirror log"); @@ -313,12 +274,12 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti, } } - if (sscanf(argv[0], SECTOR_FORMAT, ®ion_size) != 1) { + if (sscanf(argv[0], "%u", ®ion_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) { @@ -333,25 +294,56 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti, 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); bitset_size >>= BYTE_SHIFT; - lc->bitset_uint32_count = bitset_size / 4; - lc->clean_bits = vmalloc(bitset_size); - if (!lc->clean_bits) { - DMWARN("couldn't allocate clean bitset"); - kfree(lc); - return -ENOMEM; + lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits); + + /* + * Disk log? + */ + if (!dev) { + lc->clean_bits = vmalloc(bitset_size); + if (!lc->clean_bits) { + DMWARN("couldn't allocate clean bitset"); + kfree(lc); + return -ENOMEM; + } + lc->disk_header = NULL; + } else { + lc->log_dev = dev; + lc->header_location.bdev = lc->log_dev->bdev; + lc->header_location.sector = 0; + + /* + * Buffer holds both header and bitset. + */ + buf_size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) + + bitset_size, ti->limits.hardsect_size); + lc->header_location.count = buf_size >> SECTOR_SHIFT; + + lc->disk_header = vmalloc(buf_size); + if (!lc->disk_header) { + DMWARN("couldn't allocate disk log buffer"); + kfree(lc); + return -ENOMEM; + } + + lc->clean_bits = (void *)lc->disk_header + + (LOG_OFFSET << SECTOR_SHIFT); } + memset(lc->clean_bits, -1, bitset_size); lc->sync_bits = vmalloc(bitset_size); if (!lc->sync_bits) { DMWARN("couldn't allocate sync bitset"); - vfree(lc->clean_bits); + if (!dev) + vfree(lc->clean_bits); + vfree(lc->disk_header); kfree(lc); return -ENOMEM; } @@ -362,25 +354,40 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti, if (!lc->recovering_bits) { DMWARN("couldn't allocate sync bitset"); vfree(lc->sync_bits); - vfree(lc->clean_bits); + if (!dev) + vfree(lc->clean_bits); + vfree(lc->disk_header); kfree(lc); return -ENOMEM; } memset(lc->recovering_bits, 0, bitset_size); lc->sync_search = 0; log->context = lc; + return 0; } -static void core_dtr(struct dirty_log *log) +static int core_ctr(struct dirty_log *log, struct dm_target *ti, + unsigned int argc, char **argv) +{ + return create_log_context(log, ti, argc, argv, NULL); +} + +static void destroy_log_context(struct log_c *lc) { - struct log_c *lc = (struct log_c *) log->context; - vfree(lc->clean_bits); vfree(lc->sync_bits); vfree(lc->recovering_bits); kfree(lc); } +static void core_dtr(struct dirty_log *log) +{ + struct log_c *lc = (struct log_c *) log->context; + + vfree(lc->clean_bits); + destroy_log_context(lc); +} + /*---------------------------------------------------------------- * disk log constructor/destructor * @@ -390,8 +397,6 @@ static int disk_ctr(struct dirty_log *log, struct dm_target *ti, unsigned int argc, char **argv) { int r; - size_t size; - struct log_c *lc; struct dm_dev *dev; if (argc < 2 || argc > 3) { @@ -404,55 +409,22 @@ static int disk_ctr(struct dirty_log *log, struct dm_target *ti, if (r) return r; - r = core_ctr(log, ti, argc - 1, argv + 1); + r = create_log_context(log, ti, argc - 1, argv + 1, dev); if (r) { dm_put_device(ti, dev); return r; } - lc = (struct log_c *) log->context; - lc->log_dev = dev; - - /* setup the disk header fields */ - lc->header_location.bdev = lc->log_dev->bdev; - lc->header_location.sector = 0; - lc->header_location.count = 1; - - /* - * We can't read less than this amount, even though we'll - * not be using most of this space. - */ - lc->disk_header = vmalloc(1 << SECTOR_SHIFT); - if (!lc->disk_header) - goto bad; - - /* setup the disk bitset fields */ - lc->bits_location.bdev = lc->log_dev->bdev; - lc->bits_location.sector = LOG_OFFSET; - - 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: - dm_put_device(ti, lc->log_dev); - core_dtr(log); - return -ENOMEM; } 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); + destroy_log_context(lc); } static int count_bits32(uint32_t *addr, unsigned size) @@ -477,12 +449,7 @@ static int disk_resume(struct dirty_log *log) if (r) return r; - /* read the bits */ - r = read_bits(lc); - if (r) - return r; - - /* set or clear any new bits */ + /* set or clear any new bits -- device has grown */ if (lc->sync == NOSYNC) for (i = lc->header.nr_regions; i < lc->region_count; i++) /* FIXME: amazingly inefficient */ @@ -492,14 +459,14 @@ static int disk_resume(struct dirty_log *log) /* FIXME: amazingly inefficient */ log_clear_bit(lc, lc->clean_bits, i); + /* clear any old bits -- device has shrunk */ + for (i = lc->region_count; i % (sizeof(*lc->clean_bits) << BYTE_SHIFT); i++) + log_clear_bit(lc, lc->clean_bits, i); + /* copy clean across to sync */ memcpy(lc->sync_bits, lc->clean_bits, size); lc->sync_count = count_bits32(lc->clean_bits, lc->bitset_uint32_count); - - /* write the bits */ - r = write_bits(lc); - if (r) - return r; + lc->sync_search = 0; /* set the correct number of regions in the header */ lc->header.nr_regions = lc->region_count; @@ -508,12 +475,19 @@ 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; } +static int core_resume(struct dirty_log *log) +{ + struct log_c *lc = (struct log_c *) log->context; + lc->sync_search = 0; + return 0; +} + static int core_is_clean(struct dirty_log *log, region_t region) { struct log_c *lc = (struct log_c *) log->context; @@ -541,7 +515,7 @@ static int disk_flush(struct dirty_log *log) if (!lc->touched) return 0; - r = write_bits(lc); + r = write_header(lc); if (!r) lc->touched = 0; @@ -568,12 +542,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)); @@ -582,16 +557,19 @@ static int core_get_resync_work(struct dirty_log *log, region_t *region) return 1; } -static void core_complete_resync_work(struct dirty_log *log, region_t region, - int success) +static void core_set_region_sync(struct dirty_log *log, region_t region, + int in_sync) { struct log_c *lc = (struct log_c *) log->context; log_clear_bit(lc, lc->recovering_bits, region); - if (success) { + if (in_sync) { log_set_bit(lc, lc->sync_bits, region); lc->sync_count++; - } + } else if (log_test_bit(lc->sync_bits, region)) { + lc->sync_count--; + log_clear_bit(lc, lc->sync_bits, region); + } } static region_t core_get_sync_count(struct dirty_log *log) @@ -616,7 +594,7 @@ static int core_status(struct dirty_log *log, status_type_t status, break; case STATUSTYPE_TABLE: - DMEMIT("%s %u " SECTOR_FORMAT " ", log->type->name, + DMEMIT("%s %u %u ", log->type->name, lc->sync == DEFAULTSYNC ? 1 : 2, lc->region_size); DMEMIT_SYNC; } @@ -637,7 +615,7 @@ static int disk_status(struct dirty_log *log, status_type_t status, case STATUSTYPE_TABLE: format_dev_t(buffer, lc->log_dev->bdev->bd_dev); - DMEMIT("%s %u %s " SECTOR_FORMAT " ", log->type->name, + DMEMIT("%s %u %s %u ", log->type->name, lc->sync == DEFAULTSYNC ? 2 : 3, buffer, lc->region_size); DMEMIT_SYNC; @@ -651,6 +629,7 @@ static struct dirty_log_type _core_type = { .module = THIS_MODULE, .ctr = core_ctr, .dtr = core_dtr, + .resume = core_resume, .get_region_size = core_get_region_size, .is_clean = core_is_clean, .in_sync = core_in_sync, @@ -658,7 +637,7 @@ static struct dirty_log_type _core_type = { .mark_region = core_mark_region, .clear_region = core_clear_region, .get_resync_work = core_get_resync_work, - .complete_resync_work = core_complete_resync_work, + .set_region_sync = core_set_region_sync, .get_sync_count = core_get_sync_count, .status = core_status, }; @@ -677,7 +656,7 @@ static struct dirty_log_type _disk_type = { .mark_region = core_mark_region, .clear_region = core_clear_region, .get_resync_work = core_get_resync_work, - .complete_resync_work = core_complete_resync_work, + .set_region_sync = core_set_region_sync, .get_sync_count = core_get_sync_count, .status = disk_status, };