1*4db6bfe0SAlasdair G Kergon /* 2*4db6bfe0SAlasdair G Kergon * Copyright (C) 2001-2002 Sistina Software (UK) Limited. 3*4db6bfe0SAlasdair G Kergon * Copyright (C) 2006-2008 Red Hat GmbH 4*4db6bfe0SAlasdair G Kergon * 5*4db6bfe0SAlasdair G Kergon * This file is released under the GPL. 6*4db6bfe0SAlasdair G Kergon */ 7*4db6bfe0SAlasdair G Kergon 8*4db6bfe0SAlasdair G Kergon #include "dm-exception-store.h" 9*4db6bfe0SAlasdair G Kergon #include "dm-snap.h" 10*4db6bfe0SAlasdair G Kergon 11*4db6bfe0SAlasdair G Kergon #include <linux/mm.h> 12*4db6bfe0SAlasdair G Kergon #include <linux/pagemap.h> 13*4db6bfe0SAlasdair G Kergon #include <linux/vmalloc.h> 14*4db6bfe0SAlasdair G Kergon #include <linux/slab.h> 15*4db6bfe0SAlasdair G Kergon #include <linux/dm-io.h> 16*4db6bfe0SAlasdair G Kergon 17*4db6bfe0SAlasdair G Kergon #define DM_MSG_PREFIX "persistent snapshot" 18*4db6bfe0SAlasdair G Kergon #define DM_CHUNK_SIZE_DEFAULT_SECTORS 32 /* 16KB */ 19*4db6bfe0SAlasdair G Kergon 20*4db6bfe0SAlasdair G Kergon /*----------------------------------------------------------------- 21*4db6bfe0SAlasdair G Kergon * Persistent snapshots, by persistent we mean that the snapshot 22*4db6bfe0SAlasdair G Kergon * will survive a reboot. 23*4db6bfe0SAlasdair G Kergon *---------------------------------------------------------------*/ 24*4db6bfe0SAlasdair G Kergon 25*4db6bfe0SAlasdair G Kergon /* 26*4db6bfe0SAlasdair G Kergon * We need to store a record of which parts of the origin have 27*4db6bfe0SAlasdair G Kergon * been copied to the snapshot device. The snapshot code 28*4db6bfe0SAlasdair G Kergon * requires that we copy exception chunks to chunk aligned areas 29*4db6bfe0SAlasdair G Kergon * of the COW store. It makes sense therefore, to store the 30*4db6bfe0SAlasdair G Kergon * metadata in chunk size blocks. 31*4db6bfe0SAlasdair G Kergon * 32*4db6bfe0SAlasdair G Kergon * There is no backward or forward compatibility implemented, 33*4db6bfe0SAlasdair G Kergon * snapshots with different disk versions than the kernel will 34*4db6bfe0SAlasdair G Kergon * not be usable. It is expected that "lvcreate" will blank out 35*4db6bfe0SAlasdair G Kergon * the start of a fresh COW device before calling the snapshot 36*4db6bfe0SAlasdair G Kergon * constructor. 37*4db6bfe0SAlasdair G Kergon * 38*4db6bfe0SAlasdair G Kergon * The first chunk of the COW device just contains the header. 39*4db6bfe0SAlasdair G Kergon * After this there is a chunk filled with exception metadata, 40*4db6bfe0SAlasdair G Kergon * followed by as many exception chunks as can fit in the 41*4db6bfe0SAlasdair G Kergon * metadata areas. 42*4db6bfe0SAlasdair G Kergon * 43*4db6bfe0SAlasdair G Kergon * All on disk structures are in little-endian format. The end 44*4db6bfe0SAlasdair G Kergon * of the exceptions info is indicated by an exception with a 45*4db6bfe0SAlasdair G Kergon * new_chunk of 0, which is invalid since it would point to the 46*4db6bfe0SAlasdair G Kergon * header chunk. 47*4db6bfe0SAlasdair G Kergon */ 48*4db6bfe0SAlasdair G Kergon 49*4db6bfe0SAlasdair G Kergon /* 50*4db6bfe0SAlasdair G Kergon * Magic for persistent snapshots: "SnAp" - Feeble isn't it. 51*4db6bfe0SAlasdair G Kergon */ 52*4db6bfe0SAlasdair G Kergon #define SNAP_MAGIC 0x70416e53 53*4db6bfe0SAlasdair G Kergon 54*4db6bfe0SAlasdair G Kergon /* 55*4db6bfe0SAlasdair G Kergon * The on-disk version of the metadata. 56*4db6bfe0SAlasdair G Kergon */ 57*4db6bfe0SAlasdair G Kergon #define SNAPSHOT_DISK_VERSION 1 58*4db6bfe0SAlasdair G Kergon 59*4db6bfe0SAlasdair G Kergon struct disk_header { 60*4db6bfe0SAlasdair G Kergon uint32_t magic; 61*4db6bfe0SAlasdair G Kergon 62*4db6bfe0SAlasdair G Kergon /* 63*4db6bfe0SAlasdair G Kergon * Is this snapshot valid. There is no way of recovering 64*4db6bfe0SAlasdair G Kergon * an invalid snapshot. 65*4db6bfe0SAlasdair G Kergon */ 66*4db6bfe0SAlasdair G Kergon uint32_t valid; 67*4db6bfe0SAlasdair G Kergon 68*4db6bfe0SAlasdair G Kergon /* 69*4db6bfe0SAlasdair G Kergon * Simple, incrementing version. no backward 70*4db6bfe0SAlasdair G Kergon * compatibility. 71*4db6bfe0SAlasdair G Kergon */ 72*4db6bfe0SAlasdair G Kergon uint32_t version; 73*4db6bfe0SAlasdair G Kergon 74*4db6bfe0SAlasdair G Kergon /* In sectors */ 75*4db6bfe0SAlasdair G Kergon uint32_t chunk_size; 76*4db6bfe0SAlasdair G Kergon }; 77*4db6bfe0SAlasdair G Kergon 78*4db6bfe0SAlasdair G Kergon struct disk_exception { 79*4db6bfe0SAlasdair G Kergon uint64_t old_chunk; 80*4db6bfe0SAlasdair G Kergon uint64_t new_chunk; 81*4db6bfe0SAlasdair G Kergon }; 82*4db6bfe0SAlasdair G Kergon 83*4db6bfe0SAlasdair G Kergon struct commit_callback { 84*4db6bfe0SAlasdair G Kergon void (*callback)(void *, int success); 85*4db6bfe0SAlasdair G Kergon void *context; 86*4db6bfe0SAlasdair G Kergon }; 87*4db6bfe0SAlasdair G Kergon 88*4db6bfe0SAlasdair G Kergon /* 89*4db6bfe0SAlasdair G Kergon * The top level structure for a persistent exception store. 90*4db6bfe0SAlasdair G Kergon */ 91*4db6bfe0SAlasdair G Kergon struct pstore { 92*4db6bfe0SAlasdair G Kergon struct dm_snapshot *snap; /* up pointer to my snapshot */ 93*4db6bfe0SAlasdair G Kergon int version; 94*4db6bfe0SAlasdair G Kergon int valid; 95*4db6bfe0SAlasdair G Kergon uint32_t exceptions_per_area; 96*4db6bfe0SAlasdair G Kergon 97*4db6bfe0SAlasdair G Kergon /* 98*4db6bfe0SAlasdair G Kergon * Now that we have an asynchronous kcopyd there is no 99*4db6bfe0SAlasdair G Kergon * need for large chunk sizes, so it wont hurt to have a 100*4db6bfe0SAlasdair G Kergon * whole chunks worth of metadata in memory at once. 101*4db6bfe0SAlasdair G Kergon */ 102*4db6bfe0SAlasdair G Kergon void *area; 103*4db6bfe0SAlasdair G Kergon 104*4db6bfe0SAlasdair G Kergon /* 105*4db6bfe0SAlasdair G Kergon * An area of zeros used to clear the next area. 106*4db6bfe0SAlasdair G Kergon */ 107*4db6bfe0SAlasdair G Kergon void *zero_area; 108*4db6bfe0SAlasdair G Kergon 109*4db6bfe0SAlasdair G Kergon /* 110*4db6bfe0SAlasdair G Kergon * Used to keep track of which metadata area the data in 111*4db6bfe0SAlasdair G Kergon * 'chunk' refers to. 112*4db6bfe0SAlasdair G Kergon */ 113*4db6bfe0SAlasdair G Kergon chunk_t current_area; 114*4db6bfe0SAlasdair G Kergon 115*4db6bfe0SAlasdair G Kergon /* 116*4db6bfe0SAlasdair G Kergon * The next free chunk for an exception. 117*4db6bfe0SAlasdair G Kergon */ 118*4db6bfe0SAlasdair G Kergon chunk_t next_free; 119*4db6bfe0SAlasdair G Kergon 120*4db6bfe0SAlasdair G Kergon /* 121*4db6bfe0SAlasdair G Kergon * The index of next free exception in the current 122*4db6bfe0SAlasdair G Kergon * metadata area. 123*4db6bfe0SAlasdair G Kergon */ 124*4db6bfe0SAlasdair G Kergon uint32_t current_committed; 125*4db6bfe0SAlasdair G Kergon 126*4db6bfe0SAlasdair G Kergon atomic_t pending_count; 127*4db6bfe0SAlasdair G Kergon uint32_t callback_count; 128*4db6bfe0SAlasdair G Kergon struct commit_callback *callbacks; 129*4db6bfe0SAlasdair G Kergon struct dm_io_client *io_client; 130*4db6bfe0SAlasdair G Kergon 131*4db6bfe0SAlasdair G Kergon struct workqueue_struct *metadata_wq; 132*4db6bfe0SAlasdair G Kergon }; 133*4db6bfe0SAlasdair G Kergon 134*4db6bfe0SAlasdair G Kergon static unsigned sectors_to_pages(unsigned sectors) 135*4db6bfe0SAlasdair G Kergon { 136*4db6bfe0SAlasdair G Kergon return DIV_ROUND_UP(sectors, PAGE_SIZE >> 9); 137*4db6bfe0SAlasdair G Kergon } 138*4db6bfe0SAlasdair G Kergon 139*4db6bfe0SAlasdair G Kergon static int alloc_area(struct pstore *ps) 140*4db6bfe0SAlasdair G Kergon { 141*4db6bfe0SAlasdair G Kergon int r = -ENOMEM; 142*4db6bfe0SAlasdair G Kergon size_t len; 143*4db6bfe0SAlasdair G Kergon 144*4db6bfe0SAlasdair G Kergon len = ps->snap->chunk_size << SECTOR_SHIFT; 145*4db6bfe0SAlasdair G Kergon 146*4db6bfe0SAlasdair G Kergon /* 147*4db6bfe0SAlasdair G Kergon * Allocate the chunk_size block of memory that will hold 148*4db6bfe0SAlasdair G Kergon * a single metadata area. 149*4db6bfe0SAlasdair G Kergon */ 150*4db6bfe0SAlasdair G Kergon ps->area = vmalloc(len); 151*4db6bfe0SAlasdair G Kergon if (!ps->area) 152*4db6bfe0SAlasdair G Kergon return r; 153*4db6bfe0SAlasdair G Kergon 154*4db6bfe0SAlasdair G Kergon ps->zero_area = vmalloc(len); 155*4db6bfe0SAlasdair G Kergon if (!ps->zero_area) { 156*4db6bfe0SAlasdair G Kergon vfree(ps->area); 157*4db6bfe0SAlasdair G Kergon return r; 158*4db6bfe0SAlasdair G Kergon } 159*4db6bfe0SAlasdair G Kergon memset(ps->zero_area, 0, len); 160*4db6bfe0SAlasdair G Kergon 161*4db6bfe0SAlasdair G Kergon return 0; 162*4db6bfe0SAlasdair G Kergon } 163*4db6bfe0SAlasdair G Kergon 164*4db6bfe0SAlasdair G Kergon static void free_area(struct pstore *ps) 165*4db6bfe0SAlasdair G Kergon { 166*4db6bfe0SAlasdair G Kergon vfree(ps->area); 167*4db6bfe0SAlasdair G Kergon ps->area = NULL; 168*4db6bfe0SAlasdair G Kergon vfree(ps->zero_area); 169*4db6bfe0SAlasdair G Kergon ps->zero_area = NULL; 170*4db6bfe0SAlasdair G Kergon } 171*4db6bfe0SAlasdair G Kergon 172*4db6bfe0SAlasdair G Kergon struct mdata_req { 173*4db6bfe0SAlasdair G Kergon struct dm_io_region *where; 174*4db6bfe0SAlasdair G Kergon struct dm_io_request *io_req; 175*4db6bfe0SAlasdair G Kergon struct work_struct work; 176*4db6bfe0SAlasdair G Kergon int result; 177*4db6bfe0SAlasdair G Kergon }; 178*4db6bfe0SAlasdair G Kergon 179*4db6bfe0SAlasdair G Kergon static void do_metadata(struct work_struct *work) 180*4db6bfe0SAlasdair G Kergon { 181*4db6bfe0SAlasdair G Kergon struct mdata_req *req = container_of(work, struct mdata_req, work); 182*4db6bfe0SAlasdair G Kergon 183*4db6bfe0SAlasdair G Kergon req->result = dm_io(req->io_req, 1, req->where, NULL); 184*4db6bfe0SAlasdair G Kergon } 185*4db6bfe0SAlasdair G Kergon 186*4db6bfe0SAlasdair G Kergon /* 187*4db6bfe0SAlasdair G Kergon * Read or write a chunk aligned and sized block of data from a device. 188*4db6bfe0SAlasdair G Kergon */ 189*4db6bfe0SAlasdair G Kergon static int chunk_io(struct pstore *ps, chunk_t chunk, int rw, int metadata) 190*4db6bfe0SAlasdair G Kergon { 191*4db6bfe0SAlasdair G Kergon struct dm_io_region where = { 192*4db6bfe0SAlasdair G Kergon .bdev = ps->snap->cow->bdev, 193*4db6bfe0SAlasdair G Kergon .sector = ps->snap->chunk_size * chunk, 194*4db6bfe0SAlasdair G Kergon .count = ps->snap->chunk_size, 195*4db6bfe0SAlasdair G Kergon }; 196*4db6bfe0SAlasdair G Kergon struct dm_io_request io_req = { 197*4db6bfe0SAlasdair G Kergon .bi_rw = rw, 198*4db6bfe0SAlasdair G Kergon .mem.type = DM_IO_VMA, 199*4db6bfe0SAlasdair G Kergon .mem.ptr.vma = ps->area, 200*4db6bfe0SAlasdair G Kergon .client = ps->io_client, 201*4db6bfe0SAlasdair G Kergon .notify.fn = NULL, 202*4db6bfe0SAlasdair G Kergon }; 203*4db6bfe0SAlasdair G Kergon struct mdata_req req; 204*4db6bfe0SAlasdair G Kergon 205*4db6bfe0SAlasdair G Kergon if (!metadata) 206*4db6bfe0SAlasdair G Kergon return dm_io(&io_req, 1, &where, NULL); 207*4db6bfe0SAlasdair G Kergon 208*4db6bfe0SAlasdair G Kergon req.where = &where; 209*4db6bfe0SAlasdair G Kergon req.io_req = &io_req; 210*4db6bfe0SAlasdair G Kergon 211*4db6bfe0SAlasdair G Kergon /* 212*4db6bfe0SAlasdair G Kergon * Issue the synchronous I/O from a different thread 213*4db6bfe0SAlasdair G Kergon * to avoid generic_make_request recursion. 214*4db6bfe0SAlasdair G Kergon */ 215*4db6bfe0SAlasdair G Kergon INIT_WORK(&req.work, do_metadata); 216*4db6bfe0SAlasdair G Kergon queue_work(ps->metadata_wq, &req.work); 217*4db6bfe0SAlasdair G Kergon flush_workqueue(ps->metadata_wq); 218*4db6bfe0SAlasdair G Kergon 219*4db6bfe0SAlasdair G Kergon return req.result; 220*4db6bfe0SAlasdair G Kergon } 221*4db6bfe0SAlasdair G Kergon 222*4db6bfe0SAlasdair G Kergon /* 223*4db6bfe0SAlasdair G Kergon * Convert a metadata area index to a chunk index. 224*4db6bfe0SAlasdair G Kergon */ 225*4db6bfe0SAlasdair G Kergon static chunk_t area_location(struct pstore *ps, chunk_t area) 226*4db6bfe0SAlasdair G Kergon { 227*4db6bfe0SAlasdair G Kergon return 1 + ((ps->exceptions_per_area + 1) * area); 228*4db6bfe0SAlasdair G Kergon } 229*4db6bfe0SAlasdair G Kergon 230*4db6bfe0SAlasdair G Kergon /* 231*4db6bfe0SAlasdair G Kergon * Read or write a metadata area. Remembering to skip the first 232*4db6bfe0SAlasdair G Kergon * chunk which holds the header. 233*4db6bfe0SAlasdair G Kergon */ 234*4db6bfe0SAlasdair G Kergon static int area_io(struct pstore *ps, int rw) 235*4db6bfe0SAlasdair G Kergon { 236*4db6bfe0SAlasdair G Kergon int r; 237*4db6bfe0SAlasdair G Kergon chunk_t chunk; 238*4db6bfe0SAlasdair G Kergon 239*4db6bfe0SAlasdair G Kergon chunk = area_location(ps, ps->current_area); 240*4db6bfe0SAlasdair G Kergon 241*4db6bfe0SAlasdair G Kergon r = chunk_io(ps, chunk, rw, 0); 242*4db6bfe0SAlasdair G Kergon if (r) 243*4db6bfe0SAlasdair G Kergon return r; 244*4db6bfe0SAlasdair G Kergon 245*4db6bfe0SAlasdair G Kergon return 0; 246*4db6bfe0SAlasdair G Kergon } 247*4db6bfe0SAlasdair G Kergon 248*4db6bfe0SAlasdair G Kergon static void zero_memory_area(struct pstore *ps) 249*4db6bfe0SAlasdair G Kergon { 250*4db6bfe0SAlasdair G Kergon memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT); 251*4db6bfe0SAlasdair G Kergon } 252*4db6bfe0SAlasdair G Kergon 253*4db6bfe0SAlasdair G Kergon static int zero_disk_area(struct pstore *ps, chunk_t area) 254*4db6bfe0SAlasdair G Kergon { 255*4db6bfe0SAlasdair G Kergon struct dm_io_region where = { 256*4db6bfe0SAlasdair G Kergon .bdev = ps->snap->cow->bdev, 257*4db6bfe0SAlasdair G Kergon .sector = ps->snap->chunk_size * area_location(ps, area), 258*4db6bfe0SAlasdair G Kergon .count = ps->snap->chunk_size, 259*4db6bfe0SAlasdair G Kergon }; 260*4db6bfe0SAlasdair G Kergon struct dm_io_request io_req = { 261*4db6bfe0SAlasdair G Kergon .bi_rw = WRITE, 262*4db6bfe0SAlasdair G Kergon .mem.type = DM_IO_VMA, 263*4db6bfe0SAlasdair G Kergon .mem.ptr.vma = ps->zero_area, 264*4db6bfe0SAlasdair G Kergon .client = ps->io_client, 265*4db6bfe0SAlasdair G Kergon .notify.fn = NULL, 266*4db6bfe0SAlasdair G Kergon }; 267*4db6bfe0SAlasdair G Kergon 268*4db6bfe0SAlasdair G Kergon return dm_io(&io_req, 1, &where, NULL); 269*4db6bfe0SAlasdair G Kergon } 270*4db6bfe0SAlasdair G Kergon 271*4db6bfe0SAlasdair G Kergon static int read_header(struct pstore *ps, int *new_snapshot) 272*4db6bfe0SAlasdair G Kergon { 273*4db6bfe0SAlasdair G Kergon int r; 274*4db6bfe0SAlasdair G Kergon struct disk_header *dh; 275*4db6bfe0SAlasdair G Kergon chunk_t chunk_size; 276*4db6bfe0SAlasdair G Kergon int chunk_size_supplied = 1; 277*4db6bfe0SAlasdair G Kergon 278*4db6bfe0SAlasdair G Kergon /* 279*4db6bfe0SAlasdair G Kergon * Use default chunk size (or hardsect_size, if larger) if none supplied 280*4db6bfe0SAlasdair G Kergon */ 281*4db6bfe0SAlasdair G Kergon if (!ps->snap->chunk_size) { 282*4db6bfe0SAlasdair G Kergon ps->snap->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS, 283*4db6bfe0SAlasdair G Kergon bdev_hardsect_size(ps->snap->cow->bdev) >> 9); 284*4db6bfe0SAlasdair G Kergon ps->snap->chunk_mask = ps->snap->chunk_size - 1; 285*4db6bfe0SAlasdair G Kergon ps->snap->chunk_shift = ffs(ps->snap->chunk_size) - 1; 286*4db6bfe0SAlasdair G Kergon chunk_size_supplied = 0; 287*4db6bfe0SAlasdair G Kergon } 288*4db6bfe0SAlasdair G Kergon 289*4db6bfe0SAlasdair G Kergon ps->io_client = dm_io_client_create(sectors_to_pages(ps->snap-> 290*4db6bfe0SAlasdair G Kergon chunk_size)); 291*4db6bfe0SAlasdair G Kergon if (IS_ERR(ps->io_client)) 292*4db6bfe0SAlasdair G Kergon return PTR_ERR(ps->io_client); 293*4db6bfe0SAlasdair G Kergon 294*4db6bfe0SAlasdair G Kergon r = alloc_area(ps); 295*4db6bfe0SAlasdair G Kergon if (r) 296*4db6bfe0SAlasdair G Kergon return r; 297*4db6bfe0SAlasdair G Kergon 298*4db6bfe0SAlasdair G Kergon r = chunk_io(ps, 0, READ, 1); 299*4db6bfe0SAlasdair G Kergon if (r) 300*4db6bfe0SAlasdair G Kergon goto bad; 301*4db6bfe0SAlasdair G Kergon 302*4db6bfe0SAlasdair G Kergon dh = (struct disk_header *) ps->area; 303*4db6bfe0SAlasdair G Kergon 304*4db6bfe0SAlasdair G Kergon if (le32_to_cpu(dh->magic) == 0) { 305*4db6bfe0SAlasdair G Kergon *new_snapshot = 1; 306*4db6bfe0SAlasdair G Kergon return 0; 307*4db6bfe0SAlasdair G Kergon } 308*4db6bfe0SAlasdair G Kergon 309*4db6bfe0SAlasdair G Kergon if (le32_to_cpu(dh->magic) != SNAP_MAGIC) { 310*4db6bfe0SAlasdair G Kergon DMWARN("Invalid or corrupt snapshot"); 311*4db6bfe0SAlasdair G Kergon r = -ENXIO; 312*4db6bfe0SAlasdair G Kergon goto bad; 313*4db6bfe0SAlasdair G Kergon } 314*4db6bfe0SAlasdair G Kergon 315*4db6bfe0SAlasdair G Kergon *new_snapshot = 0; 316*4db6bfe0SAlasdair G Kergon ps->valid = le32_to_cpu(dh->valid); 317*4db6bfe0SAlasdair G Kergon ps->version = le32_to_cpu(dh->version); 318*4db6bfe0SAlasdair G Kergon chunk_size = le32_to_cpu(dh->chunk_size); 319*4db6bfe0SAlasdair G Kergon 320*4db6bfe0SAlasdair G Kergon if (!chunk_size_supplied || ps->snap->chunk_size == chunk_size) 321*4db6bfe0SAlasdair G Kergon return 0; 322*4db6bfe0SAlasdair G Kergon 323*4db6bfe0SAlasdair G Kergon DMWARN("chunk size %llu in device metadata overrides " 324*4db6bfe0SAlasdair G Kergon "table chunk size of %llu.", 325*4db6bfe0SAlasdair G Kergon (unsigned long long)chunk_size, 326*4db6bfe0SAlasdair G Kergon (unsigned long long)ps->snap->chunk_size); 327*4db6bfe0SAlasdair G Kergon 328*4db6bfe0SAlasdair G Kergon /* We had a bogus chunk_size. Fix stuff up. */ 329*4db6bfe0SAlasdair G Kergon free_area(ps); 330*4db6bfe0SAlasdair G Kergon 331*4db6bfe0SAlasdair G Kergon ps->snap->chunk_size = chunk_size; 332*4db6bfe0SAlasdair G Kergon ps->snap->chunk_mask = chunk_size - 1; 333*4db6bfe0SAlasdair G Kergon ps->snap->chunk_shift = ffs(chunk_size) - 1; 334*4db6bfe0SAlasdair G Kergon 335*4db6bfe0SAlasdair G Kergon r = dm_io_client_resize(sectors_to_pages(ps->snap->chunk_size), 336*4db6bfe0SAlasdair G Kergon ps->io_client); 337*4db6bfe0SAlasdair G Kergon if (r) 338*4db6bfe0SAlasdair G Kergon return r; 339*4db6bfe0SAlasdair G Kergon 340*4db6bfe0SAlasdair G Kergon r = alloc_area(ps); 341*4db6bfe0SAlasdair G Kergon return r; 342*4db6bfe0SAlasdair G Kergon 343*4db6bfe0SAlasdair G Kergon bad: 344*4db6bfe0SAlasdair G Kergon free_area(ps); 345*4db6bfe0SAlasdair G Kergon return r; 346*4db6bfe0SAlasdair G Kergon } 347*4db6bfe0SAlasdair G Kergon 348*4db6bfe0SAlasdair G Kergon static int write_header(struct pstore *ps) 349*4db6bfe0SAlasdair G Kergon { 350*4db6bfe0SAlasdair G Kergon struct disk_header *dh; 351*4db6bfe0SAlasdair G Kergon 352*4db6bfe0SAlasdair G Kergon memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT); 353*4db6bfe0SAlasdair G Kergon 354*4db6bfe0SAlasdair G Kergon dh = (struct disk_header *) ps->area; 355*4db6bfe0SAlasdair G Kergon dh->magic = cpu_to_le32(SNAP_MAGIC); 356*4db6bfe0SAlasdair G Kergon dh->valid = cpu_to_le32(ps->valid); 357*4db6bfe0SAlasdair G Kergon dh->version = cpu_to_le32(ps->version); 358*4db6bfe0SAlasdair G Kergon dh->chunk_size = cpu_to_le32(ps->snap->chunk_size); 359*4db6bfe0SAlasdair G Kergon 360*4db6bfe0SAlasdair G Kergon return chunk_io(ps, 0, WRITE, 1); 361*4db6bfe0SAlasdair G Kergon } 362*4db6bfe0SAlasdair G Kergon 363*4db6bfe0SAlasdair G Kergon /* 364*4db6bfe0SAlasdair G Kergon * Access functions for the disk exceptions, these do the endian conversions. 365*4db6bfe0SAlasdair G Kergon */ 366*4db6bfe0SAlasdair G Kergon static struct disk_exception *get_exception(struct pstore *ps, uint32_t index) 367*4db6bfe0SAlasdair G Kergon { 368*4db6bfe0SAlasdair G Kergon BUG_ON(index >= ps->exceptions_per_area); 369*4db6bfe0SAlasdair G Kergon 370*4db6bfe0SAlasdair G Kergon return ((struct disk_exception *) ps->area) + index; 371*4db6bfe0SAlasdair G Kergon } 372*4db6bfe0SAlasdair G Kergon 373*4db6bfe0SAlasdair G Kergon static void read_exception(struct pstore *ps, 374*4db6bfe0SAlasdair G Kergon uint32_t index, struct disk_exception *result) 375*4db6bfe0SAlasdair G Kergon { 376*4db6bfe0SAlasdair G Kergon struct disk_exception *e = get_exception(ps, index); 377*4db6bfe0SAlasdair G Kergon 378*4db6bfe0SAlasdair G Kergon /* copy it */ 379*4db6bfe0SAlasdair G Kergon result->old_chunk = le64_to_cpu(e->old_chunk); 380*4db6bfe0SAlasdair G Kergon result->new_chunk = le64_to_cpu(e->new_chunk); 381*4db6bfe0SAlasdair G Kergon } 382*4db6bfe0SAlasdair G Kergon 383*4db6bfe0SAlasdair G Kergon static void write_exception(struct pstore *ps, 384*4db6bfe0SAlasdair G Kergon uint32_t index, struct disk_exception *de) 385*4db6bfe0SAlasdair G Kergon { 386*4db6bfe0SAlasdair G Kergon struct disk_exception *e = get_exception(ps, index); 387*4db6bfe0SAlasdair G Kergon 388*4db6bfe0SAlasdair G Kergon /* copy it */ 389*4db6bfe0SAlasdair G Kergon e->old_chunk = cpu_to_le64(de->old_chunk); 390*4db6bfe0SAlasdair G Kergon e->new_chunk = cpu_to_le64(de->new_chunk); 391*4db6bfe0SAlasdair G Kergon } 392*4db6bfe0SAlasdair G Kergon 393*4db6bfe0SAlasdair G Kergon /* 394*4db6bfe0SAlasdair G Kergon * Registers the exceptions that are present in the current area. 395*4db6bfe0SAlasdair G Kergon * 'full' is filled in to indicate if the area has been 396*4db6bfe0SAlasdair G Kergon * filled. 397*4db6bfe0SAlasdair G Kergon */ 398*4db6bfe0SAlasdair G Kergon static int insert_exceptions(struct pstore *ps, int *full) 399*4db6bfe0SAlasdair G Kergon { 400*4db6bfe0SAlasdair G Kergon int r; 401*4db6bfe0SAlasdair G Kergon unsigned int i; 402*4db6bfe0SAlasdair G Kergon struct disk_exception de; 403*4db6bfe0SAlasdair G Kergon 404*4db6bfe0SAlasdair G Kergon /* presume the area is full */ 405*4db6bfe0SAlasdair G Kergon *full = 1; 406*4db6bfe0SAlasdair G Kergon 407*4db6bfe0SAlasdair G Kergon for (i = 0; i < ps->exceptions_per_area; i++) { 408*4db6bfe0SAlasdair G Kergon read_exception(ps, i, &de); 409*4db6bfe0SAlasdair G Kergon 410*4db6bfe0SAlasdair G Kergon /* 411*4db6bfe0SAlasdair G Kergon * If the new_chunk is pointing at the start of 412*4db6bfe0SAlasdair G Kergon * the COW device, where the first metadata area 413*4db6bfe0SAlasdair G Kergon * is we know that we've hit the end of the 414*4db6bfe0SAlasdair G Kergon * exceptions. Therefore the area is not full. 415*4db6bfe0SAlasdair G Kergon */ 416*4db6bfe0SAlasdair G Kergon if (de.new_chunk == 0LL) { 417*4db6bfe0SAlasdair G Kergon ps->current_committed = i; 418*4db6bfe0SAlasdair G Kergon *full = 0; 419*4db6bfe0SAlasdair G Kergon break; 420*4db6bfe0SAlasdair G Kergon } 421*4db6bfe0SAlasdair G Kergon 422*4db6bfe0SAlasdair G Kergon /* 423*4db6bfe0SAlasdair G Kergon * Keep track of the start of the free chunks. 424*4db6bfe0SAlasdair G Kergon */ 425*4db6bfe0SAlasdair G Kergon if (ps->next_free <= de.new_chunk) 426*4db6bfe0SAlasdair G Kergon ps->next_free = de.new_chunk + 1; 427*4db6bfe0SAlasdair G Kergon 428*4db6bfe0SAlasdair G Kergon /* 429*4db6bfe0SAlasdair G Kergon * Otherwise we add the exception to the snapshot. 430*4db6bfe0SAlasdair G Kergon */ 431*4db6bfe0SAlasdair G Kergon r = dm_add_exception(ps->snap, de.old_chunk, de.new_chunk); 432*4db6bfe0SAlasdair G Kergon if (r) 433*4db6bfe0SAlasdair G Kergon return r; 434*4db6bfe0SAlasdair G Kergon } 435*4db6bfe0SAlasdair G Kergon 436*4db6bfe0SAlasdair G Kergon return 0; 437*4db6bfe0SAlasdair G Kergon } 438*4db6bfe0SAlasdair G Kergon 439*4db6bfe0SAlasdair G Kergon static int read_exceptions(struct pstore *ps) 440*4db6bfe0SAlasdair G Kergon { 441*4db6bfe0SAlasdair G Kergon int r, full = 1; 442*4db6bfe0SAlasdair G Kergon 443*4db6bfe0SAlasdair G Kergon /* 444*4db6bfe0SAlasdair G Kergon * Keeping reading chunks and inserting exceptions until 445*4db6bfe0SAlasdair G Kergon * we find a partially full area. 446*4db6bfe0SAlasdair G Kergon */ 447*4db6bfe0SAlasdair G Kergon for (ps->current_area = 0; full; ps->current_area++) { 448*4db6bfe0SAlasdair G Kergon r = area_io(ps, READ); 449*4db6bfe0SAlasdair G Kergon if (r) 450*4db6bfe0SAlasdair G Kergon return r; 451*4db6bfe0SAlasdair G Kergon 452*4db6bfe0SAlasdair G Kergon r = insert_exceptions(ps, &full); 453*4db6bfe0SAlasdair G Kergon if (r) 454*4db6bfe0SAlasdair G Kergon return r; 455*4db6bfe0SAlasdair G Kergon } 456*4db6bfe0SAlasdair G Kergon 457*4db6bfe0SAlasdair G Kergon ps->current_area--; 458*4db6bfe0SAlasdair G Kergon 459*4db6bfe0SAlasdair G Kergon return 0; 460*4db6bfe0SAlasdair G Kergon } 461*4db6bfe0SAlasdair G Kergon 462*4db6bfe0SAlasdair G Kergon static struct pstore *get_info(struct dm_exception_store *store) 463*4db6bfe0SAlasdair G Kergon { 464*4db6bfe0SAlasdair G Kergon return (struct pstore *) store->context; 465*4db6bfe0SAlasdair G Kergon } 466*4db6bfe0SAlasdair G Kergon 467*4db6bfe0SAlasdair G Kergon static void persistent_fraction_full(struct dm_exception_store *store, 468*4db6bfe0SAlasdair G Kergon sector_t *numerator, sector_t *denominator) 469*4db6bfe0SAlasdair G Kergon { 470*4db6bfe0SAlasdair G Kergon *numerator = get_info(store)->next_free * store->snap->chunk_size; 471*4db6bfe0SAlasdair G Kergon *denominator = get_dev_size(store->snap->cow->bdev); 472*4db6bfe0SAlasdair G Kergon } 473*4db6bfe0SAlasdair G Kergon 474*4db6bfe0SAlasdair G Kergon static void persistent_destroy(struct dm_exception_store *store) 475*4db6bfe0SAlasdair G Kergon { 476*4db6bfe0SAlasdair G Kergon struct pstore *ps = get_info(store); 477*4db6bfe0SAlasdair G Kergon 478*4db6bfe0SAlasdair G Kergon destroy_workqueue(ps->metadata_wq); 479*4db6bfe0SAlasdair G Kergon dm_io_client_destroy(ps->io_client); 480*4db6bfe0SAlasdair G Kergon vfree(ps->callbacks); 481*4db6bfe0SAlasdair G Kergon free_area(ps); 482*4db6bfe0SAlasdair G Kergon kfree(ps); 483*4db6bfe0SAlasdair G Kergon } 484*4db6bfe0SAlasdair G Kergon 485*4db6bfe0SAlasdair G Kergon static int persistent_read_metadata(struct dm_exception_store *store) 486*4db6bfe0SAlasdair G Kergon { 487*4db6bfe0SAlasdair G Kergon int r, uninitialized_var(new_snapshot); 488*4db6bfe0SAlasdair G Kergon struct pstore *ps = get_info(store); 489*4db6bfe0SAlasdair G Kergon 490*4db6bfe0SAlasdair G Kergon /* 491*4db6bfe0SAlasdair G Kergon * Read the snapshot header. 492*4db6bfe0SAlasdair G Kergon */ 493*4db6bfe0SAlasdair G Kergon r = read_header(ps, &new_snapshot); 494*4db6bfe0SAlasdair G Kergon if (r) 495*4db6bfe0SAlasdair G Kergon return r; 496*4db6bfe0SAlasdair G Kergon 497*4db6bfe0SAlasdair G Kergon /* 498*4db6bfe0SAlasdair G Kergon * Now we know correct chunk_size, complete the initialisation. 499*4db6bfe0SAlasdair G Kergon */ 500*4db6bfe0SAlasdair G Kergon ps->exceptions_per_area = (ps->snap->chunk_size << SECTOR_SHIFT) / 501*4db6bfe0SAlasdair G Kergon sizeof(struct disk_exception); 502*4db6bfe0SAlasdair G Kergon ps->callbacks = dm_vcalloc(ps->exceptions_per_area, 503*4db6bfe0SAlasdair G Kergon sizeof(*ps->callbacks)); 504*4db6bfe0SAlasdair G Kergon if (!ps->callbacks) 505*4db6bfe0SAlasdair G Kergon return -ENOMEM; 506*4db6bfe0SAlasdair G Kergon 507*4db6bfe0SAlasdair G Kergon /* 508*4db6bfe0SAlasdair G Kergon * Do we need to setup a new snapshot ? 509*4db6bfe0SAlasdair G Kergon */ 510*4db6bfe0SAlasdair G Kergon if (new_snapshot) { 511*4db6bfe0SAlasdair G Kergon r = write_header(ps); 512*4db6bfe0SAlasdair G Kergon if (r) { 513*4db6bfe0SAlasdair G Kergon DMWARN("write_header failed"); 514*4db6bfe0SAlasdair G Kergon return r; 515*4db6bfe0SAlasdair G Kergon } 516*4db6bfe0SAlasdair G Kergon 517*4db6bfe0SAlasdair G Kergon ps->current_area = 0; 518*4db6bfe0SAlasdair G Kergon zero_memory_area(ps); 519*4db6bfe0SAlasdair G Kergon r = zero_disk_area(ps, 0); 520*4db6bfe0SAlasdair G Kergon if (r) { 521*4db6bfe0SAlasdair G Kergon DMWARN("zero_disk_area(0) failed"); 522*4db6bfe0SAlasdair G Kergon return r; 523*4db6bfe0SAlasdair G Kergon } 524*4db6bfe0SAlasdair G Kergon } else { 525*4db6bfe0SAlasdair G Kergon /* 526*4db6bfe0SAlasdair G Kergon * Sanity checks. 527*4db6bfe0SAlasdair G Kergon */ 528*4db6bfe0SAlasdair G Kergon if (ps->version != SNAPSHOT_DISK_VERSION) { 529*4db6bfe0SAlasdair G Kergon DMWARN("unable to handle snapshot disk version %d", 530*4db6bfe0SAlasdair G Kergon ps->version); 531*4db6bfe0SAlasdair G Kergon return -EINVAL; 532*4db6bfe0SAlasdair G Kergon } 533*4db6bfe0SAlasdair G Kergon 534*4db6bfe0SAlasdair G Kergon /* 535*4db6bfe0SAlasdair G Kergon * Metadata are valid, but snapshot is invalidated 536*4db6bfe0SAlasdair G Kergon */ 537*4db6bfe0SAlasdair G Kergon if (!ps->valid) 538*4db6bfe0SAlasdair G Kergon return 1; 539*4db6bfe0SAlasdair G Kergon 540*4db6bfe0SAlasdair G Kergon /* 541*4db6bfe0SAlasdair G Kergon * Read the metadata. 542*4db6bfe0SAlasdair G Kergon */ 543*4db6bfe0SAlasdair G Kergon r = read_exceptions(ps); 544*4db6bfe0SAlasdair G Kergon if (r) 545*4db6bfe0SAlasdair G Kergon return r; 546*4db6bfe0SAlasdair G Kergon } 547*4db6bfe0SAlasdair G Kergon 548*4db6bfe0SAlasdair G Kergon return 0; 549*4db6bfe0SAlasdair G Kergon } 550*4db6bfe0SAlasdair G Kergon 551*4db6bfe0SAlasdair G Kergon static int persistent_prepare(struct dm_exception_store *store, 552*4db6bfe0SAlasdair G Kergon struct dm_snap_exception *e) 553*4db6bfe0SAlasdair G Kergon { 554*4db6bfe0SAlasdair G Kergon struct pstore *ps = get_info(store); 555*4db6bfe0SAlasdair G Kergon uint32_t stride; 556*4db6bfe0SAlasdair G Kergon chunk_t next_free; 557*4db6bfe0SAlasdair G Kergon sector_t size = get_dev_size(store->snap->cow->bdev); 558*4db6bfe0SAlasdair G Kergon 559*4db6bfe0SAlasdair G Kergon /* Is there enough room ? */ 560*4db6bfe0SAlasdair G Kergon if (size < ((ps->next_free + 1) * store->snap->chunk_size)) 561*4db6bfe0SAlasdair G Kergon return -ENOSPC; 562*4db6bfe0SAlasdair G Kergon 563*4db6bfe0SAlasdair G Kergon e->new_chunk = ps->next_free; 564*4db6bfe0SAlasdair G Kergon 565*4db6bfe0SAlasdair G Kergon /* 566*4db6bfe0SAlasdair G Kergon * Move onto the next free pending, making sure to take 567*4db6bfe0SAlasdair G Kergon * into account the location of the metadata chunks. 568*4db6bfe0SAlasdair G Kergon */ 569*4db6bfe0SAlasdair G Kergon stride = (ps->exceptions_per_area + 1); 570*4db6bfe0SAlasdair G Kergon next_free = ++ps->next_free; 571*4db6bfe0SAlasdair G Kergon if (sector_div(next_free, stride) == 1) 572*4db6bfe0SAlasdair G Kergon ps->next_free++; 573*4db6bfe0SAlasdair G Kergon 574*4db6bfe0SAlasdair G Kergon atomic_inc(&ps->pending_count); 575*4db6bfe0SAlasdair G Kergon return 0; 576*4db6bfe0SAlasdair G Kergon } 577*4db6bfe0SAlasdair G Kergon 578*4db6bfe0SAlasdair G Kergon static void persistent_commit(struct dm_exception_store *store, 579*4db6bfe0SAlasdair G Kergon struct dm_snap_exception *e, 580*4db6bfe0SAlasdair G Kergon void (*callback) (void *, int success), 581*4db6bfe0SAlasdair G Kergon void *callback_context) 582*4db6bfe0SAlasdair G Kergon { 583*4db6bfe0SAlasdair G Kergon unsigned int i; 584*4db6bfe0SAlasdair G Kergon struct pstore *ps = get_info(store); 585*4db6bfe0SAlasdair G Kergon struct disk_exception de; 586*4db6bfe0SAlasdair G Kergon struct commit_callback *cb; 587*4db6bfe0SAlasdair G Kergon 588*4db6bfe0SAlasdair G Kergon de.old_chunk = e->old_chunk; 589*4db6bfe0SAlasdair G Kergon de.new_chunk = e->new_chunk; 590*4db6bfe0SAlasdair G Kergon write_exception(ps, ps->current_committed++, &de); 591*4db6bfe0SAlasdair G Kergon 592*4db6bfe0SAlasdair G Kergon /* 593*4db6bfe0SAlasdair G Kergon * Add the callback to the back of the array. This code 594*4db6bfe0SAlasdair G Kergon * is the only place where the callback array is 595*4db6bfe0SAlasdair G Kergon * manipulated, and we know that it will never be called 596*4db6bfe0SAlasdair G Kergon * multiple times concurrently. 597*4db6bfe0SAlasdair G Kergon */ 598*4db6bfe0SAlasdair G Kergon cb = ps->callbacks + ps->callback_count++; 599*4db6bfe0SAlasdair G Kergon cb->callback = callback; 600*4db6bfe0SAlasdair G Kergon cb->context = callback_context; 601*4db6bfe0SAlasdair G Kergon 602*4db6bfe0SAlasdair G Kergon /* 603*4db6bfe0SAlasdair G Kergon * If there are exceptions in flight and we have not yet 604*4db6bfe0SAlasdair G Kergon * filled this metadata area there's nothing more to do. 605*4db6bfe0SAlasdair G Kergon */ 606*4db6bfe0SAlasdair G Kergon if (!atomic_dec_and_test(&ps->pending_count) && 607*4db6bfe0SAlasdair G Kergon (ps->current_committed != ps->exceptions_per_area)) 608*4db6bfe0SAlasdair G Kergon return; 609*4db6bfe0SAlasdair G Kergon 610*4db6bfe0SAlasdair G Kergon /* 611*4db6bfe0SAlasdair G Kergon * If we completely filled the current area, then wipe the next one. 612*4db6bfe0SAlasdair G Kergon */ 613*4db6bfe0SAlasdair G Kergon if ((ps->current_committed == ps->exceptions_per_area) && 614*4db6bfe0SAlasdair G Kergon zero_disk_area(ps, ps->current_area + 1)) 615*4db6bfe0SAlasdair G Kergon ps->valid = 0; 616*4db6bfe0SAlasdair G Kergon 617*4db6bfe0SAlasdair G Kergon /* 618*4db6bfe0SAlasdair G Kergon * Commit exceptions to disk. 619*4db6bfe0SAlasdair G Kergon */ 620*4db6bfe0SAlasdair G Kergon if (ps->valid && area_io(ps, WRITE)) 621*4db6bfe0SAlasdair G Kergon ps->valid = 0; 622*4db6bfe0SAlasdair G Kergon 623*4db6bfe0SAlasdair G Kergon /* 624*4db6bfe0SAlasdair G Kergon * Advance to the next area if this one is full. 625*4db6bfe0SAlasdair G Kergon */ 626*4db6bfe0SAlasdair G Kergon if (ps->current_committed == ps->exceptions_per_area) { 627*4db6bfe0SAlasdair G Kergon ps->current_committed = 0; 628*4db6bfe0SAlasdair G Kergon ps->current_area++; 629*4db6bfe0SAlasdair G Kergon zero_memory_area(ps); 630*4db6bfe0SAlasdair G Kergon } 631*4db6bfe0SAlasdair G Kergon 632*4db6bfe0SAlasdair G Kergon for (i = 0; i < ps->callback_count; i++) { 633*4db6bfe0SAlasdair G Kergon cb = ps->callbacks + i; 634*4db6bfe0SAlasdair G Kergon cb->callback(cb->context, ps->valid); 635*4db6bfe0SAlasdair G Kergon } 636*4db6bfe0SAlasdair G Kergon 637*4db6bfe0SAlasdair G Kergon ps->callback_count = 0; 638*4db6bfe0SAlasdair G Kergon } 639*4db6bfe0SAlasdair G Kergon 640*4db6bfe0SAlasdair G Kergon static void persistent_drop(struct dm_exception_store *store) 641*4db6bfe0SAlasdair G Kergon { 642*4db6bfe0SAlasdair G Kergon struct pstore *ps = get_info(store); 643*4db6bfe0SAlasdair G Kergon 644*4db6bfe0SAlasdair G Kergon ps->valid = 0; 645*4db6bfe0SAlasdair G Kergon if (write_header(ps)) 646*4db6bfe0SAlasdair G Kergon DMWARN("write header failed"); 647*4db6bfe0SAlasdair G Kergon } 648*4db6bfe0SAlasdair G Kergon 649*4db6bfe0SAlasdair G Kergon int dm_create_persistent(struct dm_exception_store *store) 650*4db6bfe0SAlasdair G Kergon { 651*4db6bfe0SAlasdair G Kergon struct pstore *ps; 652*4db6bfe0SAlasdair G Kergon 653*4db6bfe0SAlasdair G Kergon /* allocate the pstore */ 654*4db6bfe0SAlasdair G Kergon ps = kmalloc(sizeof(*ps), GFP_KERNEL); 655*4db6bfe0SAlasdair G Kergon if (!ps) 656*4db6bfe0SAlasdair G Kergon return -ENOMEM; 657*4db6bfe0SAlasdair G Kergon 658*4db6bfe0SAlasdair G Kergon ps->snap = store->snap; 659*4db6bfe0SAlasdair G Kergon ps->valid = 1; 660*4db6bfe0SAlasdair G Kergon ps->version = SNAPSHOT_DISK_VERSION; 661*4db6bfe0SAlasdair G Kergon ps->area = NULL; 662*4db6bfe0SAlasdair G Kergon ps->next_free = 2; /* skipping the header and first area */ 663*4db6bfe0SAlasdair G Kergon ps->current_committed = 0; 664*4db6bfe0SAlasdair G Kergon 665*4db6bfe0SAlasdair G Kergon ps->callback_count = 0; 666*4db6bfe0SAlasdair G Kergon atomic_set(&ps->pending_count, 0); 667*4db6bfe0SAlasdair G Kergon ps->callbacks = NULL; 668*4db6bfe0SAlasdair G Kergon 669*4db6bfe0SAlasdair G Kergon ps->metadata_wq = create_singlethread_workqueue("ksnaphd"); 670*4db6bfe0SAlasdair G Kergon if (!ps->metadata_wq) { 671*4db6bfe0SAlasdair G Kergon kfree(ps); 672*4db6bfe0SAlasdair G Kergon DMERR("couldn't start header metadata update thread"); 673*4db6bfe0SAlasdair G Kergon return -ENOMEM; 674*4db6bfe0SAlasdair G Kergon } 675*4db6bfe0SAlasdair G Kergon 676*4db6bfe0SAlasdair G Kergon store->destroy = persistent_destroy; 677*4db6bfe0SAlasdair G Kergon store->read_metadata = persistent_read_metadata; 678*4db6bfe0SAlasdair G Kergon store->prepare_exception = persistent_prepare; 679*4db6bfe0SAlasdair G Kergon store->commit_exception = persistent_commit; 680*4db6bfe0SAlasdair G Kergon store->drop_snapshot = persistent_drop; 681*4db6bfe0SAlasdair G Kergon store->fraction_full = persistent_fraction_full; 682*4db6bfe0SAlasdair G Kergon store->context = ps; 683*4db6bfe0SAlasdair G Kergon 684*4db6bfe0SAlasdair G Kergon return 0; 685*4db6bfe0SAlasdair G Kergon } 686*4db6bfe0SAlasdair G Kergon 687*4db6bfe0SAlasdair G Kergon int dm_persistent_snapshot_init(void) 688*4db6bfe0SAlasdair G Kergon { 689*4db6bfe0SAlasdair G Kergon return 0; 690*4db6bfe0SAlasdair G Kergon } 691*4db6bfe0SAlasdair G Kergon 692*4db6bfe0SAlasdair G Kergon void dm_persistent_snapshot_exit(void) 693*4db6bfe0SAlasdair G Kergon { 694*4db6bfe0SAlasdair G Kergon } 695