]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sparc64: kernel panic -- vds_bh_reset
authorBijan Mottahedeh <bijan.mottahedeh@oracle.com>
Thu, 5 May 2016 09:10:45 +0000 (02:10 -0700)
committerAllen Pais <allen.pais@oracle.com>
Thu, 15 Sep 2016 06:35:18 +0000 (12:05 +0530)
Orabug: 23199936

The panic is an assertion failure in fs/buffer.c:1269

static inline void check_irqs_on(void)
{
         *** BUG_ON(irqs_disabled()); ***
}

The vds reset path calls the backend fini routine which eventually calls
the file close interface:

         vds_vio_lock(vio, flags);
         vds_be_fini(port);

vds_vio_lock() grabs a spin lock and disables local irqs and thus
the eventual assertion failure.

The fix is to add a new r/w mutex to protect backend state and move the
vds_vio_lock() call after vds_be_fini().

Signed-off-by: Bijan Mottahedeh <bijan.mottahedeh@oracle.com>
Reviewed-by: Liam Merwick <Liam.Merwick@oracle.com>
(cherry picked from commit 6e33112afcdd654ada7c9414a1c4d83278533911)
(cherry picked from commit e62908110662f009f2449df5faae496ac43a1d65)
Signed-off-by: Allen Pais <allen.pais@oracle.com>
drivers/block/vds/vds.h
drivers/block/vds/vds_io.c
drivers/block/vds/vds_main.c

index ef191275940005ba94512409864cedcad866243b..197eb1ab7e462269ddf318f5954f4a72ea8a4073 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/device-mapper.h>
 #include <linux/sysfs.h>
 #include <linux/wait.h>
+#include <linux/rwsem.h>
 
 #include <asm/vio.h>
 #include <asm/ldc.h>
@@ -45,6 +46,7 @@ struct vds_port {
        u64                     seq;
        const char              *path;
        void                    *msgbuf;
+       struct rw_semaphore     be_lock;
        struct vds_be_ops       *be_ops;        /* backend ops */
        void                    *be_data;
        struct mutex            label_lock;
@@ -102,15 +104,49 @@ int vds_vtoc_get(struct vds_port *port);
 int vds_vtoc_set(struct vds_port *port, struct vio_disk_vtoc *vtoc);
 int vds_vtoc_clear(struct vds_port *port);
 
+/*
+ * Backend r/w lock blocks changes to the backend state
+ * while there are outstanding IO operations against that backend.
+ */
+#define        vds_be_rlock(p)                                         \
+       do {                                                    \
+               vdsdbg(LOCK, "backend rlock\n");                \
+               down_read(&(p)->be_lock);                       \
+       } while (0)
+
+#define        vds_be_runlock(p)                                       \
+       do {                                                    \
+               vdsdbg(LOCK, "backend runlock\n");              \
+               up_read(&(p)->be_lock);                         \
+       } while (0)
+
+#define        vds_be_wlock(p)                                         \
+       do {                                                    \
+               vdsdbg(LOCK, "backend wlock\n");                \
+               down_write(&(p)->be_lock);                      \
+       } while (0)
+
+#define        vds_be_wunlock(p)                                       \
+       do {                                                    \
+               vdsdbg(LOCK, "backend wunlock\n");              \
+               up_write(&(p)->be_lock);                        \
+       } while (0)
+
+#define        vds_be_dglock(p)                                        \
+       do {                                                    \
+               vdsdbg(LOCK, "backend downgrade lock\n");       \
+               downgrade_write(&(p)->be_lock);                 \
+       } while (0)
+
 #define        vds_label_lock(p, v)                                    \
        do {                                                    \
-               vdsdbg(LOCK, "label lock\n");   \
+               vdsdbg(LOCK, "label lock\n");                   \
                mutex_lock(&(p)->label_lock);                   \
        } while (0)
 
 #define        vds_label_unlock(p, v)                                  \
        do {                                                    \
-               vdsdbg(LOCK, "label unlock\n"); \
+               vdsdbg(LOCK, "label unlock\n");                 \
                mutex_unlock(&(p)->label_lock);                 \
        } while (0)
 
index 7409e6b488f3ebd63b0ea6ab21da3119dbcff537..fee9a9f7aed771edd5a576e09972e5b6af23aa41 100644 (file)
@@ -221,20 +221,23 @@ void vds_io_done(struct vds_io *io)
         * This means that any request which was scheduled from the
         * kernel workqueue AFTER the reset will execute but no response
         * will be sent to the client.
+        *
+        * The reset can be initiated by an explicit incoming request
+        * or while processing an IO request.  Wakeup anyone waiting on
+        * the IO list in either case.
         */
        vds_vio_lock(vio, flags);
        list_del(&io->list);
        if (io->flags & VDS_IO_FINI)
                INIT_LIST_HEAD(&port->io_list);
-       else
-               wake_up(&port->wait);
+       wake_up(&port->wait);
        vds_vio_unlock(vio, flags);
        vds_io_free(io);
 }
 
 static int vds_io_rw(struct vds_io *io)
 {
-       int err;
+       int err = 0;
        void *buf;
        unsigned long len;
        struct vio_driver_state *vio = io->vio;
@@ -246,13 +249,17 @@ static int vds_io_rw(struct vds_io *io)
        if (!to_sector(io->size))
                return -EINVAL;
 
-       if (!port->be_ops)
-               return -EIO;
+       vds_be_rlock(port);
+
+       if (!port->be_ops) {
+               err = -EIO;
+               goto done;
+       }
 
        len = (unsigned long)roundup(io->size, PAGE_SIZE);
        err = vds_io_alloc_pages(io, len);
        if (err)
-               return err;
+               goto done;
 
        buf = page_address(io->pages);
 
@@ -269,6 +276,8 @@ static int vds_io_rw(struct vds_io *io)
 
        vds_io_free_pages(io);
 
+done:
+       vds_be_runlock(port);
        return err;
 }
 
