Skip to content

Commit ce88822

Browse files
committed
Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull SCSI fixes from James Bottomley: "Eight patches which looks like quite a large core change, but most of the diffstat is reverting the attempt to rejig reference counting introduced in the last merge window which caused issues with device and module removal. Of the remaining four patches, only the fix use-after-free is substantial" * tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: scsi: mpt3sas: Fix use-after-free warning scsi: core: Fix a use-after-free scsi: core: Revert "Make sure that targets outlive devices" scsi: core: Revert "Make sure that hosts outlive targets" scsi: core: Revert "Simplify LLD module reference counting" scsi: core: Revert "Call blk_mq_free_tag_set() earlier" scsi: lpfc: Add missing destroy_workqueue() in error path scsi: lpfc: Return DID_TRANSPORT_DISRUPTED instead of DID_REQUEUE
2 parents e35be05 + 991df3d commit ce88822

File tree

11 files changed

+47
-56
lines changed

11 files changed

+47
-56
lines changed

drivers/scsi/hosts.c

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,15 @@ void scsi_remove_host(struct Scsi_Host *shost)
182182
mutex_unlock(&shost->scan_mutex);
183183
scsi_proc_host_rm(shost);
184184

185+
/*
186+
* New SCSI devices cannot be attached anymore because of the SCSI host
187+
* state so drop the tag set refcnt. Wait until the tag set refcnt drops
188+
* to zero because .exit_cmd_priv implementations may need the host
189+
* pointer.
190+
*/
191+
kref_put(&shost->tagset_refcnt, scsi_mq_free_tags);
192+
wait_for_completion(&shost->tagset_freed);
193+
185194
spin_lock_irqsave(shost->host_lock, flags);
186195
if (scsi_host_set_state(shost, SHOST_DEL))
187196
BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY));
@@ -190,15 +199,6 @@ void scsi_remove_host(struct Scsi_Host *shost)
190199
transport_unregister_device(&shost->shost_gendev);
191200
device_unregister(&shost->shost_dev);
192201
device_del(&shost->shost_gendev);
193-
194-
/*
195-
* After scsi_remove_host() has returned the scsi LLD module can be
196-
* unloaded and/or the host resources can be released. Hence wait until
197-
* the dependent SCSI targets and devices are gone before returning.
198-
*/
199-
wait_event(shost->targets_wq, atomic_read(&shost->target_count) == 0);
200-
201-
scsi_mq_destroy_tags(shost);
202202
}
203203
EXPORT_SYMBOL(scsi_remove_host);
204204

@@ -254,6 +254,9 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
254254
if (error)
255255
goto fail;
256256

257+
kref_init(&shost->tagset_refcnt);
258+
init_completion(&shost->tagset_freed);
259+
257260
/*
258261
* Increase usage count temporarily here so that calling
259262
* scsi_autopm_put_host() will trigger runtime idle if there is
@@ -309,8 +312,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
309312
return error;
310313

311314
/*
312-
* Any resources associated with the SCSI host in this function except
313-
* the tag set will be freed by scsi_host_dev_release().
315+
* Any host allocation in this function will be freed in
316+
* scsi_host_dev_release().
314317
*/
315318
out_del_dev:
316319
device_del(&shost->shost_dev);
@@ -326,7 +329,7 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
326329
pm_runtime_disable(&shost->shost_gendev);
327330
pm_runtime_set_suspended(&shost->shost_gendev);
328331
pm_runtime_put_noidle(&shost->shost_gendev);
329-
scsi_mq_destroy_tags(shost);
332+
kref_put(&shost->tagset_refcnt, scsi_mq_free_tags);
330333
fail:
331334
return error;
332335
}
@@ -406,7 +409,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
406409
INIT_LIST_HEAD(&shost->starved_list);
407410
init_waitqueue_head(&shost->host_wait);
408411
mutex_init(&shost->scan_mutex);
409-
init_waitqueue_head(&shost->targets_wq);
410412

411413
index = ida_alloc(&host_index_ida, GFP_KERNEL);
412414
if (index < 0) {

drivers/scsi/lpfc/lpfc_init.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8053,7 +8053,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
80538053
/* Allocate device driver memory */
80548054
rc = lpfc_mem_alloc(phba, SGL_ALIGN_SZ);
80558055
if (rc)
8056-
return -ENOMEM;
8056+
goto out_destroy_workqueue;
80578057

80588058
/* IF Type 2 ports get initialized now. */
80598059
if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) >=
@@ -8481,6 +8481,9 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
84818481
lpfc_destroy_bootstrap_mbox(phba);
84828482
out_free_mem:
84838483
lpfc_mem_free(phba);
8484+
out_destroy_workqueue:
8485+
destroy_workqueue(phba->wq);
8486+
phba->wq = NULL;
84848487
return rc;
84858488
}
84868489

