scst_vdisk: Avoid that LUN refresh triggers a general protection fault

Avoid that triggering LUN referesh concurrently with device deletion
triggers the following:

general protection fault: 0000 [#1]
Workqueue: events vdev_inq_changed_fn [scst_vdisk]
Call Trace:
 _raw_spin_lock_bh+0x2b/0x30
 scst_cm_update_dev+0x87/0x190 [scst]
 scst_dev_inquiry_data_changed+0xfb/0x1b0 [scst]
 vdev_inq_changed_fn+0x60/0x120 [scst_vdisk]
 process_one_work+0x14d/0x410
 worker_thread+0x66/0x460
 kthread+0xdb/0x100
 ret_from_fork+0x3f/0x70

Reported-by: Jinpu Wang <jinpu.wang@profitbricks.com>
Tested-by: Jinpu Wang <jinpu.wang@profitbricks.com>


git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@7101 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Bart Van Assche
2017-03-01 15:31:59 +00:00
parent 08f2570761
commit 0bb6de9471
5 changed files with 39 additions and 12 deletions

View File

@@ -3766,7 +3766,10 @@ static inline int scst_register_virtual_device(struct scst_dev_type *dev_handler
return scst_register_virtual_device_node(dev_handler, dev_name,
NUMA_NO_NODE);
}
void scst_unregister_virtual_device(int id);
void scst_unregister_virtual_device(int id,
void (*on_free)(struct scst_device *dev,
void *arg),
void *arg);
/*
* Get/Set functions for tgt's sg_tablesize

View File

@@ -3518,7 +3518,7 @@ out:
return res;
out_unreg_drv:
scst_unregister_virtual_device(dev->virt_id);
scst_unregister_virtual_device(dev->virt_id, NULL, NULL);
out_unreg_handler:
scst_unregister_virtual_dev_driver(&dev->devtype);
@@ -3887,7 +3887,7 @@ static int dev_user_exit_dev(struct scst_user_dev *dev)
wake_up(&cleanup_list_waitQ);
scst_unregister_virtual_device(dev->virt_id);
scst_unregister_virtual_device(dev->virt_id, NULL, NULL);
scst_unregister_virtual_dev_driver(&dev->devtype);
sgv_pool_flush(dev->pool_clust);

View File

@@ -353,6 +353,7 @@ static enum compl_status_e vdisk_exec_write_same(struct vdisk_cmd_params *p);
static int vdisk_fsync(loff_t loff,
loff_t len, struct scst_device *dev, gfp_t gfp_flags,
struct scst_cmd *cmd, bool async);
static void vdev_on_free(struct scst_device *dev, void *arg);
#ifdef CONFIG_SCST_PROC
static int vdisk_read_proc(struct seq_file *seq,
struct scst_dev_type *dev_type);
@@ -7875,8 +7876,6 @@ static inline int vdev_create(struct scst_dev_type *devt,
static void vdev_destroy(struct scst_vdisk_dev *virt_dev)
{
cancel_work_sync(&virt_dev->vdev_inq_changed_work);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
vdisk_free_bioset(virt_dev);
#endif
@@ -8403,12 +8402,27 @@ out:
#endif /* CONFIG_SCST_PROC */
static void vdev_on_free(struct scst_device *dev, void *arg)
{
struct scst_vdisk_dev *virt_dev = arg;
TRACE_DBG("%s(%s)", __func__, dev->virt_name ? : "(?)");
/*
* This call must happen after scst_unregister_virtual_device()
* has called scst_dev_sysfs_del() and before scst_free_device()
* starts deallocating *dev.
*/
cancel_work_sync(&virt_dev->vdev_inq_changed_work);
}
/* scst_vdisk_mutex supposed to be held */
static void vdev_del_device(struct scst_vdisk_dev *virt_dev)
{
TRACE_ENTRY();
scst_unregister_virtual_device(virt_dev->virt_id);
scst_unregister_virtual_device(virt_dev->virt_id, vdev_on_free,
virt_dev);
list_del(&virt_dev->vdev_list_entry);

View File

@@ -2345,7 +2345,7 @@ static void scst_cm_init_inq_finish(struct scst_cmd *cmd)
/* cmd->dev can be NULL here! */
rc = scst_cm_err_check_retry(cmd, cmd->start_time, scst_cm_inq_retry_fn);
if (rc == SCST_CM_STATUS_RETRY)
if (rc == SCST_CM_STATUS_RETRY || !cmd->dev || !cmd->tgt_dev)
goto out;
spin_lock_bh(&dev->dev_lock);
@@ -2544,8 +2544,6 @@ static unsigned int scst_cm_get_lun(const struct scst_device *dev)
}
}
WARN_ON_ONCE(res == SCST_MAX_LUN);
out:
TRACE_EXIT_RES(res);
return res;

View File

@@ -1266,7 +1266,10 @@ static struct scst_device *__scst_lookup_device(struct scsi_device *scsidp)
return NULL;
}
static void scst_unregister_device(struct scsi_device *scsidp)
static void scst_unregister_device(struct scsi_device *scsidp,
void (*on_free)(struct scst_device *dev,
void* arg),
void *arg)
{
struct scst_device *dev;
struct scst_acg_dev *acg_dev, *aa;
@@ -1324,6 +1327,9 @@ static void scst_unregister_device(struct scsi_device *scsidp)
scsidp->host->host_no, scsidp->channel, scsidp->id,
(u64)scsidp->lun, scsidp->type);
if (on_free)
on_free(dev, arg);
scst_free_device(dev);
out:
@@ -1557,7 +1563,10 @@ EXPORT_SYMBOL_GPL(scst_register_virtual_device_node);
* scst_unregister_virtual_device() - unegister a virtual device.
* @id: the device's ID, returned by the registration function
*/
void scst_unregister_virtual_device(int id)
void scst_unregister_virtual_device(int id,
void (*on_free)(struct scst_device *dev,
void *arg),
void *arg)
{
struct scst_device *d, *dev = NULL;
struct scst_acg_dev *acg_dev, *aa;
@@ -1604,6 +1613,9 @@ void scst_unregister_virtual_device(int id)
PRINT_INFO("Detached from virtual device %s (id %d)",
dev->virt_name, dev->virt_id);
if (on_free)
on_free(dev, arg);
scst_free_device(dev);
out:
@@ -2404,7 +2416,7 @@ static void scst_remove(struct device *cdev, struct class_interface *intf)
if ((scsidp->host->hostt->name == NULL) ||
(strcmp(scsidp->host->hostt->name, SCST_LOCAL_NAME) != 0))
scst_unregister_device(scsidp);
scst_unregister_device(scsidp, NULL, NULL);
TRACE_EXIT();
return;