Skip to contentSkip to conversation

Auto-generated IDs

RFC PATCH Superseded Jeff Cody 3 participants 9 messages Sep 1, 2015

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Jeff Cody

author
#0 lore raw

cover Auto-generated IDs

Born from the conversation on qemu-devel, this generation scheme uses the format ultimately proposed by Kevin, after list discussion.

It attempts to keep the ID strings as small as possible, while fulfilling:

1.) Guarantee no collisions with a user-specified ID
2.) Identify the sub-system the ID belongs to
3.) Guarantee of uniqueness
4.) Spoiling predictibility, to avoid creating an assumption
of object ordering and parsing (i.e., we don't want users to think they can guess the next ID based on prior behavior).

See patch 1 for the generation scheme details.

Jeff Cody (2):
util - add automated ID generation utility
block: auto-generated node-names

 block.c               | 25 ++++++++++++++++---------
 include/qemu-common.h |  8 ++++++++
 util/id.c             | 35 +++++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+), 9 deletions(-)
Signature · 1 line
1.9.3
author
#1 lore raw

RFC PATCH 1/2 util - add automated ID generation utility

Multiple sub-systems in QEMU may find it useful to generated IDs for objects that a user may reference via QMP or HMP. This patch presents a standardized way to do it, so that automatic ID generation follows the same rules.

This patch enforces the following rules when generating an ID:

1.) Guarantee no collisions with a user-specified ID
2.) Identify the sub-system the ID belongs to
3.) Guarantee of uniqueness
4.) Spoiling predictibility, to avoid creating an assumption
of object ordering and parsing (i.e., we don't want users to think they can guess the next ID based on prior behavior).

The scheme for this is as follows (no spaces):

                # subsys D RR
Reserved char --|    |   | |
Subsytem String -----|   | |
Unique number (64-bit) --| |
Two-digit random number ---|

For example, a generated node-name for the block sub-system may take the look like this:

    #block076

The caller of id_generate() is responsible for freeing the generated node name string with g_free().

---
 include/qemu-common.h |  8 ++++++++
 util/id.c             | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+)
include/qemu-common.h +8 -0
index bbaffd1..f6b0105 100644
@@ -237,6 +237,14 @@ int64_t strtosz_suffix_unit(const char *nptr, char **end,
 #define STR_OR_NULL(str) ((str) ? (str) : "null")
 
 /* id.c */
+
+typedef enum IdSubSystems {
+    ID_QDEV,
+    ID_BLOCK,
+    ID_MAX      /* last element, used as array size */
+} IdSubSystems;
+
+char *id_generate(IdSubSystems);
 bool id_wellformed(const char *id);
 
 /* path.c */
util/id.c +35 -0
index 09b22fb..48e2935 100644
@@ -26,3 +26,38 @@ bool id_wellformed(const char *id)
     }
     return true;
 }
+
+#define ID_SPECIAL_CHAR '#'
+
+/* Generates an ID of the form:
+ *
+ * "#block146",
+ *
+ *  where:
+ *      - "#" is always the reserved character '#'
+ *      - "block" refers to the subsystem identifed via IdSubSystems
+ *        and id_subsys_str[]
+ *      - "1" is a unique number (up to a uint64_t) for the subsystem,
+ *      - "46" is a pseudo-random numer to create uniqueness
+ *
+ * The caller is responsible for freeing the returned string with g_free()
+ */
+char *id_generate(IdSubSystems id)
+{
+    const char *id_subsys_str[] = {
+        [ID_QDEV]  = "qdev",
+        [ID_BLOCK] = "block",
+    };
+
+    static uint64_t id_counters[ID_MAX];
+    uint32_t rnd;
+
+    assert(id < ID_MAX);
+
+    rnd = g_random_int_range(0, 99);
+
+    return g_strdup_printf("%c%s%" PRIu64 "%" PRId32, ID_SPECIAL_CHAR,
+                                                      id_subsys_str[id],
+                                                      id_counters[id]++,
+                                                      rnd);
+}
Signature · 1 line
1.9.3
author
#6 lore raw

RFC PATCH 2/2 block: auto-generated node-names

In reply to Jeff Cody

If a node-name is not specified, automatically generate the node-name.

Generated node-names will use the "block" sub-system identifier.

---
 block.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)
