Skip to content

Commit

Permalink
rbd: reference count parent requests
Browse files Browse the repository at this point in the history
Keep a reference count for uses of the parent information for an rbd
device.

An initial reference is set in rbd_img_request_create() if the
target image has a parent (with non-zero overlap).  Each image
request for an image with a non-zero parent overlap gets another
reference when it's created, and that reference is dropped when the
request is destroyed.

The initial reference is dropped when the image gets torn down.

Signed-off-by: Alex Elder <elder@inktank.com>
Reviewed-by: Josh Durgin <josh.durgin@inktank.com>
  • Loading branch information
Alex Elder committed May 13, 2013
1 parent e93f315 commit a2acd00
Showing 1 changed file with 102 additions and 2 deletions.
104 changes: 102 additions & 2 deletions drivers/block/rbd.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,39 @@
#define SECTOR_SHIFT 9
#define SECTOR_SIZE (1ULL << SECTOR_SHIFT)

/*
* Increment the given counter and return its updated value.
* If the counter is already 0 it will not be incremented.
* If the counter is already at its maximum value returns
* -EINVAL without updating it.
*/
static int atomic_inc_return_safe(atomic_t *v)
{
unsigned int counter;

counter = (unsigned int)__atomic_add_unless(v, 1, 0);
if (counter <= (unsigned int)INT_MAX)
return (int)counter;

atomic_dec(v);

return -EINVAL;
}

/* Decrement the counter. Return the resulting value, or -EINVAL */
static int atomic_dec_return_safe(atomic_t *v)
{
int counter;

counter = atomic_dec_return(v);
if (counter >= 0)
return counter;

atomic_inc(v);

return -EINVAL;
}

#define RBD_DRV_NAME "rbd"
#define RBD_DRV_NAME_LONG "rbd (rados block device)"

Expand Down Expand Up @@ -312,6 +345,7 @@ struct rbd_device {

struct rbd_spec *parent_spec;
u64 parent_overlap;
atomic_t parent_ref;
struct rbd_device *parent;

/* protects updating the header */
Expand Down Expand Up @@ -361,6 +395,7 @@ static ssize_t rbd_add(struct bus_type *bus, const char *buf,
static ssize_t rbd_remove(struct bus_type *bus, const char *buf,
size_t count);
static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping);
static void rbd_spec_put(struct rbd_spec *spec);

static struct bus_attribute rbd_bus_attrs[] = {
__ATTR(add, S_IWUSR, NULL, rbd_add),
Expand Down Expand Up @@ -1505,6 +1540,12 @@ static void img_request_layered_set(struct rbd_img_request *img_request)
smp_mb();
}

static void img_request_layered_clear(struct rbd_img_request *img_request)
{
clear_bit(IMG_REQ_LAYERED, &img_request->flags);
smp_mb();
}

static bool img_request_layered_test(struct rbd_img_request *img_request)
{
smp_mb();
Expand Down Expand Up @@ -1859,6 +1900,58 @@ static void rbd_dev_unparent(struct rbd_device *rbd_dev)
rbd_dev->parent_overlap = 0;
}

/*
* Parent image reference counting is used to determine when an
* image's parent fields can be safely torn down--after there are no
* more in-flight requests to the parent image. When the last
* reference is dropped, cleaning them up is safe.
*/
static void rbd_dev_parent_put(struct rbd_device *rbd_dev)
{
int counter;

if (!rbd_dev->parent_spec)
return;

counter = atomic_dec_return_safe(&rbd_dev->parent_ref);
if (counter > 0)
return;

/* Last reference; clean up parent data structures */

if (!counter)
rbd_dev_unparent(rbd_dev);
else
rbd_warn(rbd_dev, "parent reference underflow\n");
}

/*
* If an image has a non-zero parent overlap, get a reference to its
* parent.
*
* Returns true if the rbd device has a parent with a non-zero
* overlap and a reference for it was successfully taken, or
* false otherwise.
*/
static bool rbd_dev_parent_get(struct rbd_device *rbd_dev)
{
int counter;

if (!rbd_dev->parent_spec)
return false;

counter = atomic_inc_return_safe(&rbd_dev->parent_ref);
if (counter > 0 && rbd_dev->parent_overlap)
return true;

/* Image was flattened, but parent is not yet torn down */

if (counter < 0)
rbd_warn(rbd_dev, "parent reference overflow\n");

return false;
}

/*
* Caller is responsible for filling in the list of object requests
* that comprises the image request, and the Linux request pointer
Expand Down Expand Up @@ -1892,7 +1985,7 @@ static struct rbd_img_request *rbd_img_request_create(
} else {
img_request->snap_id = rbd_dev->spec->snap_id;
}
if (rbd_dev->parent_overlap)
if (rbd_dev_parent_get(rbd_dev))
img_request_layered_set(img_request);
spin_lock_init(&img_request->completion_lock);
img_request->next_completion = 0;
Expand Down Expand Up @@ -1923,6 +2016,11 @@ static void rbd_img_request_destroy(struct kref *kref)
rbd_img_obj_request_del(img_request, obj_request);
rbd_assert(img_request->obj_request_count == 0);

if (img_request_layered_test(img_request)) {
img_request_layered_clear(img_request);
rbd_dev_parent_put(img_request->rbd_dev);
}

if (img_request_write_test(img_request))
ceph_put_snap_context(img_request->snapc);

Expand Down Expand Up @@ -3502,6 +3600,7 @@ static struct rbd_device *rbd_dev_create(struct rbd_client *rbdc,

spin_lock_init(&rbd_dev->lock);
rbd_dev->flags = 0;
atomic_set(&rbd_dev->parent_ref, 0);
INIT_LIST_HEAD(&rbd_dev->node);
init_rwsem(&rbd_dev->header_rwsem);

Expand Down Expand Up @@ -4534,7 +4633,7 @@ static void rbd_dev_unprobe(struct rbd_device *rbd_dev)
{
struct rbd_image_header *header;

rbd_dev_unparent(rbd_dev);
rbd_dev_parent_put(rbd_dev);

/* Free dynamic fields from the header, then zero it out */

Expand Down Expand Up @@ -4606,6 +4705,7 @@ static int rbd_dev_probe_parent(struct rbd_device *rbd_dev)
if (ret < 0)
goto out_err;
rbd_dev->parent = parent;
atomic_set(&rbd_dev->parent_ref, 1);

return 0;
out_err:
Expand Down

0 comments on commit a2acd00

Please sign in to comment.