]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - lib/idr.c
idr: fix obscure bug in allocation path
[mirror_ubuntu-artful-kernel.git] / lib / idr.c
index 305117ca2d415a1adf91700fe34a06d88028de67..7b5a59caa9891d74ebbec9e29fef35cd2ba7e3b0 100644 (file)
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -100,10 +100,11 @@ static int sub_alloc(struct idr *idp, void *ptr, int *starting_id)
        int n, m, sh;
        struct idr_layer *p, *new;
        struct idr_layer *pa[MAX_LEVEL];
-       int l, id;
+       int l, id, oid;
        long bm;
 
        id = *starting_id;
+ restart:
        p = idp->top;
        l = idp->layers;
        pa[l--] = NULL;
@@ -117,12 +118,23 @@ static int sub_alloc(struct idr *idp, void *ptr, int *starting_id)
                if (m == IDR_SIZE) {
                        /* no space available go back to previous layer. */
                        l++;
+                       oid = id;
                        id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;
+
+                       /* if already at the top layer, we need to grow */
                        if (!(p = pa[l])) {
                                *starting_id = id;
                                return -2;
                        }
-                       continue;
+
+                       /* If we need to go up one layer, continue the
+                        * loop; otherwise, restart from the top.
+                        */
+                       sh = IDR_BITS * (l + 1);
+                       if (oid >> sh == id >> sh)
+                               continue;
+                       else
+                               goto restart;
                }
                if (m != n) {
                        sh = IDR_BITS*l;