block.c +16 -9
index d088ee0..892e127 100644
@@ -771,32 +771,39 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
                                   const char *node_name,
                                   Error **errp)
 {
+    char *gen_node_name = NULL;
+
     if (!node_name) {
-        return;
-    }
-
-    /* Check for empty string or invalid characters */
-    if (!id_wellformed(node_name)) {
-        error_setg(errp, "Invalid node name");
-        return;
+        gen_node_name = id_generate(ID_BLOCK);
+        node_name = gen_node_name;
+    } else {
+        /* Check for empty string or invalid characters, but not if it is
+         * generated (generated names use characters not available to the user)
+         * */
+        if (!id_wellformed(node_name)) {
+            error_setg(errp, "Invalid node name");
+            return;
+        }
     }
 
     /* takes care of avoiding namespaces collisions */
     if (blk_by_name(node_name)) {
         error_setg(errp, "node-name=%s is conflicting with a device id",
                    node_name);
-        return;
+        goto out;
     }
 
     /* takes care of avoiding duplicates node names */
     if (bdrv_find_node(node_name)) {
         error_setg(errp, "Duplicate node name");
-        return;
+        goto out;
     }
 
     /* copy node name into the bs and insert it into the graph list */
     pstrcpy(bs->node_name, sizeof(bs->node_name), node_name);
     QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
+out:
+    g_free(gen_node_name);
 }
 
 static QemuOptsList bdrv_runtime_opts = {
Signature · 1 line
1.9.3
#2 lore raw

Re: [Qemu-devel] [RFC PATCH 1/2] util - add automated ID generation utility

In reply to Jeff Cody
Quoted 14 lines · Jeff Cody

Multiple sub-systems in QEMU may find it useful to generated IDs for objects that a user may reference via QMP or HMP. This patch presents a standardized way to do it, so that automatic ID generation follows the same rules.

This patch enforces the following rules when generating an ID:

1.) Guarantee no collisions with a user-specified ID
2.) Identify the sub-system the ID belongs to
3.) Guarantee of uniqueness
4.) Spoiling predictibility, to avoid creating an assumption
of object ordering and parsing (i.e., we don't want users to think they can guess the next ID based on prior behavior).

Quoted 5 lines

The scheme for this is as follows (no spaces):

                # subsys D RR
Reserved char --|    |   | |
Subsytem String -----|   | |

s/Subsytem/Subsystem/

Quoted 5 lines

Unique number (64-bit) --| |
Two-digit random number ---|

For example, a generated node-name for the block sub-system may take the look like this:

s/take the//

Quoted 13 lines
    #block076

The caller of id_generate() is responsible for freeing the generated node name string with g_free().

---
 include/qemu-common.h |  8 ++++++++
 util/id.c             | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+)

util/id.c bool id_wellformed(const char *id) · line 47

Quoted 3 lines
util/id.c +3 -0
+char *id_generate(IdSubSystems id)
+{
+    const char *id_subsys_str[] = {

s/id_/const id_/

util/id.c bool id_wellformed(const char *id) · line 50

Quoted 3 lines
util/id.c +3 -0
+        [ID_QDEV]  = "qdev",
+        [ID_BLOCK] = "block",
+    };

Do we want some sort of compile-time assertion that we have entries for all id values?...

util/id.c bool id_wellformed(const char *id) · line 55

Quoted 5 lines
util/id.c +5 -0
+
+    static uint64_t id_counters[ID_MAX];
+    uint32_t rnd;
+
+    assert(id < ID_MAX);

...maybe in the form of assert(id_subsys_str[id])

util/id.c bool id_wellformed(const char *id) · line 63

Quoted 9 lines
util/id.c +8 -0
+
+    rnd = g_random_int_range(0, 99);
+
+    return g_strdup_printf("%c%s%" PRIu64 "%" PRId32, ID_SPECIAL_CHAR,
+                                                      id_subsys_str[id],
+                                                      id_counters[id]++,
+                                                      rnd);
+}

Looks reasonable to me.

Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
Reviewed-by on v1 2/2
#7 lore raw

Re: [Qemu-devel] [RFC PATCH 2/2] block: auto-generated node-names

In reply to Jeff Cody
Quoted 8 lines · Jeff Cody

If a node-name is not specified, automatically generate the node-name.

Generated node-names will use the "block" sub-system identifier.

---
 block.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)
Signature · 2 lines
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org
#4 lore raw