@@ -517,10 +526,12 @@ int vd_op_flush(struct vds_io *io)
         */
        vdsdbg(FLUSH, "VD_OP_FLUSH\n");
        vds_io_wait(io);
+       vds_be_rlock(port);
        if (port->be_ops)
                rv = port->be_ops->flush(port);
        else
                rv = -EIO;
+       vds_be_runlock(port);
 
        vdsdbg(FLUSH, "VD_OP_FLUSH rv=%d\n", rv);
        return rv;
@@ -594,10 +605,12 @@ int vd_op_rw(struct vds_io *io)
        io->size = desc->size;
        io->offset = offset;
 
+       vds_be_rlock(port);
        if (port->be_ops)
                err = port->be_ops->rw(io);
        else
                err = -EIO;
+       vds_be_runlock(port);
 
        if (!err && !(io->rw & WRITE))
                err = vds_copy(vio, LDC_COPY_OUT, buf, desc, 0, 0);
@@ -619,6 +632,11 @@ int vds_be_init(struct vds_port *port)
 {
        int i, rv;
        bool iso;
+       enum {
+               none,
+               read,
+               write
+       } be_lock = none;
        umode_t mode;
        struct path path;
        struct inode *inode;
@@ -632,6 +650,8 @@ int vds_be_init(struct vds_port *port)
        mode = inode->i_mode;
        path_put(&path);
 
+       vds_be_wlock(port);
+       be_lock = write;
        if (S_ISREG(mode))
                port->be_ops = vds_reg_get_ops();
        else if (S_ISBLK(mode))
@@ -655,6 +675,13 @@ int vds_be_init(struct vds_port *port)
                goto done;
        }
 
+       /*
+        * Downgrade to a read lock since label operations use backend
+        * IO routines which grab the read lock.
+        */
+       vds_be_dglock(port);
+       be_lock = read;
+
        rv = vds_label_chk_iso(port, &iso);
        if (rv) {
                vdsmsg(err, "media check error\n");
@@ -676,6 +703,10 @@ int vds_be_init(struct vds_port *port)
        vds_label_init(port);
 
 done:
+       if (be_lock == read)
+               vds_be_runlock(port);
+       else if (be_lock == write)
+               vds_be_wunlock(port);
        if (rv)
                vdsmsg(err, "%s: init failed (%d)\n", port->path, rv);
 
@@ -684,9 +715,13 @@ done:
 
 void vds_be_fini(struct vds_port *port)
 {
+       struct vio_driver_state *vio = &port->vio;
+
        vds_label_fini(port);
+       vds_be_wlock(port);
        if (port->be_ops) {
                port->be_ops->fini(port);
                port->be_data = NULL;
        }
+       vds_be_wunlock(port);
 }
index 7b49bf931685ba22a886cd509da60da386ed534b..e67553a91475ab9c842ed5cfc3883738d6318538 100644 (file)
@@ -453,9 +453,9 @@ static void vds_reset(struct vds_io *io)
 
        io->flags |= VDS_IO_FINI;
 
-       vds_vio_lock(vio, flags);
        vds_be_fini(port);
 
+       vds_vio_lock(vio, flags);
        vio_link_state_change(vio, LDC_EVENT_RESET);
        vio->desc_buf_len = 0;
 
@@ -895,6 +895,7 @@ static int vds_port_probe(struct vio_dev *vdev, const struct vio_device_id *id)
        INIT_LIST_HEAD(&port->io_list);
        init_waitqueue_head(&port->wait);
 
+       init_rwsem(&port->be_lock);
        mutex_init(&port->label_lock);
 
        dev_set_drvdata(&vdev->dev, port);