#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);
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);
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;
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);
}
/*
* The on-disk version of the metadata.
*/
-#define MIRROR_DISK_VERSION 1
+#define MIRROR_DISK_VERSION 2
#define LOG_OFFSET 2
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;
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
*/
struct log_header *disk_header;
struct io_region bits_location;
- uint32_t *disk_bits;
};
/*
*/
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;
}
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;
/*----------------------------------------------------------------
* 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, ®ion_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", ®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) {
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;
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) {
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)
{
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;
}
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:
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);
}
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);
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;
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));
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,
.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 = {
.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)