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>
#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>
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;
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)
* 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;
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);
vds_io_free_pages(io);
+done:
+ vds_be_runlock(port);
return err;
}
*/
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;
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);
{
int i, rv;
bool iso;
+ enum {
+ none,
+ read,
+ write
+ } be_lock = none;
umode_t mode;
struct path path;
struct inode *inode;
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))
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");
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);
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);
}
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;
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);