Skip to contentSkip to conversation

rbd: improve how rbd ids are selected

PATCH In review Alex Elder 3 participants 10 messages Feb 29, 2012 – Mar 2, 2012

Subsystems (2)
lore
Share & tools
lore

Alex Elder

author
#0 lore raw

cover rbd: improve how rbd ids are selected

New rbd devices are granted a unique identifiers based on how many devices are already in existence. This series rearranges how that is done, switching from using a spinlock to using an atomic variable to select the next rbd id to use. In the process a bit of the code got a bit more isolated.

-Alex

author
#1 lore raw

PATCH 2/5 rbd: encapsulate new rbd id selection

Move the loop that finds a new unique rbd id to use into its own helper function.

---
 drivers/block/rbd.c |   30 +++++++++++++++++++-----------
 1 files changed, 19 insertions(+), 11 deletions(-)
drivers/block/rbd.c +19 -11
index 25ed3c0..35290b1 100644
@@ -2148,6 +2148,23 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
 	return ret;
 }
 
+/* caller must hold ctl_mutex */
+static int rbd_id_get(void)
+{
+	struct list_head *tmp;
+	int new_id = 0;
+
+	list_for_each(tmp, &rbd_dev_list) {
+		struct rbd_device *rbd_dev;
+
+		rbd_dev = list_entry(tmp, struct rbd_device, node);
+		if (rbd_dev->id >= new_id)
+			new_id = rbd_dev->id + 1;
+	}
+
+	return new_id;
+}
+
 static ssize_t rbd_add(struct bus_type *bus,
 		       const char *buf,
 		       size_t count)
