summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYehuda Sadeh <yehuda@hq.newdream.net>2010-05-17 12:43:40 -0700
committerSage Weil <sage@newdream.net>2010-05-21 16:26:20 -0700
commit3a6e756908487ca0ec1d201c389e823c557de863 (patch)
tree320f2c387dfb2b4ab538e5e03ba18d932761df00
parent3836f9dc0c5bbed09d36826201f442c6e0227ba4 (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.c400
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
};