dm snapshot: allow live exception store handover between tables
[safe/jmp/linux-2.6] / drivers / md / dm-snap-persistent.c
index 6e3fe4f..157999e 100644 (file)
@@ -106,6 +106,13 @@ struct pstore {
        void *zero_area;
 
        /*
+        * An area used for header. The header can be written
+        * concurrently with metadata (when invalidating the snapshot),
+        * so it needs a separate buffer.
+        */
+       void *header_area;
+
+       /*
         * Used to keep track of which metadata area the data in
         * 'chunk' refers to.
         */
@@ -148,16 +155,27 @@ static int alloc_area(struct pstore *ps)
         */
        ps->area = vmalloc(len);
        if (!ps->area)
-               return r;
+               goto err_area;
 
        ps->zero_area = vmalloc(len);
-       if (!ps->zero_area) {
-               vfree(ps->area);
-               return r;
-       }
+       if (!ps->zero_area)
+               goto err_zero_area;
        memset(ps->zero_area, 0, len);
 
+       ps->header_area = vmalloc(len);
+       if (!ps->header_area)
+               goto err_header_area;
+
        return 0;
+
+err_header_area:
+       vfree(ps->zero_area);
+
+err_zero_area:
+       vfree(ps->area);
+
+err_area:
+       return r;
 }
 
 static void free_area(struct pstore *ps)
@@ -169,6 +187,10 @@ static void free_area(struct pstore *ps)
        if (ps->zero_area)
                vfree(ps->zero_area);
        ps->zero_area = NULL;
