diff options
author | Nagarajkumar Narayanan <nagarajkumar.narayanan@seagate.com> | 2015-08-18 13:27:10 +0530 |
---|---|---|
committer | James Bottomley <JBottomley@Odin.com> | 2015-09-06 11:57:25 -0700 |
commit | 6229b414b3adb3aac0b54e67d72d6462fc230c0d (patch) | |
tree | df110926c0947fd46d57eba75c88dafee6f35f92 /drivers/scsi/mpt2sas/mpt2sas_ctl.c | |
parent | 50acde8ed35620fdfad88f4e20a39a1eb0d8d4c9 (diff) | |
download | op-kernel-dev-6229b414b3adb3aac0b54e67d72d6462fc230c0d.zip op-kernel-dev-6229b414b3adb3aac0b54e67d72d6462fc230c0d.tar.gz |
mpt2sas: setpci reset kernel oops fix
mpt2sas: 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
>From c53a1cff4c07528b8b9ec7f6716e94950283e8f9 Mon Sep 17 00:00:00 2001
From: Nagarajkumar Narayanan <nagarajkumar.narayanan@seagate.com>
Date: Tue, 18 Aug 2015 11:58:13 +0530
Subject: [PATCH] mpt2sas setpci reset oops fix
In mpt2sas driver due to lack of synchronization between ioctl,
BRM status access through sysfs, pci resource removal kernel oops
happen as ioctl path and BRM status sysfs access path still tries
to access the removed resources
Two locks added to provide syncrhonization
1. pci_access_mutex: Mutex to synchronize ioctl,sysfs show path and
pci resource handling. PCI resource freeing will lead to free
vital hardware/memory resource, which might be in use by cli/sysfs
path functions resulting in Null pointer reference followed by kernel
crash. To avoid the above race condition we use mutex syncrhonization
which ensures the syncrhonization between cli/sysfs_show path
Note: pci_access_mutex is used only if nytro warpdrive cards
(ioc->is_warpdrive based on device id) are used
as we could not test this case with other SAS2 HBA cards
We can remove this check if this behaviour confirmed from other
cards.
2. spinlock on list operations over IOCs
Case: when multiple warpdrive cards(IOCs) are in use
Each IOC will added to the ioc list stucture on initialization.
Watchdog threads run at regular intervals to check IOC for any
fault conditions which will trigger the dead_ioc thread to
deallocate pci resource, resulting deleting the IOC netry from list,
this deletion need to protected by spinlock to enusre that
ioc removal is syncrhonized, if not synchronized it might lead to
list_del corruption as the ioc list is traversed in cli path
Signed-off-by: Nagarajkumar Narayanan <nagarajkumar.narayanan@seagate.com>
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Acked-by: Sreekanth Reddy <sreekanth.reddy@avagotech.com>
Signed-off-by: James Bottomley <JBottomley@Odin.com>
Diffstat (limited to 'drivers/scsi/mpt2sas/mpt2sas_ctl.c')
-rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_ctl.c | 38 |
1 files changed, 31 insertions, 7 deletions
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c index 4e50960..3694b63 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c +++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c @@ -427,13 +427,16 @@ static int _ctl_verify_adapter(int ioc_number, struct MPT2SAS_ADAPTER **iocpp) { struct MPT2SAS_ADAPTER *ioc; - + /* global ioc lock to protect controller on list operations */ + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { if (ioc->id != ioc_number) continue; + spin_unlock(&gioc_lock); *iocpp = ioc; return ioc_number; } + spin_unlock(&gioc_lock); *iocpp = NULL; return -1; } @@ -522,10 +525,15 @@ _ctl_poll(struct file *filep, poll_table *wait) poll_wait(filep, &ctl_poll_wait, wait); + /* global ioc lock to protect controller on list operations */ + spin_lock(&gioc_lock); list_for_each_entry(ioc, &mpt2sas_ioc_list, list) { - if (ioc->aen_event_read_flag) + if (ioc->aen_event_read_flag) { + spin_unlock(&gioc_lock); return POLLIN | POLLRDNORM; + } } + spin_unlock(&gioc_lock); return 0; } @@ -2168,16 +2176,23 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, if (_ctl_verify_adapter(ioctl_header.ioc_number, &ioc) == -1 || !ioc) return -ENODEV; + /* pci_access_mutex lock acquired by ioctl path */ + mutex_lock(&ioc->pci_access_mutex); if (ioc->shost_recovery || ioc->pci_error_recovery || - ioc->is_driver_loading) - return -EAGAIN; + ioc->is_driver_loading || ioc->remove_host) { + ret = -EAGAIN; + goto out_unlock_pciaccess; + } state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING : BLOCKING; if (state == NON_BLOCKING) { - if (!mutex_trylock(&ioc->ctl_cmds.mutex)) - return -EAGAIN; + if (!mutex_trylock(&ioc->ctl_cmds.mutex)) { + ret = -EAGAIN; + goto out_unlock_pciaccess; + } } else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) { - return -ERESTARTSYS; + ret = -ERESTARTSYS; + goto out_unlock_pciaccess; } switch (cmd) { @@ -2258,6 +2273,8 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg, } mutex_unlock(&ioc->ctl_cmds.mutex); +out_unlock_pciaccess: + mutex_unlock(&ioc->pci_access_mutex); return ret; } @@ -2711,6 +2728,12 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, "warpdrive\n", ioc->name, __func__); goto out; } + /* pci_access_mutex lock acquired by sysfs show path */ + mutex_lock(&ioc->pci_access_mutex); + if (ioc->pci_error_recovery || ioc->remove_host) { + mutex_unlock(&ioc->pci_access_mutex); + return 0; + } /* allocate upto GPIOVal 36 entries */ sz = offsetof(Mpi2IOUnitPage3_t, GPIOVal) + (sizeof(u16) * 36); @@ -2749,6 +2772,7 @@ _ctl_BRM_status_show(struct device *cdev, struct device_attribute *attr, out: kfree(io_unit_pg3); + mutex_unlock(&ioc->pci_access_mutex); return rc; } static DEVICE_ATTR(BRM_status, S_IRUGO, _ctl_BRM_status_show, NULL); |