drivers: staging: nvshm: fix spinlock deadlock
Martin Chabot [Fri, 12 Apr 2013 12:09:44 +0000 (14:09 +0200)]
Change to spinlock_irq to spinlock_irqsave/restore
This avoid re-enabling interrupt in critical section
resulting in a deadlock (seen in 3G transfert)

Bug 1270310

Change-Id: I3f0600ad7bf0bbdea44d0234e64fe5cceca9d577
Signed-off-by: Martin Chabot <mchabot@nvidia.com>
Reviewed-on: http://git-master/r/218914
Reviewed-by: Jean-Marc Guiraudet <jguiraudet@nvidia.com>
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Greg Heinrich <gheinrich@nvidia.com>
Reviewed-by: Julien Vuillaumier <jvuillaumier@nvidia.com>
Reviewed-by: Thomas Cherry <tcherry@nvidia.com>

drivers/staging/nvshm/nvshm_iobuf.c
drivers/staging/nvshm/nvshm_queue.c

index 17a19c8..d954132 100644 (file)
@@ -38,12 +38,13 @@ struct nvshm_iobuf *nvshm_iobuf_alloc(struct nvshm_channel *chan, int size)
 {
        struct nvshm_handle *handle = nvshm_get_handle();
        struct nvshm_iobuf *desc = NULL;
+       unsigned long f;
 
-       spin_lock_irq(&alloc.lock);
+       spin_lock_irqsave(&alloc.lock, f);
        if (alloc.free_pool_head) {
                if (size > (alloc.free_pool_head->totalLength -
                            NVSHM_DEFAULT_OFFSET)) {
-                       spin_unlock_irq(&alloc.lock);
+                       spin_unlock_irqrestore(&alloc.lock, f);
                        pr_err("%s: requested size (%d > %d) too big\n",
                               __func__,
                               size,
@@ -73,13 +74,13 @@ struct nvshm_iobuf *nvshm_iobuf_alloc(struct nvshm_channel *chan, int size)
                desc->ref = 1;
 
        } else {
-               spin_unlock_irq(&alloc.lock);
+               spin_unlock_irqrestore(&alloc.lock, f);
                pr_err("%s: no more alloc space\n", __func__);
                /* No error since it's only Xoff situation */
                return desc;
        }
 
-       spin_unlock_irq(&alloc.lock);
+       spin_unlock_irqrestore(&alloc.lock, f);
 
        return desc;
 }
@@ -89,6 +90,7 @@ void nvshm_iobuf_free(struct nvshm_iobuf *desc)
 {
        struct nvshm_handle *priv = nvshm_get_handle();
        int callback = 0, chan;
+       unsigned long f;
 
        if (desc->ref == 0) {
                pr_err("%s: freeing an already freed iobuf (0x%x)\n",
@@ -96,7 +98,7 @@ void nvshm_iobuf_free(struct nvshm_iobuf *desc)
                       (unsigned int)desc);
                return;
        }
-       spin_lock_irq(&alloc.lock);
+       spin_lock_irqsave(&alloc.lock, f);
        pr_debug("%s: free 0x%p ref %d pool %x\n", __func__,
                 desc, desc->ref, desc->pool_id);
        desc->ref--;
@@ -133,13 +135,13 @@ void nvshm_iobuf_free(struct nvshm_iobuf *desc)
                        desc->length = 0;
                        desc->dataOffset = 0;
                        desc->qnext = NULL;
-                       spin_unlock_irq(&alloc.lock);
+                       spin_unlock_irqrestore(&alloc.lock, f);
                        nvshm_queue_put(priv, desc);
                        nvshm_generate_ipc(priv);
                        return;
                }
        }
-       spin_unlock_irq(&alloc.lock);
+       spin_unlock_irqrestore(&alloc.lock, f);
        if (callback)
                nvshm_start_tx(&priv->chan[chan]);
 }
@@ -171,20 +173,22 @@ void nvshm_iobuf_free_cluster(struct nvshm_iobuf *list)
 int nvshm_iobuf_ref(struct nvshm_iobuf *iob)
 {
        int ref;
+       unsigned long f;
 
-       spin_lock_irq(&alloc.lock);
+       spin_lock_irqsave(&alloc.lock, f);
        ref = iob->ref++;
-       spin_unlock_irq(&alloc.lock);
+       spin_unlock_irqrestore(&alloc.lock, f);
        return ref;
 }
 
 int nvshm_iobuf_unref(struct nvshm_iobuf *iob)
 {
        int ref;
+       unsigned long f;
 
-       spin_lock_irq(&alloc.lock);
+       spin_lock_irqsave(&alloc.lock, f);
        ref = iob->ref--;
-       spin_unlock_irq(&alloc.lock);
+       spin_unlock_irqrestore(&alloc.lock, f);
        return ref;
 }
 
index 299292e..9d7b375 100644 (file)
@@ -124,7 +124,9 @@ struct nvshm_iobuf *nvshm_queue_get(struct nvshm_handle *handle)
 
 int nvshm_queue_put(struct nvshm_handle *handle, struct nvshm_iobuf *iob)
 {
-       spin_lock_irq(&handle->qlock);
+       unsigned long f;
+
+       spin_lock_irqsave(&handle->qlock, f);
        if (!handle->shared_queue_tail) {
                spin_unlock_irq(&handle->qlock);
                pr_err("%s: Queue not init!\n", __func__);
@@ -133,14 +135,14 @@ int nvshm_queue_put(struct nvshm_handle *handle, struct nvshm_iobuf *iob)
 
        if (!iob) {
                pr_err("%s: Queueing null pointer!\n", __func__);
-               spin_unlock_irq(&handle->qlock);
+               spin_unlock_irqrestore(&handle->qlock, f);
                return -EINVAL;
        }
 
        /* Sanity check */
        if (handle->shared_queue_tail->qnext) {
                pr_err("%s: illegal queue pointer detected!\n", __func__);
-               spin_unlock_irq(&handle->qlock);
+               spin_unlock_irqrestore(&handle->qlock, f);
                return -EINVAL;
        }
 
@@ -158,7 +160,7 @@ int nvshm_queue_put(struct nvshm_handle *handle, struct nvshm_iobuf *iob)
        FLUSH_CPU_DCACHE(handle->shared_queue_tail, sizeof(struct nvshm_iobuf));
        handle->shared_queue_tail = iob;
 
-       spin_unlock_irq(&handle->qlock);
+       spin_unlock_irqrestore(&handle->qlock, f);
        return 0;
 }