From: "Michael S. Tsirkin" <m...@redhat.com>

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 <m...@redhat.com>
Signed-off-by: Jason Wang <jasow...@redhat.com>
---
 include/linux/ptr_ring.h         | 71 +++++++++++++++++++++++++++++++++++-----
 tools/virtio/ringtest/ptr_ring.c |  5 +++
 2 files changed, 67 insertions(+), 9 deletions(-)

diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h
index d78b8b8..2052011 100644
--- a/include/linux/ptr_ring.h
+++ b/include/linux/ptr_ring.h
@@ -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;
diff --git a/tools/virtio/ringtest/ptr_ring.c b/tools/virtio/ringtest/ptr_ring.c
index 74abd74..68e4f9f 100644
--- a/tools/virtio/ringtest/ptr_ring.c
+++ b/tools/virtio/ringtest/ptr_ring.c
@@ -17,6 +17,11 @@
 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);
-- 
2.7.4

Reply via email to