drivers/scsi/lpfc/lpfc_scsi.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4272,7 +4272,7 @@ lpfc_fcp_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn,
42724272
lpfc_cmd->result == IOERR_ABORT_REQUESTED ||
42734273
lpfc_cmd->result == IOERR_RPI_SUSPENDED ||
42744274
lpfc_cmd->result == IOERR_SLER_CMD_RCV_FAILURE) {
4275-
cmd->result = DID_REQUEUE << 16;
4275+
cmd->result = DID_TRANSPORT_DISRUPTED << 16;
42764276
break;
42774277
}
42784278
if ((lpfc_cmd->result == IOERR_RX_DMA_FAILED ||
@@ -4562,7 +4562,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn,
45624562
lpfc_cmd->result == IOERR_NO_RESOURCES ||
45634563
lpfc_cmd->result == IOERR_ABORT_REQUESTED ||
45644564
lpfc_cmd->result == IOERR_SLER_CMD_RCV_FAILURE) {
4565-
cmd->result = DID_REQUEUE << 16;
4565+
cmd->result = DID_TRANSPORT_DISRUPTED << 16;
45664566
break;
45674567
}
45684568
if ((lpfc_cmd->result == IOERR_RX_DMA_FAILED ||

drivers/scsi/mpt3sas/mpt3sas_scsih.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3670,6 +3670,7 @@ static struct fw_event_work *dequeue_next_fw_event(struct MPT3SAS_ADAPTER *ioc)
36703670
fw_event = list_first_entry(&ioc->fw_event_list,
36713671
struct fw_event_work, list);
36723672
list_del_init(&fw_event->list);
3673+
fw_event_work_put(fw_event);
36733674
}
36743675
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
36753676

@@ -3751,7 +3752,6 @@ _scsih_fw_event_cleanup_queue(struct MPT3SAS_ADAPTER *ioc)
37513752
if (cancel_work_sync(&fw_event->work))
37523753
fw_event_work_put(fw_event);
37533754

3754-
fw_event_work_put(fw_event);
37553755
}
37563756
ioc->fw_events_cleanup = 0;
37573757
}

drivers/scsi/scsi.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -586,13 +586,10 @@ EXPORT_SYMBOL(scsi_device_get);
586586
*/
587587
void scsi_device_put(struct scsi_device *sdev)
588588
{
589-
/*
590-
* Decreasing the module reference count before the device reference
591-
* count is safe since scsi_remove_host() only returns after all
592-
* devices have been removed.
593-
*/
594-
module_put(sdev->host->hostt->module);
589+
struct module *mod = sdev->host->hostt->module;
590+
595591
put_device(&sdev->sdev_gendev);
592+
module_put(mod);
596593
}
597594
EXPORT_SYMBOL(scsi_device_put);
598595

drivers/scsi/scsi_lib.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1983,9 +1983,13 @@ int scsi_mq_setup_tags(struct Scsi_Host *shost)
19831983
return blk_mq_alloc_tag_set(tag_set);
19841984
}
19851985

1986-
void scsi_mq_destroy_tags(struct Scsi_Host *shost)
1986+
void scsi_mq_free_tags(struct kref *kref)
19871987
{
1988+
struct Scsi_Host *shost = container_of(kref, typeof(*shost),
1989+
tagset_refcnt);
1990+
19881991
blk_mq_free_tag_set(&shost->tag_set);
1992+
complete(&shost->tagset_freed);
19891993
}
19901994

