summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/isci/remote_device.c
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-03-03 17:59:32 -0800
committerDan Williams <dan.j.williams@intel.com>2011-07-03 03:55:29 -0700
commitd9c37390c4f02153188a64a7a89fa6798dc3ffc2 (patch)
tree508f28b5b88348b2d989f479442cbd6813bd2b42 /drivers/scsi/isci/remote_device.c
parent6ad31fec306d532031b2f778f8656385df1b9d8f (diff)
downloadblackbird-op-linux-d9c37390c4f02153188a64a7a89fa6798dc3ffc2.tar.gz
blackbird-op-linux-d9c37390c4f02153188a64a7a89fa6798dc3ffc2.zip
isci: preallocate remote devices
Until we synchronize against device removal this limits the damage of use after free bugs to the driver's own objects. Unless we implement reference counting we need to ensure at least a subset of a remote device is valid at all times. We follow the lead of other libsas drivers that also preallocate devices. This also enforces maximum remote device accounting at the lldd layer, but the core may still run out of RNC's before we hit this limit. Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/scsi/isci/remote_device.c')
-rw-r--r--drivers/scsi/isci/remote_device.c73
1 files changed, 35 insertions, 38 deletions
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c
index db2259ce003f..48556e47bb9d 100644
--- a/drivers/scsi/isci/remote_device.c
+++ b/drivers/scsi/isci/remote_device.c
@@ -67,40 +67,35 @@
/**
* isci_remote_device_deconstruct() - This function frees an isci_remote_device.
- * @isci_host: This parameter specifies the isci host object.
- * @isci_device: This parameter specifies the remote device to be freed.
+ * @ihost: This parameter specifies the isci host object.
+ * @idev: This parameter specifies the remote device to be freed.
*
*/
-static void isci_remote_device_deconstruct(
- struct isci_host *isci_host,
- struct isci_remote_device *isci_device)
+static void isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_remote_device *idev)
{
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_device = %p\n", __func__, isci_device);
+ dev_dbg(&ihost->pdev->dev,
+ "%s: isci_device = %p\n", __func__, idev);
/* There should not be any outstanding io's. All paths to
* here should go through isci_remote_device_nuke_requests.
* If we hit this condition, we will need a way to complete
* io requests in process */
- while (!list_empty(&isci_device->reqs_in_process)) {
+ while (!list_empty(&idev->reqs_in_process)) {
- dev_err(&isci_host->pdev->dev,
+ dev_err(&ihost->pdev->dev,
"%s: ** request list not empty! **\n", __func__);
BUG();
}
- /* Remove all related references to this device and free
- * the cache object.
- */
- scic_remote_device_destruct(to_sci_dev(isci_device));
- isci_device->domain_dev->lldd_dev = NULL;
- list_del(&isci_device->node);
-
- clear_bit(IDEV_STOP_PENDING, &isci_device->flags);
- clear_bit(IDEV_START_PENDING, &isci_device->flags);
- wake_up(&isci_host->eventq);
- complete(isci_device->cmp);
- kmem_cache_free(isci_kmem_cache, isci_device);
+ scic_remote_device_destruct(to_sci_dev(idev));
+ idev->domain_dev->lldd_dev = NULL;
+ idev->domain_dev = NULL;
+ idev->isci_port = NULL;
+ list_del_init(&idev->node);
+
+ clear_bit(IDEV_START_PENDING, &idev->flags);
+ clear_bit(IDEV_STOP_PENDING, &idev->flags);
+ wake_up(&ihost->eventq);
}
@@ -259,25 +254,27 @@ void isci_remote_device_nuke_requests(
* pointer to new isci_remote_device.
*/
static struct isci_remote_device *
-isci_remote_device_alloc(struct isci_host *isci_host, struct isci_port *port)
+isci_remote_device_alloc(struct isci_host *ihost, struct isci_port *iport)
{
- struct isci_remote_device *isci_device;
+ struct isci_remote_device *idev;
+ int i;
- isci_device = kmem_cache_zalloc(isci_kmem_cache, GFP_KERNEL);
+ for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
+ idev = idev_by_id(ihost, i);
+ if (!test_and_set_bit(IDEV_ALLOCATED, &idev->flags))
+ break;
+ }
- if (!isci_device) {
- dev_warn(&isci_host->pdev->dev, "%s: failed\n", __func__);
+ if (i >= SCI_MAX_REMOTE_DEVICES) {
+ dev_warn(&ihost->pdev->dev, "%s: failed\n", __func__);
return NULL;
}
- INIT_LIST_HEAD(&isci_device->reqs_in_process);
- INIT_LIST_HEAD(&isci_device->node);
-
- spin_lock_init(&isci_device->state_lock);
- isci_remote_device_change_state(isci_device, isci_freed);
-
- return isci_device;
+ BUG_ON(!list_empty(&idev->reqs_in_process));
+ BUG_ON(!list_empty(&idev->node));
+ isci_remote_device_change_state(idev, isci_freed);
+ return idev;
}
/**
@@ -381,24 +378,22 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem
{
enum sci_status status;
unsigned long flags;
- DECLARE_COMPLETION_ONSTACK(completion);
dev_dbg(&ihost->pdev->dev,
"%s: isci_device = %p\n", __func__, idev);
isci_remote_device_change_state(idev, isci_stopping);
set_bit(IDEV_STOP_PENDING, &idev->flags);
- idev->cmp = &completion;
spin_lock_irqsave(&ihost->scic_lock, flags);
-
status = scic_remote_device_stop(to_sci_dev(idev), 50);
-
spin_unlock_irqrestore(&ihost->scic_lock, flags);
/* Wait for the stop complete callback. */
- if (status == SCI_SUCCESS)
+ if (status == SCI_SUCCESS) {
wait_for_device_stop(ihost, idev);
+ clear_bit(IDEV_ALLOCATED, &idev->flags);
+ }
dev_dbg(&ihost->pdev->dev,
"%s: idev = %p - after completion wait\n",
@@ -469,6 +464,8 @@ int isci_remote_device_found(struct domain_device *domain_dev)
return -ENODEV;
isci_device = isci_remote_device_alloc(isci_host, isci_port);
+ if (!isci_device)
+ return -ENODEV;
INIT_LIST_HEAD(&isci_device->node);
domain_dev->lldd_dev = isci_device;
OpenPOWER on IntegriCloud