ptr_ring: support resizing multiple queues
authorMichael S. Tsirkin <mst@redhat.com>
Thu, 30 Jun 2016 06:45:33 +0000 (14:45 +0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 1 Jul 2016 09:32:17 +0000 (05:32 -0400)
Sometimes, we need support resizing multiple queues at once. This is
because it was not easy to recover to recover from a partial failure
of multiple queues resizing.

Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/ptr_ring.h
tools/virtio/ringtest/ptr_ring.c

index d78b8b89c70704582564d744c79095e793f74c9e..2052011bf9fbfb6ba92ac3da19a60f4292fb0108 100644 (file)
@@ -349,20 +349,14 @@ static inline int ptr_ring_init(struct ptr_ring *r, int size, gfp_t gfp)
        return 0;
 }
 
-static inline int ptr_ring_resize(struct ptr_ring *r, int size, gfp_t gfp,
-                                 void (*destroy)(void *))
+static inline void **__ptr_ring_swap_queue(struct ptr_ring *r, void **queue,
+                                          int size, gfp_t gfp,
+                                          void (*destroy)(void *))
 {
-       unsigned long flags;
        int producer = 0;
-       void **queue = __ptr_ring_init_queue_alloc(size, gfp);
        void **old;
        void *ptr;
 
-       if (!queue)
-               return -ENOMEM;
-
-       spin_lock_irqsave(&(r)->producer_lock, flags);
-
        while ((ptr = ptr_ring_consume(r)))
                if (producer < size)
                        queue[producer++] = ptr;
@@ -375,6 +369,23 @@ static inline int ptr_ring_resize(struct ptr_ring *r, int size, gfp_t gfp,
        old = r->queue;
        r->queue = queue;
 
+       return old;
+}
+
+static inline int ptr_ring_resize(struct ptr_ring *r, int size, gfp_t gfp,
+                                 void (*destroy)(void *))
+{
+       unsigned long flags;
+       void **queue = __ptr_ring_init_queue_alloc(size, gfp);
+       void **old;
+
+       if (!queue)
+               return -ENOMEM;
+
+       spin_lock_irqsave(&(r)->producer_lock, flags);
+
+       old = __ptr_ring_swap_queue(r, queue, size, gfp, destroy);
+
        spin_unlock_irqrestore(&(r)->producer_lock, flags);
 
        kfree(old);
@@ -382,6 +393,48 @@ static inline int ptr_ring_resize(struct ptr_ring *r, int size, gfp_t gfp,
        return 0;
 }
 
+static inline int ptr_ring_resize_multiple(struct ptr_ring **rings, int nrings,
+                                          int size,
+                                          gfp_t gfp, void (*destroy)(void *))
+{
+       unsigned long flags;
+       void ***queues;
+       int i;
+
+       queues = kmalloc(nrings * sizeof *queues, gfp);
+       if (!queues)
+               goto noqueues;
+
+       for (i = 0; i < nrings; ++i) {
+               queues[i] = __ptr_ring_init_queue_alloc(size, gfp);
+               if (!queues[i])
+                       goto nomem;
+       }
+
+       for (i = 0; i < nrings; ++i) {
+               spin_lock_irqsave(&(rings[i])->producer_lock, flags);
+               queues[i] = __ptr_ring_swap_queue(rings[i], queues[i],
+                                                 size, gfp, destroy);
+               spin_unlock_irqrestore(&(rings[i])->producer_lock, flags);
+       }
+
+       for (i = 0; i < nrings; ++i)
+               kfree(queues[i]);
+
+       kfree(queues);
+
+       return 0;
+
+nomem:
+       while (--i >= 0)
+               kfree(queues[i]);
+
+       kfree(queues);
+
+noqueues:
+       return -ENOMEM;
+}
+
 static inline void ptr_ring_cleanup(struct ptr_ring *r, void (*destroy)(void *))
 {
        void *ptr;
index 74abd746ae9145bc70365a46435aa00d4e4e1694..68e4f9f0da3abe48ea6e91cb3c8ce215bbd118c5 100644 (file)
 typedef pthread_spinlock_t  spinlock_t;
 
 typedef int gfp_t;
+static void *kmalloc(unsigned size, gfp_t gfp)
+{
+       return memalign(64, size);
+}
+
 static void *kzalloc(unsigned size, gfp_t gfp)
 {
        void *p = memalign(64, size);