19911995
/**

drivers/scsi/scsi_priv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ extern void scsi_run_host_queues(struct Scsi_Host *shost);
9494
extern void scsi_requeue_run_queue(struct work_struct *work);
9595
extern void scsi_start_queue(struct scsi_device *sdev);
9696
extern int scsi_mq_setup_tags(struct Scsi_Host *shost);
97-
extern void scsi_mq_destroy_tags(struct Scsi_Host *shost);
97+
extern void scsi_mq_free_tags(struct kref *kref);
9898
extern void scsi_exit_queue(void);
9999
extern void scsi_evt_thread(struct work_struct *work);
100100

drivers/scsi/scsi_scan.c

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
340340
kfree(sdev);
341341
goto out;
342342
}
343+
kref_get(&sdev->host->tagset_refcnt);
343344
sdev->request_queue = q;
344345
q->queuedata = sdev;
345346
__scsi_init_queue(sdev->host, q);
@@ -406,14 +407,9 @@ static void scsi_target_destroy(struct scsi_target *starget)
406407
static void scsi_target_dev_release(struct device *dev)
407408
{
408409
struct device *parent = dev->parent;
409-
struct Scsi_Host *shost = dev_to_shost(parent);
410410
struct scsi_target *starget = to_scsi_target(dev);
411411

412412
kfree(starget);
413-
414-
if (atomic_dec_return(&shost->target_count) == 0)
415-
wake_up(&shost->targets_wq);
416-
417413
put_device(parent);
418414
}
419415

@@ -526,10 +522,6 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
526522
starget->state = STARGET_CREATED;
527523
starget->scsi_level = SCSI_2;
528524
starget->max_target_blocked = SCSI_DEFAULT_TARGET_BLOCKED;
529-
init_waitqueue_head(&starget->sdev_wq);
530-
531-
atomic_inc(&shost->target_count);
532-
533525
retry:
534526
spin_lock_irqsave(shost->host_lock, flags);
535527

drivers/scsi/scsi_sysfs.c

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -443,15 +443,18 @@ static void scsi_device_cls_release(struct device *class_dev)
443443

444444
static void scsi_device_dev_release_usercontext(struct work_struct *work)
445445
{
446-
struct scsi_device *sdev = container_of(work, struct scsi_device,
447-
ew.work);
448-
struct scsi_target *starget = sdev->sdev_target;
446+
struct scsi_device *sdev;
449447
struct device *parent;
450448
struct list_head *this, *tmp;
451449
struct scsi_vpd *vpd_pg80 = NULL, *vpd_pg83 = NULL;
452450
struct scsi_vpd *vpd_pg0 = NULL, *vpd_pg89 = NULL;
453451
struct scsi_vpd *vpd_pgb0 = NULL, *vpd_pgb1 = NULL, *vpd_pgb2 = NULL;
454452
unsigned long flags;
453+
struct module *mod;
454+
455+
sdev = container_of(work, struct scsi_device, ew.work);
456+
457+
mod = sdev->host->hostt->module;
455458

456459
scsi_dh_release_device(sdev);
457460

@@ -513,16 +516,19 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)
513516
kfree(sdev->inquiry);
514517
kfree(sdev);
515518

516-
if (starget && atomic_dec_return(&starget->sdev_count) == 0)
517-
wake_up(&starget->sdev_wq);
518-
519519
if (parent)
520520
put_device(parent);
521+
module_put(mod);
521522
}
522523

523524
static void scsi_device_dev_release(struct device *dev)
524525
{
525526
struct scsi_device *sdp = to_scsi_device(dev);
527+
528+
/* Set module pointer as NULL in case of module unloading */
529+
if (!try_module_get(sdp->host->hostt->module))
530+
sdp->host->hostt->module = NULL;
531+
526532
execute_in_process_context(scsi_device_dev_release_usercontext,
527533
&sdp->ew);
528534
}
@@ -1470,6 +1476,7 @@ void __scsi_remove_device(struct scsi_device *sdev)
14701476
mutex_unlock(&sdev->state_mutex);
14711477

14721478
blk_mq_destroy_queue(sdev->request_queue);
1479+
kref_put(&sdev->host->tagset_refcnt, scsi_mq_free_tags);
14731480
cancel_work_sync(&sdev->requeue_work);
14741481

14751482
if (sdev->host->hostt->slave_destroy)
@@ -1529,14 +1536,6 @@ static void __scsi_remove_target(struct scsi_target *starget)
15291536
goto restart;
15301537
}
15311538
spin_unlock_irqrestore(shost->host_lock, flags);
1532-
1533-
/*
1534-
* After scsi_remove_target() returns its caller can remove resources
1535-
* associated with @starget, e.g. an rport or session. Wait until all
1536-
* devices associated with @starget have been removed to prevent that
1537-
* a SCSI error handling callback function triggers a use-after-free.
1538-
*/
1539-
wait_event(starget->sdev_wq, atomic_read(&starget->sdev_count) == 0);
15401539
}
15411540

15421541
/**
@@ -1647,9 +1646,6 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev)
16471646
list_add_tail(&sdev->same_target_siblings, &starget->devices);
16481647
list_add_tail(&sdev->siblings, &shost->__devices);
16491648
spin_unlock_irqrestore(shost->host_lock, flags);
1650-
1651-
atomic_inc(&starget->sdev_count);
1652-
16531649
/*
16541650
* device can now only be removed via __scsi_remove_device() so hold
16551651
* the target. Target will be held in CREATED state until something

include/scsi/scsi_device.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,6 @@ struct scsi_target {
309309
struct list_head devices;
310310
struct device dev;
311311
struct kref reap_ref; /* last put renders target invisible */
312-
atomic_t sdev_count;
313-
wait_queue_head_t sdev_wq;
314312
unsigned int channel;
315313
unsigned int id; /* target id ... replace
316314
* scsi_device.id eventually */

0 commit comments

Comments
 (0)