Skip to content

Commit 08c4d55

Browse files
Sreekanth Reddymartinkpetersen
authored andcommitted
mpt3sas: setpci reset kernel oops fix
setpci reset on nytro warpdrive card along with sysfs access and cli ioctl access resulted in kernel oops 1. pci_access_mutex lock added to provide synchronization between IOCTL, sysfs, PCI resource handling path 2. gioc_lock spinlock to protect list operations over multiple controllers This patch is ported from commit 6229b41 ("mpt2sas: setpci reset kernel oops fix"). Signed-off-by: Sreekanth Reddy <[email protected]> Acked-by: Christoph Hellwig <[email protected]> Reviewed-by: Hannes Reinecke <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 989e43c commit 08c4d55

File tree

4 files changed

+71
-9
lines changed

4 files changed

+71
-9
lines changed

drivers/scsi/mpt3sas/mpt3sas_base.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,12 @@ _scsih_set_fwfault_debug(const char *val, struct kernel_param *kp)
108108
if (ret)
109109
return ret;
110110

111+
/* global ioc spinlock to protect controller list on list operations */
111112
pr_info("setting fwfault_debug(%d)\n", mpt3sas_fwfault_debug);
113+
spin_lock(&gioc_lock);
112114
list_for_each_entry(ioc, &mpt3sas_ioc_list, list)
113115
ioc->fwfault_debug = mpt3sas_fwfault_debug;
116+
spin_unlock(&gioc_lock);
114117
return 0;
115118
}
116119
module_param_call(mpt3sas_fwfault_debug, _scsih_set_fwfault_debug,
@@ -5136,6 +5139,8 @@ mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc)
51365139
dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
51375140
__func__));
51385141

5142+
/* synchronizing freeing resource with pci_access_mutex lock */
5143+
mutex_lock(&ioc->pci_access_mutex);
51395144
if (ioc->chip_phys && ioc->chip) {
51405145
_base_mask_interrupts(ioc);
51415146
ioc->shost_recovery = 1;
@@ -5144,6 +5149,7 @@ mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc)
51445149
}
51455150

51465151
mpt3sas_base_unmap_resources(ioc);
5152+
mutex_unlock(&ioc->pci_access_mutex);
51475153
return;
51485154
}
51495155