+
+       if (ps->header_area)
+               vfree(ps->header_area);
+       ps->header_area = NULL;
 }
 
 struct mdata_req {
@@ -188,17 +210,18 @@ static void do_metadata(struct work_struct *work)
 /*
  * Read or write a chunk aligned and sized block of data from a device.
  */
-static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata)
+static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
+                   int metadata)
 {
        struct dm_io_region where = {
-               .bdev = ps->store->cow->bdev,
+               .bdev = dm_snap_cow(ps->store->snap)->bdev,
                .sector = ps->store->chunk_size * chunk,
                .count = ps->store->chunk_size,
        };
        struct dm_io_request io_req = {
                .bi_rw = rw,
                .mem.type = DM_IO_VMA,
-               .mem.ptr.vma = ps->area,
+               .mem.ptr.vma = area,
                .client = ps->io_client,
                .notify.fn = NULL,
        };
@@ -240,7 +263,7 @@ static int area_io(struct pstore *ps, int rw)
 
        chunk = area_location(ps, ps->current_area);
 
-       r = chunk_io(ps, chunk, rw, 0);
+       r = chunk_io(ps, ps->area, chunk, rw, 0);
        if (r)
                return r;
 
@@ -254,35 +277,25 @@ static void zero_memory_area(struct pstore *ps)
 
 static int zero_disk_area(struct pstore *ps, chunk_t area)
 {
-       struct dm_io_region where = {
-               .bdev = ps->store->cow->bdev,
-               .sector = ps->store->chunk_size * area_location(ps, area),
-               .count = ps->store->chunk_size,
-       };
-       struct dm_io_request io_req = {
-               .bi_rw = WRITE,
-               .mem.type = DM_IO_VMA,
-               .mem.ptr.vma = ps->zero_area,
-               .client = ps->io_client,
-               .notify.fn = NULL,
-       };
-
-       return dm_io(&io_req, 1, &where, NULL);
+       return chunk_io(ps, ps->zero_area, area_location(ps, area), WRITE, 0);
 }
 
 static int read_header(struct pstore *ps, int *new_snapshot)
 {
        int r;
        struct disk_header *dh;
-       chunk_t chunk_size;
+       unsigned chunk_size;
        int chunk_size_supplied = 1;
+       char *chunk_err;
 
        /*
-        * Use default chunk size (or hardsect_size, if larger) if none supplied
+        * Use default chunk size (or logical_block_size, if larger)
+        * if none supplied
         */
        if (!ps->store->chunk_size) {
                ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS,
-                   bdev_logical_block_size(ps->store->cow->bdev) >> 9);
+                   bdev_logical_block_size(dm_snap_cow(ps->store->snap)->
+                                           bdev) >> 9);
                ps->store->chunk_mask = ps->store->chunk_size - 1;
                ps->store->chunk_shift = ffs(ps->store->chunk_size) - 1;
                chunk_size_supplied = 0;
@@ -297,11 +310,11 @@ static int read_header(struct pstore *ps, int *new_snapshot)
        if (r)
                return r;
 
-       r = chunk_io(ps, 0, READ, 1);
+       r = chunk_io(ps, ps->header_area, 0, READ, 1);
        if (r)
                goto bad;
 
-       dh = (struct disk_header *) ps->area;
+       dh = ps->header_area;
 
        if (le32_to_cpu(dh->magic) == 0) {
                *new_snapshot = 1;
@@ -319,20 +332,24 @@ static int read_header(struct pstore *ps, int *new_snapshot)
        ps->version = le32_to_cpu(dh->version);
        chunk_size = le32_to_cpu(dh->chunk_size);
 
-       if (!chunk_size_supplied || ps->store->chunk_size == chunk_size)
+       if (ps->store->chunk_size == chunk_size)
                return 0;
 
-       DMWARN("chunk size %llu in device metadata overrides "
-              "table chunk size of %llu.",
-              (unsigned long long)chunk_size,
-              (unsigned long long)ps->store->chunk_size);
+       if (chunk_size_supplied)
+               DMWARN("chunk size %u in device metadata overrides "
+                      "table chunk size of %u.",
+                      chunk_size, ps->store->chunk_size);
 
        /* We had a bogus chunk_size. Fix stuff up. */
        free_area(ps);
 
-       ps->store->chunk_size = chunk_size;
-       ps->store->chunk_mask = chunk_size - 1;
-       ps->store->chunk_shift = ffs(chunk_size) - 1;
+       r = dm_exception_store_set_chunk_size(ps->store, chunk_size,
+                                             &chunk_err);
+       if (r) {
+               DMERR("invalid on-disk chunk size %u: %s.",
+                     chunk_size, chunk_err);
+               return r;
+       }
 
        r = dm_io_client_resize(sectors_to_pages(ps->store->chunk_size),
                                ps->io_client);
@@ -351,15 +368,15 @@ static int write_header(struct pstore *ps)
 {
        struct disk_header *dh;
 
-       memset(ps->area, 0, ps->store->chunk_size << SECTOR_SHIFT);
+       memset(ps->header_area, 0, ps->store->chunk_size << SECTOR_SHIFT);
 
-       dh = (struct disk_header *) ps->area;
+       dh = ps->header_area;
        dh->magic = cpu_to_le32(SNAP_MAGIC);
        dh->valid = cpu_to_le32(ps->valid);
        dh->version = cpu_to_le32(ps->version);
        dh->chunk_size = cpu_to_le32(ps->store->chunk_size);
 
-       return chunk_io(ps, 0, WRITE, 1);
+       return chunk_io(ps, ps->header_area, 0, WRITE, 1);
 }
 
 /*
@@ -473,11 +490,22 @@ static struct pstore *get_info(struct dm_exception_store *store)
        return (struct pstore *) store->context;
 }
 
-static void persistent_fraction_full(struct dm_exception_store *store,
-                                    sector_t *numerator, sector_t *denominator)
+static void persistent_usage(struct dm_exception_store *store,
+                            sector_t *total_sectors,
+                            sector_t *sectors_allocated,
+                            sector_t *metadata_sectors)
 {
-       *numerator = get_info(store)->next_free * store->chunk_size;
-       *denominator = get_dev_size(store->cow->bdev);
+       struct pstore *ps = get_info(store);
+
+       *sectors_allocated = ps->next_free * store->chunk_size;
+       *total_sectors = get_dev_size(dm_snap_cow(store->snap)->bdev);
+
+       /*
+        * First chunk is the fixed header.
+        * Then there are (ps->current_area + 1) metadata chunks, each one
+        * separated from the next by ps->exceptions_per_area data chunks.
+        */
+       *metadata_sectors = (ps->current_area + 2) * store->chunk_size;
 }
 
 static void persistent_dtr(struct dm_exception_store *store)
@@ -536,44 +564,40 @@ static int persistent_read_metadata(struct dm_exception_store *store,
                ps->current_area = 0;
                zero_memory_area(ps);
                r = zero_disk_area(ps, 0);
-               if (r) {
+               if (r)
                        DMWARN("zero_disk_area(0) failed");
-                       return r;
-               }
-       } else {
-               /*
-                * Sanity checks.
-                */
-               if (ps->version != SNAPSHOT_DISK_VERSION) {
-                       DMWARN("unable to handle snapshot disk version %d",
-                              ps->version);
-                       return -EINVAL;
-               }
+               return r;
+       }
+       /*
+        * Sanity checks.
+        */
+       if (ps->version != SNAPSHOT_DISK_VERSION) {
+               DMWARN("unable to handle snapshot disk version %d",
+                      ps->version);
+               return -EINVAL;
+       }
 
-               /*
-                * Metadata are valid, but snapshot is invalidated
-                */
-               if (!ps->valid)
-                       return 1;
+       /*
+        * Metadata are valid, but snapshot is invalidated
+        */
+       if (!ps->valid)
+               return 1;
 
-               /*
-                * Read the metadata.
-                */
-               r = read_exceptions(ps, callback, callback_context);
-               if (r)
-                       return r;
-       }
+       /*
+        * Read the metadata.
+        */
+       r = read_exceptions(ps, callback, callback_context);
 