Re: [Qemu-devel] [RFC PATCH 1/2] util - add automated ID generation utility

In reply to Jeff Cody
Quoted 1 line · Jeff Cody

Multiple sub-systems in QEMU may find it useful to generated IDs

generate

Quoted 10 lines

for objects that a user may reference via QMP or HMP. This patch presents a standardized way to do it, so that automatic ID generation follows the same rules.

This patch enforces the following rules when generating an ID:

1.) Guarantee no collisions with a user-specified ID 2.) Identify the sub-system the ID belongs to
3.) Guarantee of uniqueness
4.) Spoiling predictibility, to avoid creating an assumption

predictability

Quoted 8 lines

of object ordering and parsing (i.e., we don't want users to think they can guess the next ID based on prior behavior).

The scheme for this is as follows (no spaces):

                # subsys D RR
Reserved char --|    |   | |
Subsytem String -----|   | |

Subsystem

Quoted 6 lines

Unique number (64-bit) --| |
Two-digit random number ---|

For example, a generated node-name for the block sub-system may take the look like this:

"take this form" or "look like this"

Quoted 51 lines
    #block076

The caller of id_generate() is responsible for freeing the generated node name string with g_free().

---
 include/qemu-common.h |  8 ++++++++
 util/id.c             | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+)
include/qemu-common.h +8 -0
index bbaffd1..f6b0105 100644
@@ -237,6 +237,14 @@ int64_t strtosz_suffix_unit(const char *nptr, char **end,
 #define STR_OR_NULL(str) ((str) ? (str) : "null")
 
 /* id.c */
+
+typedef enum IdSubSystems {
+    ID_QDEV,
+    ID_BLOCK,
+    ID_MAX      /* last element, used as array size */
+} IdSubSystems;
+
+char *id_generate(IdSubSystems);
 bool id_wellformed(const char *id);
 
 /* path.c */
util/id.c +13 -0
index 09b22fb..48e2935 100644
@@ -26,3 +26,38 @@ bool id_wellformed(const char *id)
     }
     return true;
 }
+
+#define ID_SPECIAL_CHAR '#'
+
+/* Generates an ID of the form:
+ *
+ * "#block146",
+ *
+ *  where:
+ *      - "#" is always the reserved character '#'
+ *      - "block" refers to the subsystem identifed via IdSubSystems
+ *        and id_subsys_str[]
+ *      - "1" is a unique number (up to a uint64_t) for the subsystem,
+ *      - "46" is a pseudo-random numer to create uniqueness

util/id.c bool id_wellformed(const char *id) · line 51

Quoted 10 lines
util/id.c +10 -0
+ *
+ * The caller is responsible for freeing the returned string with g_free()
+ */
+char *id_generate(IdSubSystems id)
+{
+    const char *id_subsys_str[] = {
+        [ID_QDEV]  = "qdev",
+        [ID_BLOCK] = "block",
+    };
+

Do we want this local to this function? A lookup table may be useful for utilities at some point.

util/id.c bool id_wellformed(const char *id) · line 63

Quoted 13 lines
util/id.c +12 -0
+    static uint64_t id_counters[ID_MAX];
+    uint32_t rnd;
+
+    assert(id < ID_MAX);
+
+    rnd = g_random_int_range(0, 99);
+
+    return g_strdup_printf("%c%s%" PRIu64 "%" PRId32, ID_SPECIAL_CHAR,
+                                                      id_subsys_str[id],
+                                                      id_counters[id]++,
+                                                      rnd);
+}

So basically, it's #<sys><counter><rnd>

So we could see:

|block|1|32|

For the block subsystem, 1st device, salt is 3.
But we could also see:

|block|13|2|

Block subsys, 13th device, salt is 2.

Forcing a zero-pad on the salt should be enough to disambiguate in all cases:

block132
block1302

This way, the last two digits are *always* salt, making the ID unambiguous and, I think, impossible to collide against regardless of that the rng returns in the future for new IDs.

author
#3 lore raw

Re: [Qemu-devel] [RFC PATCH 1/2] util - add automated ID generation utility

In reply to Eric Blake
Quoted 44 lines · Eric Blake
Quoted 14 lines · Jeff Cody

Multiple sub-systems in QEMU may find it useful to generated IDs for objects that a user may reference via QMP or HMP. This patch presents a standardized way to do it, so that automatic ID generation follows the same rules.

