From: Bijan Mottahedeh Date: Thu, 5 May 2016 09:10:45 +0000 (-0700) Subject: sparc64: kernel panic -- vds_bh_reset X-Git-Tag: v4.1.12-92~74^2~20 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=199085e7e27afdd156bd137f60b354c61c34bba8;p=users%2Fjedix%2Flinux-maple.git sparc64: kernel panic -- vds_bh_reset 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 Reviewed-by: Liam Merwick (cherry picked from commit 6e33112afcdd654ada7c9414a1c4d83278533911) (cherry picked from commit e62908110662f009f2449df5faae496ac43a1d65) Signed-off-by: Allen Pais --- diff --git a/drivers/block/vds/vds.h b/drivers/block/vds/vds.h index ef1912759400..197eb1ab7e46 100644 --- a/drivers/block/vds/vds.h +++ b/drivers/block/vds/vds.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -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) diff --git a/drivers/block/vds/vds_io.c b/drivers/block/vds/vds_io.c index 7409e6b488f3..fee9a9f7aed7 100644 --- a/drivers/block/vds/vds_io.c +++ b/drivers/block/vds/vds_io.c @@ -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); } diff --git a/drivers/block/vds/vds_main.c b/drivers/block/vds/vds_main.c index 7b49bf931685..e67553a91475 100644 --- a/drivers/block/vds/vds_main.c +++ b/drivers/block/vds/vds_main.c @@ -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);