drivers/scsi/mpt3sas/mpt3sas_base.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,13 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc);
916916
* @replyPostRegisterIndex: index of next position in Reply Desc Post Queue
917917
* @delayed_tr_list: target reset link list
918918
* @delayed_tr_volume_list: volume target reset link list
919-
* @@temp_sensors_count: flag to carry the number of temperature sensors
919+
* @temp_sensors_count: flag to carry the number of temperature sensors
920+
* @pci_access_mutex: Mutex to synchronize ioctl,sysfs show path and
921+
* pci resource handling. PCI resource freeing will lead to free
922+
* vital hardware/memory resource, which might be in use by cli/sysfs
923+
* path functions resulting in Null pointer reference followed by kernel
924+
* crash. To avoid the above race condition we use mutex syncrhonization
925+
* which ensures the syncrhonization between cli/sysfs_show path.
920926
*/
921927
struct MPT3SAS_ADAPTER {
922928
struct list_head list;
@@ -1131,6 +1137,7 @@ struct MPT3SAS_ADAPTER {
11311137
struct list_head delayed_tr_list;
11321138
struct list_head delayed_tr_volume_list;
11331139
u8 temp_sensors_count;
1140+
struct mutex pci_access_mutex;
11341141

11351142
/* diag buffer support */
11361143
u8 *diag_buffer[MPI2_DIAG_BUF_TYPE_COUNT];
@@ -1161,6 +1168,17 @@ typedef u8 (*MPT_CALLBACK)(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index,
11611168
/* base shared API */
11621169
extern struct list_head mpt3sas_ioc_list;
11631170
extern char driver_name[MPT_NAME_LENGTH];
1171+
/* spinlock on list operations over IOCs
1172+
* Case: when multiple warpdrive cards(IOCs) are in use
1173+
* Each IOC will added to the ioc list structure on initialization.
1174+
* Watchdog threads run at regular intervals to check IOC for any
1175+
* fault conditions which will trigger the dead_ioc thread to
1176+
* deallocate pci resource, resulting deleting the IOC netry from list,
1177+
* this deletion need to protected by spinlock to enusre that
1178+
* ioc removal is syncrhonized, if not synchronized it might lead to
1179+
* list_del corruption as the ioc list is traversed in cli path.
1180+
*/
1181+
extern spinlock_t gioc_lock;
11641182

11651183
void mpt3sas_base_start_watchdog(struct MPT3SAS_ADAPTER *ioc);
11661184
void mpt3sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc);

drivers/scsi/mpt3sas/mpt3sas_ctl.c

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,16 @@ static int
416416
_ctl_verify_adapter(int ioc_number, struct MPT3SAS_ADAPTER **iocpp)
417417
{
418418
struct MPT3SAS_ADAPTER *ioc;
419-
419+
/* global ioc lock to protect controller on list operations */
420+
spin_lock(&gioc_lock);
420421
list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
421422
if (ioc->id != ioc_number)
422423
continue;
424+
spin_unlock(&gioc_lock);
423425
*iocpp = ioc;
424426
return ioc_number;
425427
}
428+
spin_unlock(&gioc_lock);
426429
*iocpp = NULL;
427430
return -1;
428431
}
@@ -511,10 +514,15 @@ ctl_poll(struct file *filep, poll_table *wait)
511514

512515
poll_wait(filep, &ctl_poll_wait, wait);
513516

517+
/* global ioc lock to protect controller on list operations */
518+
spin_lock(&gioc_lock);
514519
list_for_each_entry(ioc, &mpt3sas_ioc_list, list) {
515-
if (ioc->aen_event_read_flag)
520+
if (ioc->aen_event_read_flag) {
521+
spin_unlock(&gioc_lock);
516522
return POLLIN | POLLRDNORM;
523+
}
517524
}
525+
spin_unlock(&gioc_lock);
518526
return 0;
519527
}
520528

@@ -2211,16 +2219,25 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg,
22112219
if (_ctl_verify_adapter(ioctl_header.ioc_number, &ioc) == -1 || !ioc)
22122220
return -ENODEV;
22132221

2222+
/* pci_access_mutex lock acquired by ioctl path */
2223+
mutex_lock(&ioc->pci_access_mutex);
2224+
22142225
if (ioc->shost_recovery || ioc->pci_error_recovery ||
2215-
ioc->is_driver_loading)
2216-
return -EAGAIN;
2226+
ioc->is_driver_loading || ioc->remove_host) {
2227+
ret = -EAGAIN;
2228+
goto out_unlock_pciaccess;
2229+
}
22172230

22182231
state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING;
22192232
if (state == NON_BLOCKING) {
2220-
if (!mutex_trylock(&ioc->ctl_cmds.mutex))
2221-
return -EAGAIN;
2222-
} else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex))
2223-
return -ERESTARTSYS;
2233+
if (!mutex_trylock(&ioc->ctl_cmds.mutex)) {
2234+
ret = -EAGAIN;
2235+
goto out_unlock_pciaccess;
2236+
}
2237+
} else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) {
2238+
ret = -ERESTARTSYS;
2239+
goto out_unlock_pciaccess;
2240+
}
22242241

22252242

22262243
switch (cmd) {
@@ -2301,6 +2318,8 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg,
23012318
}
23022319

23032320
mutex_unlock(&ioc->ctl_cmds.mutex);
2321+
out_unlock_pciaccess:
2322+
mutex_unlock(&ioc->pci_access_mutex);
23042323
return ret;
23052324
}
23062325