This patch enforces the following rules when generating an ID:

1.) Guarantee no collisions with a user-specified ID
2.) Identify the sub-system the ID belongs to
3.) Guarantee of uniqueness
4.) Spoiling predictibility, to avoid creating an assumption
of object ordering and parsing (i.e., we don't want users to think they can guess the next ID based on prior behavior).

Quoted 5 lines

The scheme for this is as follows (no spaces):

                # subsys D RR
Reserved char --|    |   | |
Subsytem String -----|   | |

s/Subsytem/Subsystem/

Quoted 5 lines

Unique number (64-bit) --| |
Two-digit random number ---|

For example, a generated node-name for the block sub-system may take the look like this:

s/take the//

Quoted 12 lines
    #block076

The caller of id_generate() is responsible for freeing the generated node name string with g_free().

---
 include/qemu-common.h |  8 ++++++++
 util/id.c             | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+)
Quoted 6 lines

util/id.c bool id_wellformed(const char *id) · line 47

Quoted 3 lines
util/id.c +3 -0
+char *id_generate(IdSubSystems id)
+{
+    const char *id_subsys_str[] = {

s/id_/const id_/

Good point.

Quoted 7 lines

util/id.c bool id_wellformed(const char *id) · line 50

Quoted 3 lines
util/id.c +3 -0
+        [ID_QDEV]  = "qdev",
+        [ID_BLOCK] = "block",
+    };

Do we want some sort of compile-time assertion that we have entries for all id values?...

Quoted 8 lines

util/id.c bool id_wellformed(const char *id) · line 55

Quoted 5 lines
util/id.c +5 -0
+
+    static uint64_t id_counters[ID_MAX];
+    uint32_t rnd;
+
+    assert(id < ID_MAX);

...maybe in the form of assert(id_subsys_str[id])

Yes, I think we do. If one is missing, that is certainly a mistake, and we run the risk of collisions as well.

Quoted 13 lines

util/id.c bool id_wellformed(const char *id) · line 63

Quoted 10 lines
util/id.c +8 -0
+
+    rnd = g_random_int_range(0, 99);
+
+    return g_strdup_printf("%c%s%" PRIu64 "%" PRId32, ID_SPECIAL_CHAR,
+                                                      id_subsys_str[id],
+                                                      id_counters[id]++,
+                                                      rnd);
+}

Looks reasonable to me.

Thanks

-Jeff

Reviewed-by on v1 2/2
#8 lore raw

Re: [Qemu-devel] [RFC PATCH 2/2] block: auto-generated node-names

In reply to Jeff Cody
Quoted 53 lines · Jeff Cody

If a node-name is not specified, automatically generate the node-name.

Generated node-names will use the "block" sub-system identifier.

---
 block.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)
block.c +14 -9
index d088ee0..892e127 100644
@@ -771,32 +771,39 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
                                   const char *node_name,
                                   Error **errp)
 {
+    char *gen_node_name = NULL;
+
     if (!node_name) {
-        return;
-    }
-
-    /* Check for empty string or invalid characters */
-    if (!id_wellformed(node_name)) {
-        error_setg(errp, "Invalid node name");
-        return;
+        gen_node_name = id_generate(ID_BLOCK);
+        node_name = gen_node_name;
+    } else {
+        /* Check for empty string or invalid characters, but not if it is
+         * generated (generated names use characters not available to the user)
+         * */
+        if (!id_wellformed(node_name)) {
+            error_setg(errp, "Invalid node name");
+            return;
+        }
     }
 
     /* takes care of avoiding namespaces collisions */
     if (blk_by_name(node_name)) {
         error_setg(errp, "node-name=%s is conflicting with a device id",
                    node_name);
-        return;
+        goto out;
     }
 
     /* takes care of avoiding duplicates node names */
     if (bdrv_find_node(node_name)) {
         error_setg(errp, "Duplicate node name");
-        return;
+        goto out;

block.c static void bdrv_assign_node_name(BlockDriverState *bs, · line 809

Quoted 10 lines
block.c +2 -0
     }
 
     /* copy node name into the bs and insert it into the graph list */
     pstrcpy(bs->node_name, sizeof(bs->node_name), node_name);
     QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
+out:
+    g_free(gen_node_name);
 }
 
 static QemuOptsList bdrv_runtime_opts = {
author
#5 lore raw

Re: [Qemu-devel] [RFC PATCH 1/2] util - add automated ID generation utility

In reply to John Snow
Quoted 31 lines · John Snow
Quoted 1 line · Jeff Cody

Multiple sub-systems in QEMU may find it useful to generated IDs

generate

Quoted 10 lines

for objects that a user may reference via QMP or HMP. This patch presents a standardized way to do it, so that automatic ID generation follows the same rules.

This patch enforces the following rules when generating an ID:

1.) Guarantee no collisions with a user-specified ID 2.) Identify the sub-system the ID belongs to
3.) Guarantee of uniqueness
4.) Spoiling predictibility, to avoid creating an assumption