@@ -2155,8 +2172,7 @@ static ssize_t rbd_add(struct bus_type *bus,
 	struct ceph_osd_client *osdc;
 	struct rbd_device *rbd_dev;
 	ssize_t rc = -ENOMEM;
-	int irc, new_id = 0;
-	struct list_head *tmp;
+	int irc;
 	char *mon_dev_name;
 	char *options;
 
@@ -2184,15 +2200,7 @@ static ssize_t rbd_add(struct bus_type *bus,
 	/* generate unique id: find highest unique id, add one */
 	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
 
-	list_for_each(tmp, &rbd_dev_list) {
-		struct rbd_device *rbd_dev;
-
-		rbd_dev = list_entry(tmp, struct rbd_device, node);
-		if (rbd_dev->id >= new_id)
-			new_id = rbd_dev->id + 1;
-	}
-
-	rbd_dev->id = new_id;
+	rbd_dev->id = rbd_id_get();
 
 	/* add to global list */
 	list_add_tail(&rbd_dev->node, &rbd_dev_list);
Signature · 1 line
1.7.5.4
author
#2 lore raw

PATCH 2/5 rbd: rework calculation of new rbd id's

In reply to Alex Elder

In order to select a new unique identifier for an added rbd device, the list of all existing ones is searched and a value one greater than the highest id is used.

The list search can be avoided by using an atomic variable that keeps track of the current highest id. Using a get/put model for id's we can limit the boundless growth of id numbers a bit by arranging to reuse the current highest id once it gets released. Add these calls to "put" the id when an rbd is getting removed.

Note that this changes the pattern of device id's used--new values will never be below the highest one seen so far (even if there exists an unused lower one). I assert this is OK because the key property of an rbd id is its uniqueness, not its magnitude.

Regardless, a follow-on patch will restore the old way of doing things, I just think this commit just makes the incremental change to atomics a little easier to understand.

---
 drivers/block/rbd.c |   35 +++++++++++++++++++++++------------
 1 files changed, 23 insertions(+), 12 deletions(-)
drivers/block/rbd.c +23 -12
index 35290b1..0c492e4 100644
@@ -2148,21 +2148,29 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
 	return ret;
 }
 
-/* caller must hold ctl_mutex */
+static atomic_t rbd_id_max = ATOMIC_INIT(0);
+
+/*
+ * Get a unique rbd identifier.  The minimum rbd id is 1.
+ */
 static int rbd_id_get(void)
 {
-	struct list_head *tmp;
-	int new_id = 0;
-
-	list_for_each(tmp, &rbd_dev_list) {
-		struct rbd_device *rbd_dev;
+	return atomic_inc_return(&rbd_id_max);
+}
 
-		rbd_dev = list_entry(tmp, struct rbd_device, node);
-		if (rbd_dev->id >= new_id)
-			new_id = rbd_dev->id + 1;
-	}
+/*
+ * Record that an rbd identifier is no longer in use.
+ */
+static void rbd_id_put(int rbd_id)
+{
+	BUG_ON(rbd_id < 1);
 
-	return new_id;
+	/*
+	 * New id's are always one more than the current maximum.
+	 * If the id being "put" *is* that maximum, decrement the
+	 * maximum so the next one requested just reuses this one.
+	 */
+	atomic_cmpxchg(&rbd_id_max, rbd_id, rbd_id - 1);
 }
 
 static ssize_t rbd_add(struct bus_type *bus,
@@ -2197,7 +2205,7 @@ static ssize_t rbd_add(struct bus_type *bus,
 	INIT_LIST_HEAD(&rbd_dev->node);
 	INIT_LIST_HEAD(&rbd_dev->snaps);
 
-	/* generate unique id: find highest unique id, add one */
+	/* generate unique id: one more than highest used so far */
 	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
 
 	rbd_dev->id = rbd_id_get();
@@ -2267,6 +2275,7 @@ err_out_bus:
 	mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
 	list_del_init(&rbd_dev->node);
 	mutex_unlock(&ctl_mutex);
+	rbd_id_put(target_id);
 
 	/* this will also clean up rest of rbd_dev stuff */
 
@@ -2283,6 +2292,7 @@ err_out_client:
 err_out_slot:
 	list_del_init(&rbd_dev->node);
 	mutex_unlock(&ctl_mutex);
+	rbd_id_put(target_id);
 
 	kfree(rbd_dev);
 err_out_opt:
@@ -2360,6 +2370,7 @@ static ssize_t rbd_remove(struct bus_type *bus,
 	}
 
 	list_del_init(&rbd_dev->node);
+	rbd_id_put(target_id);
 
 	__rbd_remove_all_snaps(rbd_dev);
 	rbd_bus_del_dev(rbd_dev);
Signature · 1 line
1.7.5.4
author
#4 lore raw

PATCH 3/5 rbd: protect the rbd_dev_list with a spinlock

In reply to Alex Elder

The rbd_dev_list is just a simple list of all the current rbd_devices. Using the ctl_mutex as a concurrency guard is overkill. Instead, use a spinlock for that specific purpose.

This also reduces the window that the ctl_mutex needs to be held in rbd_add().

---
 drivers/block/rbd.c |   33 ++++++++++++++++++++++-----------
 1 files changed, 22 insertions(+), 11 deletions(-)
drivers/block/rbd.c +0 -2
index 0c492e4..e839289 100644
@@ -174,11 +174,13 @@ static struct bus_type rbd_bus_type = {
 	.name		= "rbd",
 };
 
-static DEFINE_SPINLOCK(node_lock);      /* protects client get/put */
-
 static DEFINE_MUTEX(ctl_mutex);	  /* Serialize 

open/close/setup/teardown */
+
static LIST_HEAD(rbd_dev_list); /* devices */
+static DEFINE_SPINLOCK(rbd_dev_list_lock);
+
static LIST_HEAD(rbd_client_list); /* clients */
+static DEFINE_SPINLOCK(node_lock); /* protects client get/put */

static int __rbd_init_snaps_header(struct rbd_device *rbd_dev); static void rbd_dev_release(struct device *dev);
@@ -2206,12 +2208,12 @@ static ssize_t rbd_add(struct bus_type *bus,
INIT_LIST_HEAD(&rbd_dev->snaps);

/* generate unique id: one more than highest used so far */
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
-
rbd_dev->id = rbd_id_get();

/* add to global list */
+ spin_lock(&rbd_dev_list_lock);
list_add_tail(&rbd_dev->node, &rbd_dev_list);
+ spin_unlock(&rbd_dev_list_lock);

/* parse add command */
if (sscanf(buf, "%" __stringify(RBD_MAX_OPT_LEN) "s "
@@ -2235,12 +2237,14 @@ static ssize_t rbd_add(struct bus_type *bus,

/* initialize rest of new object */ snprintf(rbd_dev->name, DEV_NAME_LEN, DRV_NAME "%d", rbd_dev->id);
+
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
rc = rbd_get_client(rbd_dev, mon_dev_name, options);
+ mutex_unlock(&ctl_mutex);
+
if (rc < 0)
goto err_out_slot;

- mutex_unlock(&ctl_mutex);
-
/* pick the pool */
osdc = &rbd_dev->rbd_client->client->osdc;
rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->pool_name);
@@ -2272,9 +2276,9 @@ static ssize_t rbd_add(struct bus_type *bus,
return count;

err_out_bus:
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+ spin_lock(&rbd_dev_list_lock);
list_del_init(&rbd_dev->node);
- mutex_unlock(&ctl_mutex);
+ spin_unlock(&rbd_dev_list_lock);
rbd_id_put(target_id);

/* this will also clean up rest of rbd_dev stuff */
@@ -2288,10 +2292,10 @@ err_out_blkdev:
unregister_blkdev(rbd_dev->major, rbd_dev->name);
err_out_client:
rbd_put_client(rbd_dev);
- mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
err_out_slot:
+ spin_lock(&rbd_dev_list_lock);
list_del_init(&rbd_dev->node);
- mutex_unlock(&ctl_mutex);
+ spin_unlock(&rbd_dev_list_lock);
rbd_id_put(target_id);

kfree(rbd_dev);
@@ -2310,11 +2314,15 @@ static struct rbd_device *__rbd_get_dev(unsigned long id)
struct list_head *tmp;
struct rbd_device *rbd_dev;

+ spin_lock(&rbd_dev_list_lock);
list_for_each(tmp, &rbd_dev_list) {
rbd_dev = list_entry(tmp, struct rbd_device, node);
- if (rbd_dev->id == id)
+ if (rbd_dev->id == id) {
+ spin_unlock(&rbd_dev_list_lock);
return rbd_dev;
+ }
}
+ spin_unlock(&rbd_dev_list_lock);
return NULL;
}

@@ -2369,7 +2377,10 @@ static ssize_t rbd_remove(struct bus_type *bus,
goto done;
}

+ spin_lock(&rbd_dev_list_lock);
list_del_init(&rbd_dev->node);
+ spin_unlock(&rbd_dev_list_lock);
+
rbd_id_put(target_id);

__rbd_remove_all_snaps(rbd_dev);

Signature · 1 line
1.7.5.4
author
#5 lore raw

PATCH 4/5 rbd: tie rbd_dev_list changes to rbd_id operations

In reply to Alex Elder

The only time entries are added to or removed from the global rbd_dev_list is exactly when a "put" or "get" operation is being performed on a rbd_dev's id. So just move the list management code into get/put routines.

---
 drivers/block/rbd.c |   47 +++++++++++++++++++++--------------------------
 1 files changed, 21 insertions(+), 26 deletions(-)
drivers/block/rbd.c +21 -26
index e839289..042377b 100644
@@ -2153,26 +2153,36 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
 static atomic_t rbd_id_max = ATOMIC_INIT(0);
 
 /*
- * Get a unique rbd identifier.  The minimum rbd id is 1.
+ * Get a unique rbd identifier for the given new rbd_dev, and add
+ * the rbd_dev to the global list.  The minimum rbd id is 1.
  */
-static int rbd_id_get(void)
+static void rbd_id_get(struct rbd_device *rbd_dev)
 {
-	return atomic_inc_return(&rbd_id_max);
+	rbd_dev->id = atomic_inc_return(&rbd_id_max);
+
+	spin_lock(&rbd_dev_list_lock);
+	list_add_tail(&rbd_dev->node, &rbd_dev_list);
+	spin_unlock(&rbd_dev_list_lock);
 }
 
 /*
- * Record that an rbd identifier is no longer in use.
+ * Remove an rbd_dev from the global list, and record that its
+ * identifier is no longer in use.
  */
-static void rbd_id_put(int rbd_id)
+static void rbd_id_put(struct rbd_device *rbd_dev)
 {
-	BUG_ON(rbd_id < 1);
+	BUG_ON(rbd_dev->id < 1);
+
+	spin_lock(&rbd_dev_list_lock);
+	list_del_init(&rbd_dev->node);
+	spin_unlock(&rbd_dev_list_lock);
 
 	/*
 	 * New id's are always one more than the current maximum.
 	 * If the id being "put" *is* that maximum, decrement the
 	 * maximum so the next one requested just reuses this one.
 	 */
-	atomic_cmpxchg(&rbd_id_max, rbd_id, rbd_id - 1);
+	atomic_cmpxchg(&rbd_id_max, rbd_dev->id, rbd_dev->id - 1);
 }
 
 static ssize_t rbd_add(struct bus_type *bus,
@@ -2208,12 +2218,7 @@ static ssize_t rbd_add(struct bus_type *bus,
 	INIT_LIST_HEAD(&rbd_dev->snaps);
 
 	/* generate unique id: one more than highest used so far */
-	rbd_dev->id = rbd_id_get();
-
-	/* add to global list */
-	spin_lock(&rbd_dev_list_lock);
-	list_add_tail(&rbd_dev->node, &rbd_dev_list);
-	spin_unlock(&rbd_dev_list_lock);
+	rbd_id_get(rbd_dev);
 
 	/* parse add command */
 	if (sscanf(buf, "%" __stringify(RBD_MAX_OPT_LEN) "s "
@@ -2276,10 +2281,7 @@ static ssize_t rbd_add(struct bus_type *bus,
 	return count;
 
 err_out_bus:
-	spin_lock(&rbd_dev_list_lock);
-	list_del_init(&rbd_dev->node);
-	spin_unlock(&rbd_dev_list_lock);
-	rbd_id_put(target_id);
+	rbd_id_put(rbd_dev);
 
 	/* this will also clean up rest of rbd_dev stuff */
 
@@ -2293,10 +2295,7 @@ err_out_blkdev:
 err_out_client:
 	rbd_put_client(rbd_dev);
 err_out_slot:
-	spin_lock(&rbd_dev_list_lock);
-	list_del_init(&rbd_dev->node);
-	spin_unlock(&rbd_dev_list_lock);
-	rbd_id_put(target_id);
+	rbd_id_put(rbd_dev);
 
 	kfree(rbd_dev);
 err_out_opt:
@@ -2377,11 +2376,7 @@ static ssize_t rbd_remove(struct bus_type *bus,
 		goto done;
 	}
 
-	spin_lock(&rbd_dev_list_lock);
-	list_del_init(&rbd_dev->node);
-	spin_unlock(&rbd_dev_list_lock);
-
-	rbd_id_put(target_id);
+	rbd_id_put(rbd_dev);
 
 	__rbd_remove_all_snaps(rbd_dev);
 	rbd_bus_del_dev(rbd_dev);
Signature · 1 line
1.7.5.4
author
#6 lore raw

PATCH 5/5 rbd: restore previous rbd id sequence behavior

In reply to Alex Elder

It used to be that selecting a new unique identifier for an added rbd device required searching all existing ones to find the highest id is used. A recent change made that unnecessary, but made it so that id's used were monotonically non-decreasing. It's a bit more pleasant to have smaller rbd id's though, and this change makes ids get allocated as they were before--each new id is one more than the maximum currently in use.

---
 drivers/block/rbd.c |   40 ++++++++++++++++++++++++++++++++++------
 1 files changed, 34 insertions(+), 6 deletions(-)
drivers/block/rbd.c +34 -6
index 042377b..4c5bb39 100644
@@ -2171,18 +2171,46 @@ static void rbd_id_get(struct rbd_device *rbd_dev)
  */
 static void rbd_id_put(struct rbd_device *rbd_dev)
 {
-	BUG_ON(rbd_dev->id < 1);
+	struct list_head *tmp;
+	int rbd_id = rbd_dev->id;
+	int max_id;
+
+	BUG_ON(rbd_id < 1);
 
 	spin_lock(&rbd_dev_list_lock);
 	list_del_init(&rbd_dev->node);
+
+	/*
+	 * If the id being "put" is not the current maximum, there
+	 * is nothing special we need to do.
+	 */
+	if (rbd_id != atomic_read(&rbd_id_max)) {
+		spin_unlock(&rbd_dev_list_lock);
+		return;
+	}
+
+	/*
+	 * We need to update the current maximum id.  Search the
+	 * list to find out what it is.  We're more likely to find
+	 * the maximum at the end, so search the list backward.
+	 */
+	max_id = 0;
+	list_for_each_prev(tmp, &rbd_dev_list) {
+		struct rbd_device *rbd_dev;
+
+		rbd_dev = list_entry(tmp, struct rbd_device, node);
+		if (rbd_id > max_id)
+			max_id = rbd_id;
+	}
 	spin_unlock(&rbd_dev_list_lock);
 
 	/*
-	 * New id's are always one more than the current maximum.
-	 * If the id being "put" *is* that maximum, decrement the
-	 * maximum so the next one requested just reuses this one.
+	 * The max id could have been updated by rbd_id_get(), in
+	 * which case it now accurately reflects the new maximum.
+	 * Be careful not to overwrite the maximum value in that
+	 * case.
 	 */
-	atomic_cmpxchg(&rbd_id_max, rbd_dev->id, rbd_dev->id - 1);
+	atomic_cmpxchg(&rbd_id_max, rbd_id, max_id);
 }
 
 static ssize_t rbd_add(struct bus_type *bus,
@@ -2217,7 +2245,7 @@ static ssize_t rbd_add(struct bus_type *bus,
 	INIT_LIST_HEAD(&rbd_dev->node);
 	INIT_LIST_HEAD(&rbd_dev->snaps);
 
-	/* generate unique id: one more than highest used so far */
+	/* generate unique id: find highest unique id, add one */
 	rbd_id_get(rbd_dev);
 
 	/* parse add command */
Signature · 1 line
1.7.5.4
#3 lore raw

Re: [PATCH 2/5] rbd: rework calculation of new rbd id's

In reply to Alex Elder
Quoted 49 lines · Alex Elder

In order to select a new unique identifier for an added rbd device, the list of all existing ones is searched and a value one greater than the highest id is used.

The list search can be avoided by using an atomic variable that keeps track of the current highest id.  Using a get/put model for id's we can limit the boundless growth of id numbers a bit by arranging to reuse the current highest id once it gets released. Add these calls to "put" the id when an rbd is getting removed.

Note that this changes the pattern of device id's used--new values will never be below the highest one seen so far (even if there exists an unused lower one).  I assert this is OK because the key property of an rbd id is its uniqueness, not its magnitude.

Regardless, a follow-on patch will restore the old way of doing things, I just think this commit just makes the incremental change to atomics a little easier to understand.

---
 drivers/block/rbd.c |   35 +++++++++++++++++++++++------------

 1 files changed, 23 insertions(+), 12 deletions(-)
drivers/block/rbd.c +7 -6
index 35290b1..0c492e4 100644
@@ -2148,21 +2148,29 @@ static int rbd_init_watch_dev(struct rbd_device
 *rbd_dev)
        return ret;
  }
 
-/* caller must hold ctl_mutex */
+static atomic_t rbd_id_max = ATOMIC_INIT(0);
+
+/*
+ * Get a unique rbd identifier.  The minimum rbd id is 1.
+ */
  static int rbd_id_get(void)
  {
-       struct list_head *tmp;
-       int new_id = 0;
-
-       list_for_each(tmp, &rbd_dev_list) {
-               struct rbd_device *rbd_dev;
+       return atomic_inc_return(&rbd_id_max);
+}
 

drivers/block/rbd.c static int rbd_init_watch_dev(struct rbd_device · line 2167

Quoted 10 lines
drivers/block/rbd.c +6 -4
-               rbd_dev = list_entry(tmp, struct rbd_device, node);
-               if (rbd_dev->id >= new_id)
-                       new_id = rbd_dev->id + 1;
-       }
+/*
+ * Record that an rbd identifier is no longer in use.
+ */
+static void rbd_id_put(int rbd_id)
+{
+       BUG_ON(rbd_id < 1);

A scenario where you have a few devices that keep being added/removed in order (e.g., for backup purposes) may exhaust the 31 bits used for this purpose. Can we use a 64 bit counter (is atomic64_t a good idea?)

Quoted 47 lines
drivers/block/rbd.c +8 -2
-       return new_id;
+       /*
+        * New id's are always one more than the current maximum.
+        * If the id being "put" *is* that maximum, decrement the
+        * maximum so the next one requested just reuses this one.
+        */
+       atomic_cmpxchg(&rbd_id_max, rbd_id, rbd_id - 1);
  }
 
  static ssize_t rbd_add(struct bus_type *bus,
@@ -2197,7 +2205,7 @@ static ssize_t rbd_add(struct bus_type *bus,
        INIT_LIST_HEAD(&rbd_dev->node);
        INIT_LIST_HEAD(&rbd_dev->snaps);
 
-       /* generate unique id: find highest unique id, add one */
+       /* generate unique id: one more than highest used so far */
        mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
 
        rbd_dev->id = rbd_id_get();
@@ -2267,6 +2275,7 @@ err_out_bus:
        mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
        list_del_init(&rbd_dev->node);
        mutex_unlock(&ctl_mutex);
+       rbd_id_put(target_id);
 
        /* this will also clean up rest of rbd_dev stuff */
 
@@ -2283,6 +2292,7 @@ err_out_client:

 err_out_slot:
       list_del_init(&rbd_dev->node);
       mutex_unlock(&ctl_mutex);
+       rbd_id_put(target_id);

       kfree(rbd_dev);
 err_out_opt:

drivers/block/rbd.c +1 -1
@@ -2360,6 +2370,7 @@ static ssize_t rbd_remove(struct bus_type *bus,
        }
 
        list_del_init(&rbd_dev->node);
+       rbd_id_put(target_id);
 
        __rbd_remove_all_snaps(rbd_dev);
        rbd_bus_del_dev(rbd_dev);
--

1.7.5.4

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#7 lore raw

Re: [PATCH 5/5] rbd: restore previous rbd id sequence behavior

In reply to Alex Elder
Quoted 7 lines · Alex Elder

It used to be that selecting a new unique identifier for an added rbd device required searching all existing ones to find the highest id is used.  A recent change made that unnecessary, but made it so that id's used were monotonically non-decreasing.  It's a bit more pleasant to have smaller rbd id's though, and this change makes ids get allocated as they were before--each new id is one more than the maximum currently in use.

Oh, so ignore my previous comment, though in any case we can still exhaust the 31 bits:

loop through:

add A
add B
remove A
add C
remove B
add A
remove C
add B

etc..

Quoted 37 lines

---
 drivers/block/rbd.c |   40 ++++++++++++++++++++++++++++++++++------

 1 files changed, 34 insertions(+), 6 deletions(-)
drivers/block/rbd.c +19 -1
index 042377b..4c5bb39 100644
@@ -2171,18 +2171,46 @@ static void rbd_id_get(struct rbd_device *rbd_dev)
  */
  static void rbd_id_put(struct rbd_device *rbd_dev)
  {
-       BUG_ON(rbd_dev->id < 1);
+       struct list_head *tmp;
+       int rbd_id = rbd_dev->id;
+       int max_id;
+
+       BUG_ON(rbd_id < 1);
 
        spin_lock(&rbd_dev_list_lock);
        list_del_init(&rbd_dev->node);
+
+       /*
+        * If the id being "put" is not the current maximum, there
+        * is nothing special we need to do.
+        */
+       if (rbd_id != atomic_read(&rbd_id_max)) {
+               spin_unlock(&rbd_dev_list_lock);
+               return;
+       }
+
+       /*
+        * We need to update the current maximum id.  Search the
+        * list to find out what it is.  We're more likely to find
+        * the maximum at the end, so search the list backward.

drivers/block/rbd.c static void rbd_id_get(struct rbd_device *rbd_dev) · line 2205

Quoted 10 lines
drivers/block/rbd.c +9 -0
+        */
+       max_id = 0;
+       list_for_each_prev(tmp, &rbd_dev_list) {
+               struct rbd_device *rbd_dev;
+
+               rbd_dev = list_entry(tmp, struct rbd_device, node);
+               if (rbd_id > max_id)
+                       max_id = rbd_id;
+       }
        spin_unlock(&rbd_dev_list_lock);

So we still need to either use 64 bit counter, or think of a better scheme to allocate ids. Maybe using some sorted data structure here?

Quoted 27 lines
drivers/block/rbd.c +6 -6
        /*
-        * New id's are always one more than the current maximum.
-        * If the id being "put" *is* that maximum, decrement the
-        * maximum so the next one requested just reuses this one.
+        * The max id could have been updated by rbd_id_get(), in
+        * which case it now accurately reflects the new maximum.
+        * Be careful not to overwrite the maximum value in that
+        * case.
         */
-       atomic_cmpxchg(&rbd_id_max, rbd_dev->id, rbd_dev->id - 1);
+       atomic_cmpxchg(&rbd_id_max, rbd_id, max_id);
  }
 
  static ssize_t rbd_add(struct bus_type *bus,
@@ -2217,7 +2245,7 @@ static ssize_t rbd_add(struct bus_type *bus,
        INIT_LIST_HEAD(&rbd_dev->node);
        INIT_LIST_HEAD(&rbd_dev->snaps);
 
-       /* generate unique id: one more than highest used so far */
+       /* generate unique id: find highest unique id, add one */
        rbd_id_get(rbd_dev);
 
        /* parse add command */
--

1.7.5.4

Quoted 4 lines

--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

author
#8 lore raw

Re: [PATCH 5/5] rbd: restore previous rbd id sequence behavior

Quoted 14 lines · Yehuda Sadeh Weinraub
Quoted 7 lines · Alex Elder

It used to be that selecting a new unique identifier for an added rbd device required searching all existing ones to find the highest id is used. A recent change made that unnecessary, but made it so that id's used were monotonically non-decreasing. It's a bit more pleasant to have smaller rbd id's though, and this change makes ids get allocated as they were before--each new id is one more than the maximum currently in use.

Oh, so ignore my previous comment, though in any case we can still exhaust the 31 bits:

loop through:

Quoted 8 lines

add A
add B
remove A
add C
remove B
add A
remove C
add B

OK with me. I didn't change the previous behavior. But I'm fine with making it 64 bits. I will implement that and post an update.

-Alex

Quoted 84 lines

etc..

Quoted 37 lines
---
  drivers/block/rbd.c |   40 ++++++++++++++++++++++++++++++++++------
  1 files changed, 34 insertions(+), 6 deletions(-)
drivers/block/rbd.c +19 -1
index 042377b..4c5bb39 100644
@@ -2171,18 +2171,46 @@ static void rbd_id_get(struct rbd_device *rbd_dev)
  */
  static void rbd_id_put(struct rbd_device *rbd_dev)
  {
-       BUG_ON(rbd_dev->id<  1);
+       struct list_head *tmp;
+       int rbd_id = rbd_dev->id;
+       int max_id;
+
+       BUG_ON(rbd_id<  1);
 
        spin_lock(&rbd_dev_list_lock);
        list_del_init(&rbd_dev->node);
+
+       /*
+        * If the id being "put" is not the current maximum, there
+        * is nothing special we need to do.
+        */
+       if (rbd_id != atomic_read(&rbd_id_max)) {
+               spin_unlock(&rbd_dev_list_lock);
+               return;
+       }
+
+       /*
+        * We need to update the current maximum id.  Search the
+        * list to find out what it is.  We're more likely to find
+        * the maximum at the end, so search the list backward.

drivers/block/rbd.c static void rbd_id_get(struct rbd_device *rbd_dev) · line 2205

Quoted 10 lines
drivers/block/rbd.c +9 -0
+        */
+       max_id = 0;
+       list_for_each_prev(tmp,&rbd_dev_list) {
+               struct rbd_device *rbd_dev;
+
+               rbd_dev = list_entry(tmp, struct rbd_device, node);
+               if (rbd_id>  max_id)
+                       max_id = rbd_id;
+       }
        spin_unlock(&rbd_dev_list_lock);

So we still need to either use 64 bit counter, or think of a better scheme to allocate ids. Maybe using some sorted data structure here?

Quoted 31 lines
drivers/block/rbd.c +6 -6
        /*
-        * New id's are always one more than the current maximum.
-        * If the id being "put" *is* that maximum, decrement the
-        * maximum so the next one requested just reuses this one.
+        * The max id could have been updated by rbd_id_get(), in
+        * which case it now accurately reflects the new maximum.
+        * Be careful not to overwrite the maximum value in that
+        * case.
         */
-       atomic_cmpxchg(&rbd_id_max, rbd_dev->id, rbd_dev->id - 1);
+       atomic_cmpxchg(&rbd_id_max, rbd_id, max_id);
  }
 
  static ssize_t rbd_add(struct bus_type *bus,
@@ -2217,7 +2245,7 @@ static ssize_t rbd_add(struct bus_type *bus,
        INIT_LIST_HEAD(&rbd_dev->node);
        INIT_LIST_HEAD(&rbd_dev->snaps);
 
-       /* generate unique id: one more than highest used so far */
+       /* generate unique id: find highest unique id, add one */
        rbd_id_get(rbd_dev);
 
        /* parse add command */
--

1.7.5.4

--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Reviewed-by on the v1 series
#9 lore raw
In reply to Alex Elder

This series looks good.

Quoted 13 lines · Alex Elder

New rbd devices are granted a unique identifiers based on how many devices are already in existence. This series rearranges how that is done, switching from using a spinlock to using an atomic variable to select the next rbd id to use. In the process a bit of the code got a bit more isolated.

-Alex
--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html