diff options
author | Yehuda Sadeh <yehuda@hq.newdream.net> | 2010-05-17 12:43:40 -0700 |
---|---|---|
committer | Sage Weil <sage@newdream.net> | 2010-05-21 16:26:20 -0700 |
commit | 3a6e756908487ca0ec1d201c389e823c557de863 (patch) | |
tree | 320f2c387dfb2b4ab538e5e03ba18d932761df00 | |
parent | 3836f9dc0c5bbed09d36826201f442c6e0227ba4 (diff) |
ceph-rbd: snapshots support
This adds snapshots capabilities to the ceph-rbd. The snapshots
can be created per a single rbd-image, and the relevant snapshot
information is being kept in the rbd header.
Signed-off-by: Yehuda Sadeh <yehuda@hq.newdream.net>
Signed-off-by: Sage Weil <sage@newdream.net>
-rw-r--r-- | fs/ceph/rbd.c | 400 |
1 files changed, 321 insertions, 79 deletions
diff --git a/fs/ceph/rbd.c b/fs/ceph/rbd.c index 01e89a0aa855..a745f3f3481c 100644 --- a/fs/ceph/rbd.c +++ b/fs/ceph/rbd.c @@ -62,6 +62,7 @@ #include "super.h" #include "osd_client.h" #include "rbd_types.h" +#include "mon_client.h" #include <linux/kernel.h> #include <linux/device.h> @@ -96,7 +97,11 @@ struct rbd_obj_header { struct rw_semaphore snap_rwsem; struct ceph_snap_context *snapc; size_t snap_names_len; + u32 snap_seq; + u32 total_snaps; + char *snap_names; + u64 *snap_sizes; }; struct rbd_request { @@ -134,6 +139,10 @@ struct rbd_device { char pool_name[RBD_MAX_POOL_NAME_SIZE]; int poolid; + u32 cur_snap; /* index+1 of current snapshot within snap context + 0 - for the head */ + int read_only; + struct list_head node; struct rbd_client_node *client_node; }; @@ -145,8 +154,21 @@ static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ static LIST_HEAD(rbddev_list); static LIST_HEAD(node_list); + +static int rbd_open(struct block_device *bdev, fmode_t mode) +{ + struct gendisk *disk = bdev->bd_disk; + struct rbd_device *rbd_dev = disk->private_data; + + if (mode & FMODE_WRITE && rbd_dev->read_only) + return -EROFS; + + return 0; +} + static const struct block_device_operations rbd_bd_ops = { - .owner = THIS_MODULE, + .owner = THIS_MODULE, + .open = rbd_open, }; /* @@ -289,6 +311,21 @@ static void rbd_put_client(struct rbd_device *rbd_dev) rbd_dev->client_node = NULL; } +static int snap_index(struct rbd_obj_header *header, int snap_num) +{ + return header->total_snaps - snap_num; +} + +static u64 cur_snap_id(struct rbd_device *rbd_dev) +{ + struct rbd_obj_header *header = &rbd_dev->header; + + if (!rbd_dev->cur_snap) + return 0; + + return header->snapc->snaps[snap_index(header, rbd_dev->cur_snap)]; +} + /* * Create a new header structure, translate header format from the on-disk @@ -300,17 +337,31 @@ static int rbd_header_from_disk(struct rbd_obj_header *header, gfp_t gfp_flags) { int i; - u16 snap_count = le16_to_cpu(ondisk->snap_count); + u32 snap_count = le32_to_cpu(ondisk->snap_count); + int ret = -ENOMEM; init_rwsem(&header->snap_rwsem); header->snap_names_len = le64_to_cpu(ondisk->snap_names_len); - header->snapc = kmalloc(sizeof(struct rbd_obj_header) + - header->snap_names_len + - snap_count * sizeof(__u64), + header->snapc = kmalloc(sizeof(struct ceph_snap_context) + + snap_count * + sizeof(struct rbd_obj_snap_ondisk), gfp_flags); if (!header->snapc) return -ENOMEM; + if (snap_count) { + header->snap_names = kmalloc(header->snap_names_len, + GFP_KERNEL); + if (!header->snap_names) + goto err_snapc; + header->snap_sizes = kmalloc(snap_count * sizeof(u64), + GFP_KERNEL); + if (!header->snap_sizes) + goto err_names; + } else { + header->snap_names = NULL; + header->snap_sizes = NULL; + } header->image_size = le64_to_cpu(ondisk->image_size); header->obj_order = ondisk->obj_order; @@ -318,21 +369,31 @@ static int rbd_header_from_disk(struct rbd_obj_header *header, header->comp_type = ondisk->comp_type; atomic_set(&header->snapc->nref, 1); - header->snapc->seq = le64_to_cpu(ondisk->snap_seq); + header->snap_seq = le32_to_cpu(ondisk->snap_seq); header->snapc->num_snaps = snap_count; + header->total_snaps = snap_count; if (snap_count && allocated_snaps == snap_count) { - for (i = 0; i < snap_count; i++) + for (i = 0; i < snap_count; i++) { header->snapc->snaps[i] = - le64_to_cpu(ondisk->snap_id[i]); + le64_to_cpu(ondisk->snaps[i].id); + header->snap_sizes[i] = + le64_to_cpu(ondisk->snaps[i].image_size); + } /* copy snapshot names */ - memcpy(&header->snapc->snaps[i], &ondisk->snap_id[i], + memcpy(header->snap_names, &ondisk->snaps[i], header->snap_names_len); } return 0; + +err_names: + kfree(header->snap_names); +err_snapc: + kfree(header->snapc); + return ret; } /* @@ -344,82 +405,174 @@ static int rbd_header_to_disk(struct rbd_obj_header_ondisk **ondisk, struct rbd_obj_header *header, gfp_t gfp_flags) { - struct ceph_snap_context *snapc = header->snapc; int i; down_read(&header->snap_rwsem); *ondisk = kmalloc(sizeof(struct rbd_obj_header_ondisk) + header->snap_names_len + - snapc->num_snaps * sizeof(__u64), + header->total_snaps * + sizeof(struct rbd_obj_snap_ondisk), gfp_flags); if (!*ondisk) return -ENOMEM; memcpy(*ondisk, old_ondisk, sizeof(*old_ondisk)); - (*ondisk)->snap_seq = cpu_to_le64(snapc->seq); - (*ondisk)->snap_count = cpu_to_le64(snapc->num_snaps); + (*ondisk)->snap_seq = cpu_to_le32(header->snap_seq); + (*ondisk)->snap_count = cpu_to_le32(header->total_snaps); + (*ondisk)->snap_names_len = cpu_to_le64(header->snap_names_len); - if (snapc->num_snaps) { - for (i = 0; i < snapc->num_snaps; i++) - (*ondisk)->snap_id[i] = + if (header->total_snaps) { + for (i = 0; i < header->total_snaps; i++) { + (*ondisk)->snaps[i].id = cpu_to_le64(header->snapc->snaps[i]); + (*ondisk)->snaps[i].image_size = + cpu_to_le64(header->snap_sizes[i]); + } /* copy snapshot names */ - memcpy(&(*ondisk)->snap_id[i], &header->snapc->snaps[i], + memcpy(&(*ondisk)->snaps[i], header->snap_names, header->snap_names_len); } - (*ondisk)->snap_names_len = cpu_to_le64(header->snap_names_len); up_read(&header->snap_rwsem); return 0; } -static int rbd_header_add_snap(struct rbd_obj_header *header, +static int rbd_header_add_snap(struct rbd_device *dev, const char *snap_name, gfp_t gfp_flags) { - struct ceph_snap_context *snapc = header->snapc; + struct rbd_obj_header *header = &dev->header; struct ceph_snap_context *new_snapc; - int ret = -ENOMEM; - char *src_names, *dst_names, *p; + char *p; int name_len = strlen(snap_name); u64 *snaps = header->snapc->snaps; + u64 *new_sizes; + char *new_names; + u64 new_snapid; int i; + int ret = -EINVAL; + + down_write(&header->snap_rwsem); - src_names = (char *)&snaps[snapc->num_snaps]; + /* we can create a snapshot only if we're pointing at the head */ + if (dev->cur_snap) + goto done; - p = src_names; - for (i = 0; i < snapc->num_snaps; i++, p += strlen(p) + 1) { + ret = -EEXIST; + p = header->snap_names; + for (i = 0; i < header->total_snaps; i++, p += strlen(p) + 1) { if (strcmp(snap_name, p) == 0) - return -EEXIST; + goto done; } - down_write(&header->snap_rwsem); + ret = -ENOMEM; new_snapc = kmalloc(sizeof(struct rbd_obj_header) + - (snapc->num_snaps + 1) * sizeof(u64) + - header->snap_names_len + name_len + 1, + (header->total_snaps + 1) * sizeof(u64), gfp_flags); if (!new_snapc) goto done; + new_names = kmalloc(header->snap_names_len + name_len + 1, gfp_flags); + if (!new_names) + goto err_snapc; + new_sizes = kmalloc((header->total_snaps + 1) * sizeof(u64), + gfp_flags); + if (!new_sizes) + goto err_names; atomic_set(&new_snapc->nref, 1); - new_snapc->seq = snapc->seq + 1; - new_snapc->num_snaps = snapc->num_snaps + 1; - memcpy(new_snapc->snaps, snaps, snapc->num_snaps * sizeof(u64)); - new_snapc->snaps[new_snapc->num_snaps - 1] = new_snapc->seq; + new_snapc->num_snaps = header->total_snaps + 1; + if (header->total_snaps) + memcpy(&new_snapc->snaps[1], snaps, + (header->total_snaps) * sizeof(u64)); + + ret = ceph_monc_create_snapid(&dev->client->monc, dev->poolid, + &new_snapid); + dout("created snapid=%lld\n", new_snapid); + if (ret < 0) + goto err_sizes; - /* copy snap names */ - dst_names = (char *)&new_snapc->snaps[new_snapc->num_snaps]; + new_snapc->seq = new_snapid; /* we're still pointing at the head */ + header->snap_seq = new_snapid; + new_snapc->snaps[0] = new_snapid; - memcpy(dst_names, src_names, header->snap_names_len); - dst_names += header->snap_names_len; - memcpy(dst_names, snap_name, name_len + 1); + /* copy snap names */ + if (header->snap_names) + memcpy(new_names + name_len + 1, header->snap_names, + header->snap_names_len); + memcpy(new_names, snap_name, name_len + 1); header->snap_names_len += name_len + 1; + /* copy snap image sizes */ + if (header->snap_sizes) + memcpy(new_sizes, header->snap_sizes, + header->total_snaps * sizeof(u64)); + new_sizes[new_snapc->num_snaps - 1] = header->image_size; + + header->total_snaps = new_snapc->num_snaps; + kfree(header->snapc); header->snapc = new_snapc; + kfree(header->snap_names); + header->snap_names = new_names; + kfree(header->snap_sizes); + header->snap_sizes = new_sizes; + + ret = 0; +done: + up_write(&header->snap_rwsem); + return ret; +err_sizes: + kfree(new_sizes); +err_names: + kfree(new_names); +err_snapc: + kfree(new_snapc); + up_write(&header->snap_rwsem); + return ret; +} + +static int rbd_header_set_snap(struct rbd_device *dev, + const char *snap_name, + u64 *size) +{ + struct rbd_obj_header *header = &dev->header; + struct ceph_snap_context *snapc = header->snapc; + char *p; + int i; + int ret = -ENOENT; + + down_write(&header->snap_rwsem); + + if (!snap_name || + !*snap_name || + strcmp(snap_name, "-") == 0 || + strcmp(snap_name, RBD_SNAP_HEAD_NAME) == 0) { + if (header->total_snaps) + snapc->seq = header->snap_seq; + else + snapc->seq = 0; + dev->cur_snap = 0; + dev->read_only = 0; + if (size) + *size = header->image_size; + } else { + p = header->snap_names; + for (i = 0; i < header->total_snaps; i++, p += strlen(p) + 1) { + if (strcmp(snap_name, p) == 0) + break; + } + if (i == header->total_snaps) + goto done; + + snapc->seq = snapc->snaps[i]; + dev->cur_snap = header->total_snaps - i; + dev->read_only = 1; + if (size) + *size = header->snap_sizes[i]; + } ret = 0; done: @@ -430,6 +583,8 @@ done: static void rbd_header_free(struct rbd_obj_header *header) { kfree(header->snapc); + kfree(header->snap_names); + kfree(header->snap_sizes); } /* @@ -577,6 +732,7 @@ err_out: static int rbd_do_request(struct request *rq, struct rbd_device *dev, struct ceph_snap_context *snapc, + u64 snapid, const char *obj, u64 ofs, u64 len, struct bio *bio, struct page **pages, @@ -636,7 +792,8 @@ static int rbd_do_request(struct request *rq, layout->fl_object_size = RBD_STRIPE_UNIT; layout->fl_pg_preferred = -1; layout->fl_pg_pool = dev->poolid; - ceph_calc_raw_layout(&dev->client->osdc, layout, ofs, len, &bno, req); + ceph_calc_raw_layout(&dev->client->osdc, layout, snapid, + ofs, len, &bno, req); ceph_osdc_build_request(req, ofs, &len, opcode, snapc, 0, @@ -711,6 +868,7 @@ static void rbd_req_cb(struct ceph_osd_request *req, struct ceph_msg *msg) */ static int rbd_req_sync_op(struct rbd_device *dev, struct ceph_snap_context *snapc, + u64 snapid, int opcode, int flags, int num_reply, const char *obj, @@ -732,7 +890,8 @@ static int rbd_req_sync_op(struct rbd_device *dev, goto done; } - ret = rbd_do_request(NULL, dev, snapc, obj, ofs, len, NULL, + ret = rbd_do_request(NULL, dev, snapc, snapid, + obj, ofs, len, NULL, pages, num_pages, opcode, flags, @@ -742,7 +901,7 @@ static int rbd_req_sync_op(struct rbd_device *dev, goto done; if (flags & CEPH_OSD_FLAG_READ) - ret = ceph_copy_from_page_vector(pages, buf, ofs, len); + ret = ceph_copy_from_page_vector(pages, buf, ofs, ret); done: ceph_release_page_vector(pages, num_pages); @@ -755,6 +914,7 @@ done: static int rbd_do_op(struct request *rq, struct rbd_device *rbd_dev , struct ceph_snap_context *snapc, + u64 snapid, int opcode, int flags, int num_reply, u64 ofs, u64 len, struct bio *bio) @@ -780,7 +940,7 @@ static int rbd_do_op(struct request *rq, truncated at this point */ BUG_ON(seg_len < len); - ret = rbd_do_request(rq, rbd_dev, snapc, + ret = rbd_do_request(rq, rbd_dev, snapc, snapid, seg_name, seg_ofs, seg_len, bio, NULL, 0, @@ -801,7 +961,7 @@ static int rbd_req_write(struct request *rq, u64 ofs, u64 len, struct bio *bio) { - return rbd_do_op(rq, rbd_dev, snapc, + return rbd_do_op(rq, rbd_dev, snapc, CEPH_NOSNAP, CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, 2, @@ -813,11 +973,12 @@ static int rbd_req_write(struct request *rq, */ static int rbd_req_sync_write(struct rbd_device *dev, struct ceph_snap_context *snapc, + u64 snapid, const char *obj, u64 ofs, u64 len, char *buf) { - return rbd_req_sync_op(dev, snapc, + return rbd_req_sync_op(dev, snapc, snapid, CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, 2, obj, ofs, len, buf); @@ -828,11 +989,12 @@ static int rbd_req_sync_write(struct rbd_device *dev, */ static int rbd_req_read(struct request *rq, struct rbd_device *rbd_dev, - struct ceph_snap_context *snapc, + u64 snapid, u64 ofs, u64 len, struct bio *bio) { - return rbd_do_op(rq, rbd_dev, snapc, + return rbd_do_op(rq, rbd_dev, NULL, + (snapid ? snapid : CEPH_NOSNAP), CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, 2, @@ -844,11 +1006,13 @@ static int rbd_req_read(struct request *rq, */ static int rbd_req_sync_read(struct rbd_device *dev, struct ceph_snap_context *snapc, + u64 snapid, const char *obj, u64 ofs, u64 len, char *buf) { - return rbd_req_sync_op(dev, snapc, + return rbd_req_sync_op(dev, NULL, + (snapid ? snapid : CEPH_NOSNAP), CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, 1, obj, ofs, len, buf); @@ -924,7 +1088,7 @@ static void rbd_rq_fn(struct request_queue *q) op_size, bio); else rbd_req_read(rq, rbd_dev, - rbd_dev->header.snapc, + cur_snap_id(rbd_dev), ofs, op_size, bio); @@ -1006,7 +1170,8 @@ static int rbd_read_header(struct rbd_device *rbd_dev, return -ENOMEM; while (1) { - int len = sizeof(*dh) + snap_count * sizeof(u64) + + int len = sizeof(*dh) + + snap_count * sizeof(struct rbd_obj_snap_ondisk) + snap_names_len; rc = -ENOMEM; @@ -1015,7 +1180,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev, goto out_obj_md; rc = rbd_req_sync_read(rbd_dev, - NULL, + NULL, CEPH_NOSNAP, obj_md_name, 0, len, (char *)dh); @@ -1026,8 +1191,8 @@ static int rbd_read_header(struct rbd_device *rbd_dev, if (rc < 0) goto out_dh; - if (snap_count != header->snapc->num_snaps) { - snap_count = header->snapc->num_snaps; + if (snap_count != header->total_snaps) { + snap_count = header->total_snaps; snap_names_len = header->snap_names_len; rbd_header_free(header); kfree(dh); @@ -1043,28 +1208,31 @@ out_obj_md: return rc; } -static int rbd_read_ondisk_header(struct rbd_device *rbd_dev, +/* + * only read the first part of the ondisk header, without the snaps info + */ +static int rbd_read_ondisk_header_nosnap(struct rbd_device *rbd_dev, struct rbd_obj_header *header, struct rbd_obj_header_ondisk *dh) { ssize_t rc; char *obj_md_name; - int snap_count = header->snapc->num_snaps; - u64 snap_names_len = header->snap_names_len; int len; obj_md_name = rbd_alloc_md_name(rbd_dev, GFP_KERNEL); if (!obj_md_name) return -ENOMEM; - len = sizeof(*dh) + snap_count * sizeof(u64) + - snap_names_len; + len = sizeof(struct rbd_obj_header_ondisk); rc = rbd_req_sync_read(rbd_dev, - NULL, + NULL, CEPH_NOSNAP, obj_md_name, 0, len, (char *)dh); + if (rc > 0 && rc < len) + rc = -EIO; + kfree(obj_md_name); return rc; } @@ -1075,7 +1243,7 @@ static int rbd_write_header(struct rbd_device *rbd_dev, { ssize_t rc; char *obj_md_name; - int snap_count = header->snapc->num_snaps; + int snap_count = header->total_snaps; u64 snap_names_len = header->snap_names_len; int len; @@ -1083,10 +1251,12 @@ static int rbd_write_header(struct rbd_device *rbd_dev, if (!obj_md_name) return -ENOMEM; - len = sizeof(*dh) + snap_count * sizeof(u64) + - snap_names_len; + len = sizeof(*dh) + + snap_count * sizeof(struct rbd_obj_snap_ondisk) + + snap_names_len; - rc = rbd_req_sync_write(rbd_dev, NULL, + rc = rbd_req_sync_write(rbd_dev, + NULL, CEPH_NOSNAP, obj_md_name, 0, len, (char *)dh); @@ -1104,8 +1274,16 @@ static int rbd_update_snaps(struct rbd_device *rbd_dev) return ret; down_write(&rbd_dev->header.snap_rwsem); + kfree(rbd_dev->header.snapc); + kfree(rbd_dev->header.snap_names); + kfree(rbd_dev->header.snap_sizes); + + rbd_dev->header.total_snaps = h.total_snaps; rbd_dev->header.snapc = h.snapc; + rbd_dev->header.snap_names = h.snap_names; + rbd_dev->header.snap_sizes = h.snap_sizes; + up_write(&rbd_dev->header.snap_rwsem); return 0; @@ -1117,13 +1295,18 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) struct request_queue *q; int rc; u64 total_size; + const char *snap = NULL; /* contact OSD, request size info about the object being mapped */ rc = rbd_read_header(rbd_dev, &rbd_dev->header); if (rc) return rc; - total_size = rbd_dev->header.image_size; + if (rbd_dev->client->mount_args) + snap = rbd_dev->client->mount_args->snap; + rc = rbd_header_set_snap(rbd_dev, snap, &total_size); + if (rc) + return rc; /* create gendisk info */ rc = -ENOMEM; @@ -1189,9 +1372,10 @@ static ssize_t class_rbd_list(struct class *c, struct rbd_device *rbd_dev; rbd_dev = list_entry(tmp, struct rbd_device, node); - n += sprintf(data+n, "%d %d %s %s\n", + n += sprintf(data+n, "%d %d client%lld %s %s\n", rbd_dev->id, rbd_dev->major, + ceph_client_id(rbd_dev->client), rbd_dev->pool_name, rbd_dev->obj); } @@ -1267,7 +1451,6 @@ static ssize_t class_rbd_add(struct class *c, goto err_out_slot; mutex_unlock(&ctl_mutex); - /* register our block device */ irc = register_blkdev(0, rbd_dev->name); if (irc < 0) { @@ -1362,6 +1545,23 @@ static ssize_t class_rbd_remove(struct class *c, return count; } +static void get_size_and_suffix(u64 orig_size, u64 *size, char *suffix) +{ + if (orig_size >= 1024*1024*1024) { + *size = orig_size / (1024*1024*1024); + *suffix = 'G'; + } else if (orig_size >= 1024*1024) { + *size = orig_size / (1024*1024); + *suffix = 'M'; + } else if (orig_size >= 1024) { + *size = orig_size / 1024; + *suffix = 'K'; + } else { + *size = orig_size; + *suffix = ' '; + } +} + static ssize_t class_rbd_snaps_list(struct class *c, struct class_attribute *attr, char *data) @@ -1369,6 +1569,8 @@ static ssize_t class_rbd_snaps_list(struct class *c, struct rbd_device *rbd_dev = NULL; struct list_head *tmp; struct rbd_obj_header *header; + char size_suffix; + u64 size; int i, n = 0, max = PAGE_SIZE; int ret; @@ -1376,26 +1578,47 @@ static ssize_t class_rbd_snaps_list(struct class *c, list_for_each(tmp, &rbddev_list) { char *names, *p; + struct ceph_snap_context *snapc; + rbd_dev = list_entry(tmp, struct rbd_device, node); header = &rbd_dev->header; - names = - (char *)&header->snapc->snaps[header->snapc->num_snaps]; - n += snprintf(data, max - n, "snapshots for device id %d:\n", + names = header->snap_names; + snapc = header->snapc; + n += snprintf(data + n, max - n, + "snapshots for device id %d:\n", rbd_dev->id); if (n == max) break; down_read(&header->snap_rwsem); + + get_size_and_suffix(header->image_size, &size, + &size_suffix); + n += snprintf(data + n, max - n, "%s\t%lld%c%s\n", + RBD_SNAP_HEAD_NAME, + size, size_suffix, + (!rbd_dev->cur_snap ? + " (*)" : "")); + if (n == max) + break; + p = names; - for (i = 0; i < header->snapc->num_snaps; - i++, p += strlen(p) + 1) { - n += snprintf(data+n, max - n, "%s\n", p); + for (i = 0; i < header->total_snaps; i++, p += strlen(p) + 1) { + get_size_and_suffix(header->snap_sizes[i], &size, + &size_suffix); + n += snprintf(data + n, max - n, "%s\t%lld%c%s\n", + p, size, size_suffix, + (rbd_dev->cur_snap && + (snap_index(header, i) == rbd_dev->cur_snap) ? + " (*)" : "")); if (n == max) break; } + up_read(&header->snap_rwsem); } + ret = n; mutex_unlock(&ctl_mutex); return ret; @@ -1437,10 +1660,11 @@ done: return ret; } -static ssize_t class_rbd_snaps_add(struct class *c, +static ssize_t class_rbd_snaps_op(struct class *c, struct class_attribute *attr, const char *buf, - size_t count) + size_t count, + int snaps_op) { struct rbd_device *rbd_dev = NULL; int target_id, ret; @@ -1468,14 +1692,23 @@ static ssize_t class_rbd_snaps_add(struct class *c, goto done_unlock; } - ret = rbd_read_ondisk_header(rbd_dev, - &rbd_dev->header, - &old_ondisk); + ret = rbd_read_ondisk_header_nosnap(rbd_dev, + &rbd_dev->header, + &old_ondisk); if (ret < 0) goto done_unlock; - ret = rbd_header_add_snap(&rbd_dev->header, - name, GFP_KERNEL); + switch (snaps_op) { + case RBD_SNAP_OP_CREATE: + ret = rbd_header_add_snap(rbd_dev, + name, GFP_KERNEL); + break; + case RBD_SNAP_OP_SET: + ret = rbd_header_set_snap(rbd_dev, name, NULL); + break; + default: + ret = -EINVAL; + } if (ret < 0) goto done_unlock; @@ -1496,12 +1729,21 @@ done: return ret; } +static ssize_t class_rbd_snap_create(struct class *c, + struct class_attribute *attr, + const char *buf, + size_t count) +{ + return class_rbd_snaps_op(c, attr, buf, count, + RBD_SNAP_OP_CREATE); +} + static struct class_attribute class_rbd_attrs[] = { __ATTR(add, 0200, NULL, class_rbd_add), __ATTR(remove, 0200, NULL, class_rbd_remove), __ATTR(list, 0444, class_rbd_list, NULL), __ATTR(snaps_refresh, 0200, NULL, class_rbd_snaps_refresh), - __ATTR(snaps_add, 0200, NULL, class_rbd_snaps_add), + __ATTR(snap_create, 0200, NULL, class_rbd_snap_create), __ATTR(snaps_list, 0444, class_rbd_snaps_list, NULL), __ATTR_NULL }; |