@@ -2748,6 +2767,12 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr,
27482767
" warpdrive\n", ioc->name, __func__);
27492768
goto out;
27502769
}
2770+
/* pci_access_mutex lock acquired by sysfs show path */
2771+
mutex_lock(&ioc->pci_access_mutex);
2772+
if (ioc->pci_error_recovery || ioc->remove_host) {
2773+
mutex_unlock(&ioc->pci_access_mutex);
2774+
return 0;
2775+
}
27512776

27522777
/* allocate upto GPIOVal 36 entries */
27532778
sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36);
@@ -2786,6 +2811,7 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr,
27862811

27872812
out:
27882813
kfree(io_unit_pg3);
2814+
mutex_unlock(&ioc->pci_access_mutex);
27892815
return rc;
27902816
}
27912817
static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL);

drivers/scsi/mpt3sas/mpt3sas_scsih.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ _scsih_setup_direct_io(struct MPT3SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,
9090
/* global parameters */
9191
LIST_HEAD(mpt3sas_ioc_list);
9292
char driver_name[MPT_NAME_LENGTH];
93+
/* global ioc lock for list operations */
94+
DEFINE_SPINLOCK(gioc_lock);
9395

9496
/* local parameters */
9597
static u8 scsi_io_cb_idx = -1;
@@ -294,8 +296,10 @@ _scsih_set_debug_level(const char *val, struct kernel_param *kp)
294296
return ret;
295297

296298
pr_info("setting logging_level(0x%08x)\n", logging_level);
299+
spin_lock(&gioc_lock);
297300
list_for_each_entry(ioc, &mpt3sas_ioc_list, list)
298301
ioc->logging_level = logging_level;
302+
spin_unlock(&gioc_lock);
299303
return 0;
300304
}
301305
module_param_call(logging_level, _scsih_set_debug_level, param_get_int,
@@ -7997,7 +8001,9 @@ void scsih_remove(struct pci_dev *pdev)
79978001
sas_remove_host(shost);
79988002
scsi_remove_host(shost);
79998003
mpt3sas_base_detach(ioc);
8004+
spin_lock(&gioc_lock);
80008005
list_del(&ioc->list);
8006+
spin_unlock(&gioc_lock);
80018007
scsi_host_put(shost);
80028008
}
80038009

@@ -8384,7 +8390,9 @@ scsih_probe(struct pci_dev *pdev, struct Scsi_Host *shost)
83848390
ioc = shost_priv(shost);
83858391
memset(ioc, 0, sizeof(struct MPT3SAS_ADAPTER));
83868392
INIT_LIST_HEAD(&ioc->list);
8393+
spin_lock(&gioc_lock);
83878394
list_add_tail(&ioc->list, &mpt3sas_ioc_list);
8395+
spin_unlock(&gioc_lock);
83888396
ioc->shost = shost;
83898397
ioc->id = mpt_ids++;
83908398
ioc->pdev = pdev;
@@ -8403,6 +8411,8 @@ scsih_probe(struct pci_dev *pdev, struct Scsi_Host *shost)
84038411
ioc->schedule_dead_ioc_flush_running_cmds = &_scsih_flush_running_cmds;
84048412
/* misc semaphores and spin locks */
84058413
mutex_init(&ioc->reset_in_progress_mutex);
8414+
/* initializing pci_access_mutex lock */
8415+
mutex_init(&ioc->pci_access_mutex);
84068416
spin_lock_init(&ioc->ioc_reset_in_progress_lock);
84078417
spin_lock_init(&ioc->scsi_lookup_lock);
84088418
spin_lock_init(&ioc->sas_device_lock);
@@ -8510,7 +8520,9 @@ scsih_probe(struct pci_dev *pdev, struct Scsi_Host *shost)
85108520
out_attach_fail:
85118521
destroy_workqueue(ioc->firmware_event_thread);
85128522
out_thread_fail:
8523+
spin_lock(&gioc_lock);
85138524
list_del(&ioc->list);
8525+
spin_unlock(&gioc_lock);
85148526
scsi_host_put(shost);
85158527
return rv;
85168528
}

0 commit comments

Comments
 (0)