predictability

Quoted 8 lines

of object ordering and parsing (i.e., we don't want users to think they can guess the next ID based on prior behavior).

The scheme for this is as follows (no spaces):

                # subsys D RR
Reserved char --|    |   | |
Subsytem String -----|   | |

Subsystem

Quoted 9 lines
Quoted 6 lines

Unique number (64-bit) --| |
Two-digit random number ---|

For example, a generated node-name for the block sub-system may take the look like this:

"take this form" or "look like this"

All I can say is, sometimes my fingers don't obey my brain.

Quoted 56 lines
Quoted 56 lines
    #block076

The caller of id_generate() is responsible for freeing the generated node name string with g_free().

---
 include/qemu-common.h |  8 ++++++++
 util/id.c             | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 43 insertions(+)
include/qemu-common.h +8 -0
index bbaffd1..f6b0105 100644
@@ -237,6 +237,14 @@ int64_t strtosz_suffix_unit(const char *nptr, char **end,
 #define STR_OR_NULL(str) ((str) ? (str) : "null")
 
 /* id.c */
+
+typedef enum IdSubSystems {
+    ID_QDEV,
+    ID_BLOCK,
+    ID_MAX      /* last element, used as array size */
+} IdSubSystems;
+
+char *id_generate(IdSubSystems);
 bool id_wellformed(const char *id);
 
 /* path.c */
util/id.c +18 -0
index 09b22fb..48e2935 100644
@@ -26,3 +26,38 @@ bool id_wellformed(const char *id)
     }
     return true;
 }
+
+#define ID_SPECIAL_CHAR '#'
+
+/* Generates an ID of the form:
+ *
+ * "#block146",
+ *
+ *  where:
+ *      - "#" is always the reserved character '#'
+ *      - "block" refers to the subsystem identifed via IdSubSystems
+ *        and id_subsys_str[]
+ *      - "1" is a unique number (up to a uint64_t) for the subsystem,
+ *      - "46" is a pseudo-random numer to create uniqueness
+ *
+ * The caller is responsible for freeing the returned string with g_free()
+ */
+char *id_generate(IdSubSystems id)
+{
Quoted 9 lines

util/id.c bool id_wellformed(const char *id) · line 51

Quoted 5 lines
util/id.c +5 -0
+    const char *id_subsys_str[] = {
+        [ID_QDEV]  = "qdev",
+        [ID_BLOCK] = "block",
+    };
+

Do we want this local to this function? A lookup table may be useful for utilities at some point.

Possibly. I'm neutral, we can move it out of the function and make it static.

Quoted 30 lines

util/id.c bool id_wellformed(const char *id) · line 63

Quoted 13 lines
util/id.c +12 -0
+    static uint64_t id_counters[ID_MAX];
+    uint32_t rnd;
+
+    assert(id < ID_MAX);
+
+    rnd = g_random_int_range(0, 99);
+
+    return g_strdup_printf("%c%s%" PRIu64 "%" PRId32, ID_SPECIAL_CHAR,
+                                                      id_subsys_str[id],
+                                                      id_counters[id]++,
+                                                      rnd);
+}

So basically, it's #<sys><counter><rnd>

So we could see:

|block|1|32|

For the block subsystem, 1st device, salt is 3.
But we could also see:

|block|13|2|

Block subsys, 13th device, salt is 2.

Forcing a zero-pad on the salt should be enough to disambiguate in all cases:

Quoted 6 lines

block132
block1302

This way, the last two digits are *always* salt, making the ID unambiguous and, I think, impossible to collide against regardless of that the rng returns in the future for new IDs.

Yes - that is actually what I meant to do. We definitely want to enforce two digits for the random element.

Thanks,

Jeff