-       return 0;
+       return r;
 }
 
 static int persistent_prepare_exception(struct dm_exception_store *store,
-                                       struct dm_snap_exception *e)
+                                       struct dm_exception *e)
 {
        struct pstore *ps = get_info(store);
        uint32_t stride;
        chunk_t next_free;
-       sector_t size = get_dev_size(store->cow->bdev);
+       sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
 
        /* Is there enough room ? */
        if (size < ((ps->next_free + 1) * store->chunk_size))
@@ -595,7 +619,7 @@ static int persistent_prepare_exception(struct dm_exception_store *store,
 }
 
 static void persistent_commit_exception(struct dm_exception_store *store,
-                                       struct dm_snap_exception *e,
+                                       struct dm_exception *e,
                                        void (*callback) (void *, int success),
                                        void *callback_context)
 {
@@ -679,6 +703,8 @@ static int persistent_ctr(struct dm_exception_store *store,
        ps->valid = 1;
        ps->version = SNAPSHOT_DISK_VERSION;
        ps->area = NULL;
+       ps->zero_area = NULL;
+       ps->header_area = NULL;
        ps->next_free = 2;      /* skipping the header and first area */
        ps->current_committed = 0;
 
@@ -708,8 +734,7 @@ static unsigned persistent_status(struct dm_exception_store *store,
        case STATUSTYPE_INFO:
                break;
        case STATUSTYPE_TABLE:
-               DMEMIT(" %s P %llu", store->cow->name,
-                      (unsigned long long)store->chunk_size);
+               DMEMIT(" P %llu", (unsigned long long)store->chunk_size);
        }
 
        return sz;
@@ -724,7 +749,7 @@ static struct dm_exception_store_type _persistent_type = {
        .prepare_exception = persistent_prepare_exception,
        .commit_exception = persistent_commit_exception,
        .drop_snapshot = persistent_drop_snapshot,
-       .fraction_full = persistent_fraction_full,
+       .usage = persistent_usage,
        .status = persistent_status,
 };
 
@@ -737,7 +762,7 @@ static struct dm_exception_store_type _persistent_compat_type = {
        .prepare_exception = persistent_prepare_exception,
        .commit_exception = persistent_commit_exception,
        .drop_snapshot = persistent_drop_snapshot,
-       .fraction_full = persistent_fraction_full,
+       .usage = persistent_usage,
        .status = persistent_status,
 };