lkml.org 
[lkml]   [2008]   [Dec]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[patch 021/104] lib/idr.c: fix rcu related race with idr_find
    2.6.27-stable review patch.  If anyone has any objections, please let us know.

    ------------------
    From: Manfred Spraul <manfred@colorfullife.com>

    commit 6ff2d39b91aec3dcae951afa982059e3dd9b49dc upstream.

    2nd part of the fixes needed for
    http://bugzilla.kernel.org/show_bug.cgi?id=11796.

    When the idr tree is either grown or shrunk, then the update to the number
    of layers and the top pointer were not atomic. This race caused crashes.

    The attached patch fixes that by replicating the layers counter in each
    layer, thus idr_find doesn't need idp->layers anymore.

    Signed-off-by: Manfred Spraul <manfred@colorfullife.com>
    Cc: Clement Calmels <cboulte@gmail.com>
    Cc: Nadia Derbey <Nadia.Derbey@bull.net>
    Cc: Pierre Peiffer <peifferp@gmail.com>
    Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

    ---
    include/linux/idr.h | 3 ++-
    lib/idr.c | 14 ++++++++++++--
    2 files changed, 14 insertions(+), 3 deletions(-)

    --- a/include/linux/idr.h
    +++ b/include/linux/idr.h
    @@ -52,13 +52,14 @@ struct idr_layer {
    unsigned long bitmap; /* A zero bit means "space here" */
    struct idr_layer *ary[1<<IDR_BITS];
    int count; /* When zero, we can release it */
    + int layer; /* distance from leaf */
    struct rcu_head rcu_head;
    };

    struct idr {
    struct idr_layer *top;
    struct idr_layer *id_free;
    - int layers;
    + int layers; /* only valid without concurrent changes */
    int id_free_cnt;
    spinlock_t lock;
    };
    --- a/lib/idr.c
    +++ b/lib/idr.c
    @@ -185,6 +185,7 @@ static int sub_alloc(struct idr *idp, in
    new = get_from_free_list(idp);
    if (!new)
    return -1;
    + new->layer = l-1;
    rcu_assign_pointer(p->ary[m], new);
    p->count++;
    }
    @@ -210,6 +211,7 @@ build_up:
    if (unlikely(!p)) {
    if (!(p = get_from_free_list(idp)))
    return -1;
    + p->layer = 0;
    layers = 1;
    }
    /*
    @@ -237,6 +239,7 @@ build_up:
    }
    new->ary[0] = p;
    new->count = 1;
    + new->layer = layers-1;
    if (p->bitmap == IDR_FULL)
    __set_bit(0, &new->bitmap);
    p = new;
    @@ -493,17 +496,21 @@ void *idr_find(struct idr *idp, int id)
    int n;
    struct idr_layer *p;

    - n = idp->layers * IDR_BITS;
    p = rcu_dereference(idp->top);
    + if (!p)
    + return NULL;
    + n = (p->layer+1) * IDR_BITS;

    /* Mask off upper bits we don't use for the search. */
    id &= MAX_ID_MASK;

    if (id >= (1 << n))
    return NULL;
    + BUG_ON(n == 0);

    while (n > 0 && p) {
    n -= IDR_BITS;
    + BUG_ON(n != p->layer*IDR_BITS);
    p = rcu_dereference(p->ary[(id >> n) & IDR_MASK]);
    }
    return((void *)p);
    @@ -582,8 +589,11 @@ void *idr_replace(struct idr *idp, void
    int n;
    struct idr_layer *p, *old_p;

    - n = idp->layers * IDR_BITS;
    p = idp->top;
    + if (!p)
    + return ERR_PTR(-EINVAL);
    +
    + n = (p->layer+1) * IDR_BITS;

    id &= MAX_ID_MASK;



    \
     
     \ /
      Last update: 2008-12-03 21:01    [W:4.187 / U:0.620 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site