+
/*
rbd.c -- Export ceph rados objects as a Linux block device
#include <linux/ceph/mon_client.h>
#include <linux/ceph/decode.h>
#include <linux/parser.h>
+#include <linux/bsearch.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
+#include <linux/slab.h>
#include "rbd_types.h"
#define RBD_SNAP_HEAD_NAME "-"
+#define BAD_SNAP_INDEX U32_MAX /* invalid index into snap array */
+
/* This allows a single page to hold an image name sent by OSD */
#define RBD_IMAGE_NAME_LEN_MAX (PAGE_SIZE - sizeof (__le32) - 1)
#define RBD_IMAGE_ID_LEN_MAX 64
u64 stripe_unit;
u64 stripe_count;
-
- u64 obj_version;
};
/*
struct ceph_osd_request *osd_req;
u64 xferred; /* bytes transferred */
- u64 version;
int result;
rbd_obj_callback_t callback;
#define for_each_obj_request_safe(ireq, oreq, n) \
list_for_each_entry_safe_reverse(oreq, n, &(ireq)->obj_requests, links)
-struct rbd_snap {
- const char *name;
- u64 size;
- struct list_head node;
- u64 id;
- u64 features;
-};
-
struct rbd_mapping {
u64 size;
u64 features;
struct list_head node;
- /* list of snapshots */
- struct list_head snaps;
-
/* sysfs related */
struct device dev;
unsigned long open_count; /* protected by lock */
static LIST_HEAD(rbd_client_list); /* clients */
static DEFINE_SPINLOCK(rbd_client_list_lock);
-static int rbd_img_request_submit(struct rbd_img_request *img_request);
+/* Slab caches for frequently-allocated structures */
+
+static struct kmem_cache *rbd_img_request_cache;
+static struct kmem_cache *rbd_obj_request_cache;
+static struct kmem_cache *rbd_segment_name_cache;
-static int rbd_dev_snaps_update(struct rbd_device *rbd_dev);
+static int rbd_img_request_submit(struct rbd_img_request *img_request);
-static void rbd_dev_release(struct device *dev);
-static void rbd_snap_destroy(struct rbd_snap *snap);
+static void rbd_dev_device_release(struct device *dev);
static ssize_t rbd_add(struct bus_type *bus, const char *buf,
size_t count);
static void rbd_img_parent_read(struct rbd_obj_request *obj_request);
static void rbd_dev_remove_parent(struct rbd_device *rbd_dev);
-static int rbd_dev_refresh(struct rbd_device *rbd_dev, u64 *hver);
-static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev, u64 *hver);
+static int rbd_dev_refresh(struct rbd_device *rbd_dev);
+static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev);
+static const char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev,
+ u64 snap_id);
+static int _rbd_dev_v2_snap_size(struct rbd_device *rbd_dev, u64 snap_id,
+ u8 *order, u64 *snap_size);
+static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
+ u64 *snap_features);
+static u64 rbd_snap_id_by_name(struct rbd_device *rbd_dev, const char *name);
static int rbd_open(struct block_device *bdev, fmode_t mode)
{
kfree(rbdc);
}
-/* Caller has to fill in snapc->seq and snapc->snaps[0..snap_count-1] */
-
-static struct ceph_snap_context *rbd_snap_context_create(u32 snap_count)
-{
- struct ceph_snap_context *snapc;
- size_t size;
-
- size = sizeof (struct ceph_snap_context);
- size += snap_count * sizeof (snapc->snaps[0]);
- snapc = kzalloc(size, GFP_KERNEL);
- if (!snapc)
- return NULL;
-
- atomic_set(&snapc->nref, 1);
- snapc->num_snaps = snap_count;
-
- return snapc;
-}
-
-static inline void rbd_snap_context_get(struct ceph_snap_context *snapc)
-{
- (void)ceph_get_snap_context(snapc);
-}
-
-static inline void rbd_snap_context_put(struct ceph_snap_context *snapc)
-{
- ceph_put_snap_context(snapc);
-}
-
/*
* Drop reference to ceph client node. If it's not referenced anymore, release
* it.
header->image_size = le64_to_cpu(ondisk->image_size);
- header->snapc = rbd_snap_context_create(snap_count);
+ header->snapc = ceph_create_snap_context(snap_count, GFP_KERNEL);
if (!header->snapc)
goto out_err;
header->snapc->seq = le64_to_cpu(ondisk->snap_seq);
return -ENOMEM;
}
-static const char *rbd_snap_name(struct rbd_device *rbd_dev, u64 snap_id)
+static const char *_rbd_dev_v1_snap_name(struct rbd_device *rbd_dev, u32 which)
+{
+ const char *snap_name;
+
+ rbd_assert(which < rbd_dev->header.snapc->num_snaps);
+
+ /* Skip over names until we find the one we are looking for */
+
+ snap_name = rbd_dev->header.snap_names;
+ while (which--)
+ snap_name += strlen(snap_name) + 1;
+
+ return kstrdup(snap_name, GFP_KERNEL);
+}
+
+/*
+ * Snapshot id comparison function for use with qsort()/bsearch().
+ * Note that result is for snapshots in *descending* order.
+ */
+static int snapid_compare_reverse(const void *s1, const void *s2)
{
- struct rbd_snap *snap;
+ u64 snap_id1 = *(u64 *)s1;
+ u64 snap_id2 = *(u64 *)s2;
+
+ if (snap_id1 < snap_id2)
+ return 1;
+ return snap_id1 == snap_id2 ? 0 : -1;
+}
+/*
+ * Search a snapshot context to see if the given snapshot id is
+ * present.
+ *
+ * Returns the position of the snapshot id in the array if it's found,
+ * or BAD_SNAP_INDEX otherwise.
+ *
+ * Note: The snapshot array is in kept sorted (by the osd) in
+ * reverse order, highest snapshot id first.
+ */
+static u32 rbd_dev_snap_index(struct rbd_device *rbd_dev, u64 snap_id)
+{
+ struct ceph_snap_context *snapc = rbd_dev->header.snapc;
+ u64 *found;
+
+ found = bsearch(&snap_id, &snapc->snaps, snapc->num_snaps,
+ sizeof (snap_id), snapid_compare_reverse);
+
+ return found ? (u32)(found - &snapc->snaps[0]) : BAD_SNAP_INDEX;
+}
+
+static const char *rbd_dev_v1_snap_name(struct rbd_device *rbd_dev,
+ u64 snap_id)
+{
+ u32 which;
+
+ which = rbd_dev_snap_index(rbd_dev, snap_id);
+ if (which == BAD_SNAP_INDEX)
+ return NULL;
+
+ return _rbd_dev_v1_snap_name(rbd_dev, which);
+}
+
+static const char *rbd_snap_name(struct rbd_device *rbd_dev, u64 snap_id)
+{
if (snap_id == CEPH_NOSNAP)
return RBD_SNAP_HEAD_NAME;
- list_for_each_entry(snap, &rbd_dev->snaps, node)
- if (snap_id == snap->id)
- return snap->name;
+ rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
+ if (rbd_dev->image_format == 1)
+ return rbd_dev_v1_snap_name(rbd_dev, snap_id);
- return NULL;
+ return rbd_dev_v2_snap_name(rbd_dev, snap_id);
}
-static struct rbd_snap *snap_by_name(struct rbd_device *rbd_dev,
- const char *snap_name)
+static int rbd_snap_size(struct rbd_device *rbd_dev, u64 snap_id,
+ u64 *snap_size)
{
- struct rbd_snap *snap;
+ rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
+ if (snap_id == CEPH_NOSNAP) {
+ *snap_size = rbd_dev->header.image_size;
+ } else if (rbd_dev->image_format == 1) {
+ u32 which;
- list_for_each_entry(snap, &rbd_dev->snaps, node)
- if (!strcmp(snap_name, snap->name))
- return snap;
+ which = rbd_dev_snap_index(rbd_dev, snap_id);
+ if (which == BAD_SNAP_INDEX)
+ return -ENOENT;
- return NULL;
+ *snap_size = rbd_dev->header.snap_sizes[which];
+ } else {
+ u64 size = 0;
+ int ret;
+
+ ret = _rbd_dev_v2_snap_size(rbd_dev, snap_id, NULL, &size);
+ if (ret)
+ return ret;
+
+ *snap_size = size;
+ }
+ return 0;
}
-static int rbd_dev_mapping_set(struct rbd_device *rbd_dev)
+static int rbd_snap_features(struct rbd_device *rbd_dev, u64 snap_id,
+ u64 *snap_features)
{
- if (!memcmp(rbd_dev->spec->snap_name, RBD_SNAP_HEAD_NAME,
- sizeof (RBD_SNAP_HEAD_NAME))) {
- rbd_dev->mapping.size = rbd_dev->header.image_size;
- rbd_dev->mapping.features = rbd_dev->header.features;
+ rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
+ if (snap_id == CEPH_NOSNAP) {
+ *snap_features = rbd_dev->header.features;
+ } else if (rbd_dev->image_format == 1) {
+ *snap_features = 0; /* No features for format 1 */
} else {
- struct rbd_snap *snap;
+ u64 features = 0;
+ int ret;
+
+ ret = _rbd_dev_v2_snap_features(rbd_dev, snap_id, &features);
+ if (ret)
+ return ret;
- snap = snap_by_name(rbd_dev, rbd_dev->spec->snap_name);
- if (!snap)
+ *snap_features = features;
+ }
+ return 0;
+}
+
+static int rbd_dev_mapping_set(struct rbd_device *rbd_dev)
+{
+ const char *snap_name = rbd_dev->spec->snap_name;
+ u64 snap_id;
+ u64 size = 0;
+ u64 features = 0;
+ int ret;
+
+ if (strcmp(snap_name, RBD_SNAP_HEAD_NAME)) {
+ snap_id = rbd_snap_id_by_name(rbd_dev, snap_name);
+ if (snap_id == CEPH_NOSNAP)
return -ENOENT;
- rbd_dev->mapping.size = snap->size;
- rbd_dev->mapping.features = snap->features;
- rbd_dev->mapping.read_only = true;
+ } else {
+ snap_id = CEPH_NOSNAP;
}
+ ret = rbd_snap_size(rbd_dev, snap_id, &size);
+ if (ret)
+ return ret;
+ ret = rbd_snap_features(rbd_dev, snap_id, &features);
+ if (ret)
+ return ret;
+
+ rbd_dev->mapping.size = size;
+ rbd_dev->mapping.features = features;
+
+ /* If we are mapping a snapshot it must be marked read-only */
+
+ if (snap_id != CEPH_NOSNAP)
+ rbd_dev->mapping.read_only = true;
+
return 0;
}
rbd_dev->mapping.read_only = true;
}
-static void rbd_header_free(struct rbd_image_header *header)
+static void rbd_dev_clear_mapping(struct rbd_device *rbd_dev)
{
- kfree(header->object_prefix);
- header->object_prefix = NULL;
- kfree(header->snap_sizes);
- header->snap_sizes = NULL;
- kfree(header->snap_names);
- header->snap_names = NULL;
- rbd_snap_context_put(header->snapc);
- header->snapc = NULL;
+ rbd_dev->mapping.size = 0;
+ rbd_dev->mapping.features = 0;
+ rbd_dev->mapping.read_only = true;
}
static const char *rbd_segment_name(struct rbd_device *rbd_dev, u64 offset)
u64 segment;
int ret;
- name = kmalloc(MAX_OBJ_NAME_SIZE + 1, GFP_NOIO);
+ name = kmem_cache_alloc(rbd_segment_name_cache, GFP_NOIO);
if (!name)
return NULL;
segment = offset >> rbd_dev->header.obj_order;
return name;
}
+static void rbd_segment_name_free(const char *name)
+{
+ /* The explicit cast here is needed to drop the const qualifier */
+
+ kmem_cache_free(rbd_segment_name_cache, (void *)name);
+}
+
static u64 rbd_segment_offset(struct rbd_device *rbd_dev, u64 offset)
{
u64 segment_size = (u64) 1 << rbd_dev->header.obj_order;
if (osd_req->r_result < 0)
obj_request->result = osd_req->r_result;
- obj_request->version = le64_to_cpu(osd_req->r_reassert_version.version);
BUG_ON(osd_req->r_num_ops > 2);
rbd_assert(obj_request_type_valid(type));
size = strlen(object_name) + 1;
- obj_request = kzalloc(sizeof (*obj_request) + size, GFP_KERNEL);
- if (!obj_request)
+ name = kmalloc(size, GFP_KERNEL);
+ if (!name)
+ return NULL;
+
+ obj_request = kmem_cache_zalloc(rbd_obj_request_cache, GFP_KERNEL);
+ if (!obj_request) {
+ kfree(name);
return NULL;
+ }
- name = (char *)(obj_request + 1);
obj_request->object_name = memcpy(name, object_name, size);
obj_request->offset = offset;
obj_request->length = length;
break;
}
- kfree(obj_request);
+ kfree(obj_request->object_name);
+ obj_request->object_name = NULL;
+ kmem_cache_free(rbd_obj_request_cache, obj_request);
}
/*
{
struct rbd_img_request *img_request;
- img_request = kmalloc(sizeof (*img_request), GFP_ATOMIC);
+ img_request = kmem_cache_alloc(rbd_img_request_cache, GFP_ATOMIC);
if (!img_request)
return NULL;
if (write_request) {
down_read(&rbd_dev->header_rwsem);
- rbd_snap_context_get(rbd_dev->header.snapc);
+ ceph_get_snap_context(rbd_dev->header.snapc);
up_read(&rbd_dev->header_rwsem);
}
rbd_assert(img_request->obj_request_count == 0);
if (img_request_write_test(img_request))
- rbd_snap_context_put(img_request->snapc);
+ ceph_put_snap_context(img_request->snapc);
if (img_request_child_test(img_request))
rbd_obj_request_put(img_request->obj_request);
- kfree(img_request);
+ kmem_cache_free(rbd_img_request_cache, img_request);
}
static bool rbd_img_obj_end_request(struct rbd_obj_request *obj_request)
length = rbd_segment_length(rbd_dev, img_offset, resid);
obj_request = rbd_obj_request_create(object_name,
offset, length, type);
- kfree(object_name); /* object request has its own copy */
+ /* object request has its own copy of the object name */
+ rbd_segment_name_free(object_name);
if (!obj_request)
goto out_unwind;
obj_request->xferred = img_request->xferred;
}
out:
+ rbd_img_request_put(img_request);
rbd_img_obj_request_read_callback(obj_request);
rbd_obj_request_complete(obj_request);
}
obj_request_done_set(obj_request);
}
-static int rbd_obj_notify_ack(struct rbd_device *rbd_dev,
- u64 ver, u64 notify_id)
+static int rbd_obj_notify_ack(struct rbd_device *rbd_dev, u64 notify_id)
{
struct rbd_obj_request *obj_request;
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
obj_request->callback = rbd_obj_request_put;
osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_NOTIFY_ACK,
- notify_id, ver, 0);
+ notify_id, 0, 0);
rbd_osd_req_format_read(obj_request);
ret = rbd_obj_request_submit(osdc, obj_request);
static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
{
struct rbd_device *rbd_dev = (struct rbd_device *)data;
- u64 hver;
+ int ret;
if (!rbd_dev)
return;
dout("%s: \"%s\" notify_id %llu opcode %u\n", __func__,
- rbd_dev->header_name, (unsigned long long) notify_id,
- (unsigned int) opcode);
- (void)rbd_dev_refresh(rbd_dev, &hver);
+ rbd_dev->header_name, (unsigned long long)notify_id,
+ (unsigned int)opcode);
+ ret = rbd_dev_refresh(rbd_dev);
+ if (ret)
+ rbd_warn(rbd_dev, ": header refresh error (%d)\n", ret);
- rbd_obj_notify_ack(rbd_dev, hver, notify_id);
+ rbd_obj_notify_ack(rbd_dev, notify_id);
}
/*
rbd_dev->watch_request->osd_req);
osd_req_op_watch_init(obj_request->osd_req, 0, CEPH_OSD_OP_WATCH,
- rbd_dev->watch_event->cookie,
- rbd_dev->header.obj_version, start);
+ rbd_dev->watch_event->cookie, 0, start);
rbd_osd_req_format_write(obj_request);
ret = rbd_obj_request_submit(osdc, obj_request);
const void *outbound,
size_t outbound_size,
void *inbound,
- size_t inbound_size,
- u64 *version)
+ size_t inbound_size)
{
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
struct rbd_obj_request *obj_request;
rbd_assert(obj_request->xferred < (u64)INT_MAX);
ret = (int)obj_request->xferred;
ceph_copy_from_page_vector(pages, inbound, 0, obj_request->xferred);
- if (version)
- *version = obj_request->version;
out:
if (obj_request)
rbd_obj_request_put(obj_request);
goto end_request; /* Shouldn't happen */
}
+ result = -EIO;
+ if (offset + length > rbd_dev->mapping.size) {
+ rbd_warn(rbd_dev, "beyond EOD (%llu~%llu > %llu)\n",
+ offset, length, rbd_dev->mapping.size);
+ goto end_request;
+ }
+
result = -ENOMEM;
img_request = rbd_img_request_create(rbd_dev, offset, length,
write_request, false);
static int rbd_obj_read_sync(struct rbd_device *rbd_dev,
const char *object_name,
- u64 offset, u64 length,
- void *buf, u64 *version)
+ u64 offset, u64 length, void *buf)
{
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
rbd_assert(obj_request->xferred <= (u64) SIZE_MAX);
size = (size_t) obj_request->xferred;
ceph_copy_from_page_vector(pages, buf, 0, size);
- rbd_assert(size <= (size_t) INT_MAX);
- ret = (int) size;
- if (version)
- *version = obj_request->version;
+ rbd_assert(size <= (size_t)INT_MAX);
+ ret = (int)size;
out:
if (obj_request)
rbd_obj_request_put(obj_request);
* Returns a pointer-coded errno if a failure occurs.
*/
static struct rbd_image_header_ondisk *
-rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version)
+rbd_dev_v1_header_read(struct rbd_device *rbd_dev)
{
struct rbd_image_header_ondisk *ondisk = NULL;
u32 snap_count = 0;
return ERR_PTR(-ENOMEM);
ret = rbd_obj_read_sync(rbd_dev, rbd_dev->header_name,
- 0, size, ondisk, version);
+ 0, size, ondisk);
if (ret < 0)
goto out_err;
if ((size_t)ret < size) {
struct rbd_image_header *header)
{
struct rbd_image_header_ondisk *ondisk;
- u64 ver = 0;
int ret;
- ondisk = rbd_dev_v1_header_read(rbd_dev, &ver);
+ ondisk = rbd_dev_v1_header_read(rbd_dev);
if (IS_ERR(ondisk))
return PTR_ERR(ondisk);
ret = rbd_header_from_disk(header, ondisk);
- if (ret >= 0)
- header->obj_version = ver;
kfree(ondisk);
return ret;
}
-static void rbd_remove_all_snaps(struct rbd_device *rbd_dev)
-{
- struct rbd_snap *snap;
- struct rbd_snap *next;
-
- list_for_each_entry_safe(snap, next, &rbd_dev->snaps, node) {
- list_del(&snap->node);
- rbd_snap_destroy(snap);
- }
-}
-
-static void rbd_update_mapping_size(struct rbd_device *rbd_dev)
-{
- if (rbd_dev->spec->snap_id != CEPH_NOSNAP)
- return;
-
- if (rbd_dev->mapping.size != rbd_dev->header.image_size) {
- sector_t size;
-
- rbd_dev->mapping.size = rbd_dev->header.image_size;
- size = (sector_t)rbd_dev->mapping.size / SECTOR_SIZE;
- dout("setting size to %llu sectors", (unsigned long long)size);
- set_capacity(rbd_dev->disk, size);
- }
-}
-
/*
* only read the first part of the ondisk header, without the snaps info
*/
-static int rbd_dev_v1_refresh(struct rbd_device *rbd_dev, u64 *hver)
+static int rbd_dev_v1_refresh(struct rbd_device *rbd_dev)
{
int ret;
struct rbd_image_header h;
/* Update image size, and check for resize of mapped image */
rbd_dev->header.image_size = h.image_size;
- rbd_update_mapping_size(rbd_dev);
+ if (rbd_dev->spec->snap_id == CEPH_NOSNAP)
+ if (rbd_dev->mapping.size != rbd_dev->header.image_size)
+ rbd_dev->mapping.size = rbd_dev->header.image_size;
/* rbd_dev->header.object_prefix shouldn't change */
kfree(rbd_dev->header.snap_sizes);
kfree(rbd_dev->header.snap_names);
/* osd requests may still refer to snapc */
- rbd_snap_context_put(rbd_dev->header.snapc);
+ ceph_put_snap_context(rbd_dev->header.snapc);
- if (hver)
- *hver = h.obj_version;
- rbd_dev->header.obj_version = h.obj_version;
rbd_dev->header.image_size = h.image_size;
rbd_dev->header.snapc = h.snapc;
rbd_dev->header.snap_names = h.snap_names;
rbd_warn(rbd_dev, "object prefix changed (ignoring)");
kfree(h.object_prefix);
- ret = rbd_dev_snaps_update(rbd_dev);
-
up_write(&rbd_dev->header_rwsem);
return ret;
}
-static int rbd_dev_refresh(struct rbd_device *rbd_dev, u64 *hver)
+/*
+ * Clear the rbd device's EXISTS flag if the snapshot it's mapped to
+ * has disappeared from the (just updated) snapshot context.
+ */
+static void rbd_exists_validate(struct rbd_device *rbd_dev)
+{
+ u64 snap_id;
+
+ if (!test_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags))
+ return;
+
+ snap_id = rbd_dev->spec->snap_id;
+ if (snap_id == CEPH_NOSNAP)
+ return;
+
+ if (rbd_dev_snap_index(rbd_dev, snap_id) == BAD_SNAP_INDEX)
+ clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags);
+}
+
+static int rbd_dev_refresh(struct rbd_device *rbd_dev)
{
+ u64 mapping_size;
int ret;
rbd_assert(rbd_image_format_valid(rbd_dev->image_format));
+ mapping_size = rbd_dev->mapping.size;
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
if (rbd_dev->image_format == 1)
- ret = rbd_dev_v1_refresh(rbd_dev, hver);
+ ret = rbd_dev_v1_refresh(rbd_dev);
else
- ret = rbd_dev_v2_refresh(rbd_dev, hver);
+ ret = rbd_dev_v2_refresh(rbd_dev);
+
+ /* If it's a mapped snapshot, validate its EXISTS flag */
+
+ rbd_exists_validate(rbd_dev);
mutex_unlock(&ctl_mutex);
- revalidate_disk(rbd_dev->disk);
- if (ret)
- rbd_warn(rbd_dev, "got notification but failed to "
- " update snaps: %d\n", ret);
+ if (mapping_size != rbd_dev->mapping.size) {
+ sector_t size;
+
+ size = (sector_t)rbd_dev->mapping.size / SECTOR_SIZE;
+ dout("setting size to %llu sectors", (unsigned long long)size);
+ set_capacity(rbd_dev->disk, size);
+ revalidate_disk(rbd_dev->disk);
+ }
return ret;
}
struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
int ret;
- ret = rbd_dev_refresh(rbd_dev, NULL);
+ ret = rbd_dev_refresh(rbd_dev);
+ if (ret)
+ rbd_warn(rbd_dev, ": manual header refresh error (%d)\n", ret);
return ret < 0 ? ret : size;
}
spin_lock_init(&rbd_dev->lock);
rbd_dev->flags = 0;
INIT_LIST_HEAD(&rbd_dev->node);
- INIT_LIST_HEAD(&rbd_dev->snaps);
init_rwsem(&rbd_dev->header_rwsem);
rbd_dev->spec = spec;
kfree(rbd_dev);
}
-static void rbd_snap_destroy(struct rbd_snap *snap)
-{
- kfree(snap->name);
- kfree(snap);
-}
-
-static struct rbd_snap *rbd_snap_create(struct rbd_device *rbd_dev,
- const char *snap_name,
- u64 snap_id, u64 snap_size,
- u64 snap_features)
-{
- struct rbd_snap *snap;
-
- snap = kzalloc(sizeof (*snap), GFP_KERNEL);
- if (!snap)
- return ERR_PTR(-ENOMEM);
-
- snap->name = snap_name;
- snap->id = snap_id;
- snap->size = snap_size;
- snap->features = snap_features;
-
- return snap;
-}
-
-/*
- * Returns a dynamically-allocated snapshot name if successful, or a
- * pointer-coded error otherwise.
- */
-static char *rbd_dev_v1_snap_info(struct rbd_device *rbd_dev, u32 which,
- u64 *snap_size, u64 *snap_features)
-{
- char *snap_name;
- int i;
-
- rbd_assert(which < rbd_dev->header.snapc->num_snaps);
-
- /* Skip over names until we find the one we are looking for */
-
- snap_name = rbd_dev->header.snap_names;
- for (i = 0; i < which; i++)
- snap_name += strlen(snap_name) + 1;
-
- snap_name = kstrdup(snap_name, GFP_KERNEL);
- if (!snap_name)
- return ERR_PTR(-ENOMEM);
-
- *snap_size = rbd_dev->header.snap_sizes[which];
- *snap_features = 0; /* No features for v1 */
-
- return snap_name;
-}
-
/*
* Get the size and object order for an image snapshot, or if
* snap_id is CEPH_NOSNAP, gets this information for the base
ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
"rbd", "get_size",
&snapid, sizeof (snapid),
- &size_buf, sizeof (size_buf), NULL);
+ &size_buf, sizeof (size_buf));
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
if (ret < 0)
return ret;
ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
"rbd", "get_object_prefix", NULL, 0,
- reply_buf, RBD_OBJ_PREFIX_LEN_MAX, NULL);
+ reply_buf, RBD_OBJ_PREFIX_LEN_MAX);
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
if (ret < 0)
goto out;
ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
"rbd", "get_features",
&snapid, sizeof (snapid),
- &features_buf, sizeof (features_buf), NULL);
+ &features_buf, sizeof (features_buf));
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
if (ret < 0)
return ret;
ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
"rbd", "get_parent",
&snapid, sizeof (snapid),
- reply_buf, size, NULL);
+ reply_buf, size);
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
if (ret < 0)
goto out_err;
ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
"rbd", "get_stripe_unit_count", NULL, 0,
- (char *)&striping_info_buf, size, NULL);
+ (char *)&striping_info_buf, size);
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
if (ret < 0)
return ret;
ret = rbd_obj_method_sync(rbd_dev, RBD_DIRECTORY,
"rbd", "dir_get_name",
image_id, image_id_size,
- reply_buf, size, NULL);
+ reply_buf, size);
if (ret < 0)
goto out;
p = reply_buf;
return image_name;
}
+static u64 rbd_v1_snap_id_by_name(struct rbd_device *rbd_dev, const char *name)
+{
+ struct ceph_snap_context *snapc = rbd_dev->header.snapc;
+ const char *snap_name;
+ u32 which = 0;
+
+ /* Skip over names until we find the one we are looking for */
+
+ snap_name = rbd_dev->header.snap_names;
+ while (which < snapc->num_snaps) {
+ if (!strcmp(name, snap_name))
+ return snapc->snaps[which];
+ snap_name += strlen(snap_name) + 1;
+ which++;
+ }
+ return CEPH_NOSNAP;
+}
+
+static u64 rbd_v2_snap_id_by_name(struct rbd_device *rbd_dev, const char *name)
+{
+ struct ceph_snap_context *snapc = rbd_dev->header.snapc;
+ u32 which;
+ bool found = false;
+ u64 snap_id;
+
+ for (which = 0; !found && which < snapc->num_snaps; which++) {
+ const char *snap_name;
+
+ snap_id = snapc->snaps[which];
+ snap_name = rbd_dev_v2_snap_name(rbd_dev, snap_id);
+ if (IS_ERR(snap_name))
+ break;
+ found = !strcmp(name, snap_name);
+ kfree(snap_name);
+ }
+ return found ? snap_id : CEPH_NOSNAP;
+}
+
+/*
+ * Assumes name is never RBD_SNAP_HEAD_NAME; returns CEPH_NOSNAP if
+ * no snapshot by that name is found, or if an error occurs.
+ */
+static u64 rbd_snap_id_by_name(struct rbd_device *rbd_dev, const char *name)
+{
+ if (rbd_dev->image_format == 1)
+ return rbd_v1_snap_id_by_name(rbd_dev, name);
+
+ return rbd_v2_snap_id_by_name(rbd_dev, name);
+}
+
/*
* When an rbd image has a parent image, it is identified by the
* pool, image, and snapshot ids (not names). This function fills
* When an image being mapped (not a parent) is probed, we have the
* pool name and pool id, image name and image id, and the snapshot
* name. The only thing we're missing is the snapshot id.
- *
- * The set of snapshots for an image is not known until they have
- * been read by rbd_dev_snaps_update(), so we can't completely fill
- * in this information until after that has been called.
*/
static int rbd_dev_spec_update(struct rbd_device *rbd_dev)
{
*/
if (spec->pool_name) {
if (strcmp(spec->snap_name, RBD_SNAP_HEAD_NAME)) {
- struct rbd_snap *snap;
+ u64 snap_id;
- snap = snap_by_name(rbd_dev, spec->snap_name);
- if (!snap)
+ snap_id = rbd_snap_id_by_name(rbd_dev, spec->snap_name);
+ if (snap_id == CEPH_NOSNAP)
return -ENOENT;
- spec->snap_id = snap->id;
+ spec->snap_id = snap_id;
} else {
spec->snap_id = CEPH_NOSNAP;
}
/* Look up the snapshot name, and make a copy */
snap_name = rbd_snap_name(rbd_dev, spec->snap_id);
- if (!snap_name) {
- rbd_warn(rbd_dev, "no snapshot with id %llu", spec->snap_id);
- ret = -EIO;
- goto out_err;
- }
- snap_name = kstrdup(snap_name, GFP_KERNEL);
if (!snap_name) {
ret = -ENOMEM;
goto out_err;
return ret;
}
-static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev, u64 *ver)
+static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev)
{
size_t size;
int ret;
ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
"rbd", "get_snapcontext", NULL, 0,
- reply_buf, size, ver);
+ reply_buf, size);
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
if (ret < 0)
goto out;
goto out;
ret = 0;
- snapc = rbd_snap_context_create(snap_count);
+ snapc = ceph_create_snap_context(snap_count, GFP_KERNEL);
if (!snapc) {
ret = -ENOMEM;
goto out;
for (i = 0; i < snap_count; i++)
snapc->snaps[i] = ceph_decode_64(&p);
+ ceph_put_snap_context(rbd_dev->header.snapc);
rbd_dev->header.snapc = snapc;
dout(" snap context seq = %llu, snap_count = %u\n",
return ret;
}
-static char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, u32 which)
+static const char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev,
+ u64 snap_id)
{
size_t size;
void *reply_buf;
- __le64 snap_id;
+ __le64 snapid;
int ret;
void *p;
void *end;
if (!reply_buf)
return ERR_PTR(-ENOMEM);
- rbd_assert(which < rbd_dev->header.snapc->num_snaps);
- snap_id = cpu_to_le64(rbd_dev->header.snapc->snaps[which]);
+ snapid = cpu_to_le64(snap_id);
ret = rbd_obj_method_sync(rbd_dev, rbd_dev->header_name,
"rbd", "get_snapshot_name",
- &snap_id, sizeof (snap_id),
- reply_buf, size, NULL);
+ &snapid, sizeof (snapid),
+ reply_buf, size);
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
if (ret < 0) {
snap_name = ERR_PTR(ret);
goto out;
dout(" snap_id 0x%016llx snap_name = %s\n",
- (unsigned long long)le64_to_cpu(snap_id), snap_name);
+ (unsigned long long)snap_id, snap_name);
out:
kfree(reply_buf);
return snap_name;
}
-static char *rbd_dev_v2_snap_info(struct rbd_device *rbd_dev, u32 which,
- u64 *snap_size, u64 *snap_features)
-{
- u64 snap_id;
- u64 size;
- u64 features;
- char *snap_name;
- int ret;
-
- rbd_assert(which < rbd_dev->header.snapc->num_snaps);
- snap_id = rbd_dev->header.snapc->snaps[which];
- ret = _rbd_dev_v2_snap_size(rbd_dev, snap_id, NULL, &size);
- if (ret)
- goto out_err;
-
- ret = _rbd_dev_v2_snap_features(rbd_dev, snap_id, &features);
- if (ret)
- goto out_err;
-
- snap_name = rbd_dev_v2_snap_name(rbd_dev, which);
- if (!IS_ERR(snap_name)) {
- *snap_size = size;
- *snap_features = features;
- }
-
- return snap_name;
-out_err:
- return ERR_PTR(ret);
-}
-
-static char *rbd_dev_snap_info(struct rbd_device *rbd_dev, u32 which,
- u64 *snap_size, u64 *snap_features)
-{
- if (rbd_dev->image_format == 1)
- return rbd_dev_v1_snap_info(rbd_dev, which,
- snap_size, snap_features);
- if (rbd_dev->image_format == 2)
- return rbd_dev_v2_snap_info(rbd_dev, which,
- snap_size, snap_features);
- return ERR_PTR(-EINVAL);
-}
-
-static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev, u64 *hver)
+static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev)
{
int ret;
ret = rbd_dev_v2_image_size(rbd_dev);
if (ret)
goto out;
- rbd_update_mapping_size(rbd_dev);
+ if (rbd_dev->spec->snap_id == CEPH_NOSNAP)
+ if (rbd_dev->mapping.size != rbd_dev->header.image_size)
+ rbd_dev->mapping.size = rbd_dev->header.image_size;
- ret = rbd_dev_v2_snap_context(rbd_dev, hver);
+ ret = rbd_dev_v2_snap_context(rbd_dev);
dout("rbd_dev_v2_snap_context returned %d\n", ret);
if (ret)
goto out;
- ret = rbd_dev_snaps_update(rbd_dev);
- dout("rbd_dev_snaps_update returned %d\n", ret);
- if (ret)
- goto out;
out:
up_write(&rbd_dev->header_rwsem);
return ret;
}
-/*
- * Scan the rbd device's current snapshot list and compare it to the
- * newly-received snapshot context. Remove any existing snapshots
- * not present in the new snapshot context. Add a new snapshot for
- * any snaphots in the snapshot context not in the current list.
- * And verify there are no changes to snapshots we already know
- * about.
- *
- * Assumes the snapshots in the snapshot context are sorted by
- * snapshot id, highest id first. (Snapshots in the rbd_dev's list
- * are also maintained in that order.)
- *
- * Note that any error occurs while updating the snapshot list
- * aborts the update, and the entire list is cleared. The snapshot
- * list becomes inconsistent at that point anyway, so it might as
- * well be empty.
- */
-static int rbd_dev_snaps_update(struct rbd_device *rbd_dev)
-{
- struct ceph_snap_context *snapc = rbd_dev->header.snapc;
- const u32 snap_count = snapc->num_snaps;
- struct list_head *head = &rbd_dev->snaps;
- struct list_head *links = head->next;
- u32 index = 0;
- int ret = 0;
-
- dout("%s: snap count is %u\n", __func__, (unsigned int)snap_count);
- while (index < snap_count || links != head) {
- u64 snap_id;
- struct rbd_snap *snap;
- char *snap_name;
- u64 snap_size = 0;
- u64 snap_features = 0;
-
- snap_id = index < snap_count ? snapc->snaps[index]
- : CEPH_NOSNAP;
- snap = links != head ? list_entry(links, struct rbd_snap, node)
- : NULL;
- rbd_assert(!snap || snap->id != CEPH_NOSNAP);
-
- if (snap_id == CEPH_NOSNAP || (snap && snap->id > snap_id)) {
- struct list_head *next = links->next;
-
- /*
- * A previously-existing snapshot is not in
- * the new snap context.
- *
- * If the now-missing snapshot is the one
- * the image represents, clear its existence
- * flag so we can avoid sending any more
- * requests to it.
- */
- if (rbd_dev->spec->snap_id == snap->id)
- clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags);
- dout("removing %ssnap id %llu\n",
- rbd_dev->spec->snap_id == snap->id ?
- "mapped " : "",
- (unsigned long long)snap->id);
-
- list_del(&snap->node);
- rbd_snap_destroy(snap);
-
- /* Done with this list entry; advance */
-
- links = next;
- continue;
- }
-
- snap_name = rbd_dev_snap_info(rbd_dev, index,
- &snap_size, &snap_features);
- if (IS_ERR(snap_name)) {
- ret = PTR_ERR(snap_name);
- dout("failed to get snap info, error %d\n", ret);
- goto out_err;
- }
-
- dout("entry %u: snap_id = %llu\n", (unsigned int)snap_count,
- (unsigned long long)snap_id);
- if (!snap || (snap_id != CEPH_NOSNAP && snap->id < snap_id)) {
- struct rbd_snap *new_snap;
-
- /* We haven't seen this snapshot before */
-
- new_snap = rbd_snap_create(rbd_dev, snap_name,
- snap_id, snap_size, snap_features);
- if (IS_ERR(new_snap)) {
- ret = PTR_ERR(new_snap);
- dout(" failed to add dev, error %d\n", ret);
- goto out_err;
- }
-
- /* New goes before existing, or at end of list */
-
- dout(" added dev%s\n", snap ? "" : " at end\n");
- if (snap)
- list_add_tail(&new_snap->node, &snap->node);
- else
- list_add_tail(&new_snap->node, head);
- } else {
- /* Already have this one */
-
- dout(" already present\n");
-
- rbd_assert(snap->size == snap_size);
- rbd_assert(!strcmp(snap->name, snap_name));
- rbd_assert(snap->features == snap_features);
-
- /* Done with this list entry; advance */
-
- links = links->next;
- }
-
- /* Advance to the next entry in the snapshot context */
-
- index++;
- }
- dout("%s: done\n", __func__);
-
- return 0;
-out_err:
- rbd_remove_all_snaps(rbd_dev);
-
- return ret;
-}
-
static int rbd_bus_add_dev(struct rbd_device *rbd_dev)
{
struct device *dev;
dev->bus = &rbd_bus_type;
dev->type = &rbd_device_type;
dev->parent = &rbd_root_dev;
- dev->release = rbd_dev_release;
+ dev->release = rbd_dev_device_release;
dev_set_name(dev, "%d", rbd_dev->dev_id);
ret = device_register(dev);
ret = rbd_obj_method_sync(rbd_dev, object_name,
"rbd", "get_id", NULL, 0,
- response, RBD_IMAGE_ID_LEN_MAX, NULL);
+ response, RBD_IMAGE_ID_LEN_MAX);
dout("%s: rbd_obj_method_sync returned %d\n", __func__, ret);
if (ret == -ENOENT) {
image_id = kstrdup("", GFP_KERNEL);
return ret;
}
+/* Undo whatever state changes are made by v1 or v2 image probe */
+
+static void rbd_dev_unprobe(struct rbd_device *rbd_dev)
+{
+ struct rbd_image_header *header;
+
+ rbd_dev_remove_parent(rbd_dev);
+ rbd_spec_put(rbd_dev->parent_spec);
+ rbd_dev->parent_spec = NULL;
+ rbd_dev->parent_overlap = 0;
+
+ /* Free dynamic fields from the header, then zero it out */
+
+ header = &rbd_dev->header;
+ ceph_put_snap_context(header->snapc);
+ kfree(header->snap_sizes);
+ kfree(header->snap_names);
+ kfree(header->object_prefix);
+ memset(header, 0, sizeof (*header));
+}
+
static int rbd_dev_v1_probe(struct rbd_device *rbd_dev)
{
int ret;
static int rbd_dev_v2_probe(struct rbd_device *rbd_dev)
{
int ret;
- u64 ver = 0;
ret = rbd_dev_v2_image_size(rbd_dev);
if (ret)
ret = rbd_dev_v2_parent_info(rbd_dev);
if (ret)
goto out_err;
- rbd_warn(rbd_dev, "WARNING: kernel support for "
- "layered rbd images is EXPERIMENTAL!");
+ /*
+ * Print a warning if this image has a parent.
+ * Don't print it if the image now being probed
+ * is itself a parent. We can tell at this point
+ * because we won't know its pool name yet (just its
+ * pool id).
+ */
+ if (rbd_dev->parent_spec && rbd_dev->spec->pool_name)
+ rbd_warn(rbd_dev, "WARNING: kernel layering "
+ "is EXPERIMENTAL!");
}
/* If the image supports fancy striping, get its parameters */
/* Get the snapshot context, plus the header version */
- ret = rbd_dev_v2_snap_context(rbd_dev, &ver);
+ ret = rbd_dev_v2_snap_context(rbd_dev);
if (ret)
goto out_err;
- rbd_dev->header.obj_version = ver;
dout("discovered version 2 image, header name is %s\n",
rbd_dev->header_name);
return ret;
}
-static int rbd_dev_probe_finish(struct rbd_device *rbd_dev)
+static int rbd_dev_device_setup(struct rbd_device *rbd_dev)
{
int ret;
return 0;
}
+static void rbd_dev_image_release(struct rbd_device *rbd_dev)
+{
+ int ret;
+
+ rbd_dev_unprobe(rbd_dev);
+ ret = rbd_dev_header_watch_sync(rbd_dev, 0);
+ if (ret)
+ rbd_warn(rbd_dev, "failed to cancel watch event (%d)\n", ret);
+ kfree(rbd_dev->header_name);
+ rbd_dev->header_name = NULL;
+ rbd_dev->image_format = 0;
+ kfree(rbd_dev->spec->image_id);
+ rbd_dev->spec->image_id = NULL;
+
+ rbd_dev_destroy(rbd_dev);
+}
+
/*
* Probe for the existence of the header object for the given rbd
* device. For format 2 images this includes determining the image
if (ret)
goto err_out_watch;
- ret = rbd_dev_snaps_update(rbd_dev);
- if (ret)
- goto err_out_watch;
-
ret = rbd_dev_spec_update(rbd_dev);
if (ret)
- goto err_out_snaps;
+ goto err_out_probe;
ret = rbd_dev_probe_parent(rbd_dev);
- if (ret)
- goto err_out_snaps;
-
- ret = rbd_dev_probe_finish(rbd_dev);
- if (ret)
- goto err_out_parent;
+ if (!ret)
+ return 0;
- return ret;
-err_out_parent:
- rbd_dev_remove_parent(rbd_dev);
- rbd_header_free(&rbd_dev->header);
-err_out_snaps:
- rbd_remove_all_snaps(rbd_dev);
+err_out_probe:
+ rbd_dev_unprobe(rbd_dev);
err_out_watch:
tmp = rbd_dev_header_watch_sync(rbd_dev, 0);
if (tmp)
if (rc < 0)
goto err_out_rbd_dev;
- return count;
+ rc = rbd_dev_device_setup(rbd_dev);
+ if (!rc)
+ return count;
+
+ rbd_dev_image_release(rbd_dev);
err_out_rbd_dev:
- kfree(rbd_dev->header_name);
rbd_dev_destroy(rbd_dev);
err_out_client:
rbd_put_client(rbdc);
return NULL;
}
-static void rbd_dev_release(struct device *dev)
+static void rbd_dev_device_release(struct device *dev)
{
struct rbd_device *rbd_dev = dev_to_rbd_dev(dev);
- /* clean up and free blkdev */
rbd_free_disk(rbd_dev);
+ clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags);
+ rbd_dev_clear_mapping(rbd_dev);
unregister_blkdev(rbd_dev->major, rbd_dev->name);
-
- /* release allocated disk header fields */
- rbd_header_free(&rbd_dev->header);
-
- /* done with the id, and with the rbd_dev */
+ rbd_dev->major = 0;
rbd_dev_id_put(rbd_dev);
rbd_dev_mapping_clear(rbd_dev);
- rbd_assert(rbd_dev->rbd_client != NULL);
- rbd_spec_put(rbd_dev->parent_spec);
- kfree(rbd_dev->header_name);
- rbd_dev_destroy(rbd_dev);
}
static void rbd_dev_remove_parent(struct rbd_device *rbd_dev)
struct rbd_device *first = rbd_dev;
struct rbd_device *second = first->parent;
struct rbd_device *third;
- int ret;
/*
* Follow to the parent with no grandparent and
second = third;
}
rbd_assert(second);
- ret = rbd_dev_header_watch_sync(rbd_dev, 0);
- if (ret)
- rbd_warn(rbd_dev,
- "failed to cancel watch event (%d)\n", ret);
- rbd_remove_all_snaps(second);
- rbd_bus_del_dev(second);
+ rbd_dev_image_release(second);
first->parent = NULL;
first->parent_overlap = 0;
spin_unlock_irq(&rbd_dev->lock);
if (ret < 0)
goto done;
-
- ret = rbd_dev_header_watch_sync(rbd_dev, 0);
- if (ret) {
- rbd_warn(rbd_dev, "failed to cancel watch event (%d)\n", ret);
- clear_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags);
- smp_mb();
- return ret;
- }
ret = count;
-
- rbd_dev_remove_parent(rbd_dev);
-
- rbd_remove_all_snaps(rbd_dev);
rbd_bus_del_dev(rbd_dev);
+ rbd_dev_image_release(rbd_dev);
module_put(THIS_MODULE);
done:
mutex_unlock(&ctl_mutex);
device_unregister(&rbd_root_dev);
}
+static int rbd_slab_init(void)
+{
+ rbd_assert(!rbd_img_request_cache);
+ rbd_img_request_cache = kmem_cache_create("rbd_img_request",
+ sizeof (struct rbd_img_request),
+ __alignof__(struct rbd_img_request),
+ 0, NULL);
+ if (!rbd_img_request_cache)
+ return -ENOMEM;
+
+ rbd_assert(!rbd_obj_request_cache);
+ rbd_obj_request_cache = kmem_cache_create("rbd_obj_request",
+ sizeof (struct rbd_obj_request),
+ __alignof__(struct rbd_obj_request),
+ 0, NULL);
+ if (!rbd_obj_request_cache)
+ goto out_err;
+
+ rbd_assert(!rbd_segment_name_cache);
+ rbd_segment_name_cache = kmem_cache_create("rbd_segment_name",
+ MAX_OBJ_NAME_SIZE + 1, 1, 0, NULL);
+ if (rbd_segment_name_cache)
+ return 0;
+out_err:
+ if (rbd_obj_request_cache) {
+ kmem_cache_destroy(rbd_obj_request_cache);
+ rbd_obj_request_cache = NULL;
+ }
+
+ kmem_cache_destroy(rbd_img_request_cache);
+ rbd_img_request_cache = NULL;
+
+ return -ENOMEM;
+}
+
+static void rbd_slab_exit(void)
+{
+ rbd_assert(rbd_segment_name_cache);
+ kmem_cache_destroy(rbd_segment_name_cache);
+ rbd_segment_name_cache = NULL;
+
+ rbd_assert(rbd_obj_request_cache);
+ kmem_cache_destroy(rbd_obj_request_cache);
+ rbd_obj_request_cache = NULL;
+
+ rbd_assert(rbd_img_request_cache);
+ kmem_cache_destroy(rbd_img_request_cache);
+ rbd_img_request_cache = NULL;
+}
+
static int __init rbd_init(void)
{
int rc;
return -EINVAL;
}
- rc = rbd_sysfs_init();
+ rc = rbd_slab_init();
if (rc)
return rc;
- pr_info("loaded " RBD_DRV_NAME_LONG "\n");
- return 0;
+ rc = rbd_sysfs_init();
+ if (rc)
+ rbd_slab_exit();
+ else
+ pr_info("loaded " RBD_DRV_NAME_LONG "\n");
+
+ return rc;
}
static void __exit rbd_exit(void)
{
rbd_sysfs_cleanup();
+ rbd_slab_exit();
}
module_init(rbd_init);