aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-03-03 20:59:32 -0500
committerDan Williams <dan.j.williams@intel.com>2011-07-03 06:55:29 -0400
commitd9c37390c4f02153188a64a7a89fa6798dc3ffc2 (patch)
tree508f28b5b88348b2d989f479442cbd6813bd2b42 /drivers
parent6ad31fec306d532031b2f778f8656385df1b9d8f (diff)
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')
-rw-r--r--drivers/scsi/isci/host.c21
-rw-r--r--drivers/scsi/isci/host.h17
-rw-r--r--drivers/scsi/isci/init.c27
-rw-r--r--drivers/scsi/isci/isci.h1
-rw-r--r--drivers/scsi/isci/remote_device.c73
-rw-r--r--drivers/scsi/isci/remote_device.h2
6 files changed, 68 insertions, 73 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 8d255666a657..ae5d46022073 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -378,8 +378,7 @@ static void __iomem *smu_base(struct isci_host *isci_host)
378 378
379int isci_host_init(struct isci_host *isci_host) 379int isci_host_init(struct isci_host *isci_host)
380{ 380{
381 int err = 0; 381 int err = 0, i;
382 int index = 0;
383 enum sci_status status; 382 enum sci_status status;
384 struct scic_sds_controller *controller; 383 struct scic_sds_controller *controller;
385 union scic_oem_parameters scic_oem_params; 384 union scic_oem_parameters scic_oem_params;
@@ -509,13 +508,19 @@ int isci_host_init(struct isci_host *isci_host)
509 if (!isci_host->dma_pool) 508 if (!isci_host->dma_pool)
510 return -ENOMEM; 509 return -ENOMEM;
511 510
512 for (index = 0; index < SCI_MAX_PORTS; index++) 511 for (i = 0; i < SCI_MAX_PORTS; i++)
513 isci_port_init(&isci_host->isci_ports[index], 512 isci_port_init(&isci_host->isci_ports[i], isci_host, i);
514 isci_host,
515 index);
516 513
517 for (index = 0; index < SCI_MAX_PHYS; index++) 514 for (i = 0; i < SCI_MAX_PHYS; i++)
518 isci_phy_init(&isci_host->phys[index], isci_host, index); 515 isci_phy_init(&isci_host->phys[i], isci_host, i);
516
517 for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
518 struct isci_remote_device *idev = idev_by_id(isci_host, i);
519
520 INIT_LIST_HEAD(&idev->reqs_in_process);
521 INIT_LIST_HEAD(&idev->node);
522 spin_lock_init(&idev->state_lock);
523 }
519 524
520 return 0; 525 return 0;
521} 526}
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
index 6a6304c06976..3c69f1ffb1c3 100644
--- a/drivers/scsi/isci/host.h
+++ b/drivers/scsi/isci/host.h
@@ -61,6 +61,7 @@
61/*#include "task.h"*/ 61/*#include "task.h"*/
62#include "timers.h" 62#include "timers.h"
63#include "remote_device.h" 63#include "remote_device.h"
64#include "scic_remote_device.h"
64 65
65#define DRV_NAME "isci" 66#define DRV_NAME "isci"
66#define SCI_PCI_BAR_COUNT 2 67#define SCI_PCI_BAR_COUNT 2
@@ -117,8 +118,18 @@ struct isci_host {
117 struct list_head requests_to_complete; 118 struct list_head requests_to_complete;
118 struct list_head requests_to_abort; 119 struct list_head requests_to_abort;
119 spinlock_t scic_lock; 120 spinlock_t scic_lock;
121
122 /* careful only access this via idev_by_id */
123 struct isci_remote_device devices[0];
120}; 124};
121 125
126static inline struct isci_remote_device *idev_by_id(struct isci_host *ihost, int i)
127{
128 void *p = ihost->devices;
129
130 return p + i * (sizeof(struct isci_remote_device) +
131 scic_remote_device_get_object_size());
132}
122 133
123/** 134/**
124 * struct isci_pci_info - This class represents the pci function containing the 135 * struct isci_pci_info - This class represents the pci function containing the
@@ -219,11 +230,7 @@ static inline void wait_for_device_start(struct isci_host *ihost, struct isci_re
219 230
220static inline void wait_for_device_stop(struct isci_host *ihost, struct isci_remote_device *idev) 231static inline void wait_for_device_stop(struct isci_host *ihost, struct isci_remote_device *idev)
221{ 232{
222 /* todo switch to: 233 wait_event(ihost->eventq, !test_bit(IDEV_STOP_PENDING, &idev->flags));
223 * wait_event(ihost->eventq, !test_bit(IDEV_STOP_PENDING, &idev->flags));
224 * once devices are statically allocated
225 */
226 wait_for_completion(idev->cmp);
227} 234}
228 235
229/** 236/**
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index f1b8a51dd49f..2838beff43b8 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -64,7 +64,6 @@
64#include "sci_environment.h" 64#include "sci_environment.h"
65 65
66static struct scsi_transport_template *isci_transport_template; 66static struct scsi_transport_template *isci_transport_template;
67struct kmem_cache *isci_kmem_cache;
68 67
69static DEFINE_PCI_DEVICE_TABLE(isci_id_table) = { 68static DEFINE_PCI_DEVICE_TABLE(isci_id_table) = {
70 { PCI_VDEVICE(INTEL, 0x1D61),}, 69 { PCI_VDEVICE(INTEL, 0x1D61),},
@@ -443,7 +442,10 @@ static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id)
443 struct Scsi_Host *shost; 442 struct Scsi_Host *shost;
444 int err; 443 int err;
445 444
446 isci_host = devm_kzalloc(&pdev->dev, sizeof(*isci_host), GFP_KERNEL); 445 isci_host = devm_kzalloc(&pdev->dev, sizeof(*isci_host) +
446 SCI_MAX_REMOTE_DEVICES *
447 (sizeof(struct isci_remote_device) +
448 scic_remote_device_get_object_size()), GFP_KERNEL);
447 if (!isci_host) 449 if (!isci_host)
448 return NULL; 450 return NULL;
449 451
@@ -656,31 +658,17 @@ static void __devexit isci_pci_remove(struct pci_dev *pdev)
656 658
657static __init int isci_init(void) 659static __init int isci_init(void)
658{ 660{
659 int err = -ENOMEM; 661 int err;
660 662
661 pr_info("%s: Intel(R) C600 SAS Controller Driver\n", DRV_NAME); 663 pr_info("%s: Intel(R) C600 SAS Controller Driver\n", DRV_NAME);
662 664
663 isci_kmem_cache = kmem_cache_create(DRV_NAME,
664 sizeof(struct isci_remote_device) +
665 scic_remote_device_get_object_size(),
666 0, 0, NULL);
667 if (!isci_kmem_cache)
668 return err;
669
670 isci_transport_template = sas_domain_attach_transport(&isci_transport_ops); 665 isci_transport_template = sas_domain_attach_transport(&isci_transport_ops);
671 if (!isci_transport_template) 666 if (!isci_transport_template)
672 goto err_kmem; 667 return -ENOMEM;
673 668
674 err = pci_register_driver(&isci_pci_driver); 669 err = pci_register_driver(&isci_pci_driver);
675 if (err) 670 if (err)
676 goto err_sas; 671 sas_release_transport(isci_transport_template);
677
678 return 0;
679
680 err_sas:
681 sas_release_transport(isci_transport_template);
682 err_kmem:
683 kmem_cache_destroy(isci_kmem_cache);
684 672
685 return err; 673 return err;
686} 674}
@@ -689,7 +677,6 @@ static __exit void isci_exit(void)
689{ 677{
690 pci_unregister_driver(&isci_pci_driver); 678 pci_unregister_driver(&isci_pci_driver);
691 sas_release_transport(isci_transport_template); 679 sas_release_transport(isci_transport_template);
692 kmem_cache_destroy(isci_kmem_cache);
693} 680}
694 681
695MODULE_LICENSE("Dual BSD/GPL"); 682MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/scsi/isci/isci.h b/drivers/scsi/isci/isci.h
index 9b9aa50954ce..24c67b039d4a 100644
--- a/drivers/scsi/isci/isci.h
+++ b/drivers/scsi/isci/isci.h
@@ -89,7 +89,6 @@
89#include "task.h" 89#include "task.h"
90#include "sata.h" 90#include "sata.h"
91 91
92extern struct kmem_cache *isci_kmem_cache;
93extern struct isci_firmware *isci_firmware; 92extern struct isci_firmware *isci_firmware;
94 93
95#define ISCI_FW_NAME "isci/isci_firmware.bin" 94#define ISCI_FW_NAME "isci/isci_firmware.bin"
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 @@
67 67
68/** 68/**
69 * isci_remote_device_deconstruct() - This function frees an isci_remote_device. 69 * isci_remote_device_deconstruct() - This function frees an isci_remote_device.
70 * @isci_host: This parameter specifies the isci host object. 70 * @ihost: This parameter specifies the isci host object.
71 * @isci_device: This parameter specifies the remote device to be freed. 71 * @idev: This parameter specifies the remote device to be freed.
72 * 72 *
73 */ 73 */
74static void isci_remote_device_deconstruct( 74static void isci_remote_device_deconstruct(struct isci_host *ihost, struct isci_remote_device *idev)
75 struct isci_host *isci_host,
76 struct isci_remote_device *isci_device)
77{ 75{
78 dev_dbg(&isci_host->pdev->dev, 76 dev_dbg(&ihost->pdev->dev,
79 "%s: isci_device = %p\n", __func__, isci_device); 77 "%s: isci_device = %p\n", __func__, idev);
80 78
81 /* There should not be any outstanding io's. All paths to 79 /* There should not be any outstanding io's. All paths to
82 * here should go through isci_remote_device_nuke_requests. 80 * here should go through isci_remote_device_nuke_requests.
83 * If we hit this condition, we will need a way to complete 81 * If we hit this condition, we will need a way to complete
84 * io requests in process */ 82 * io requests in process */
85 while (!list_empty(&isci_device->reqs_in_process)) { 83 while (!list_empty(&idev->reqs_in_process)) {
86 84
87 dev_err(&isci_host->pdev->dev, 85 dev_err(&ihost->pdev->dev,
88 "%s: ** request list not empty! **\n", __func__); 86 "%s: ** request list not empty! **\n", __func__);
89 BUG(); 87 BUG();
90 } 88 }
91 89
92 /* Remove all related references to this device and free 90 scic_remote_device_destruct(to_sci_dev(idev));
93 * the cache object. 91 idev->domain_dev->lldd_dev = NULL;
94 */ 92 idev->domain_dev = NULL;
95 scic_remote_device_destruct(to_sci_dev(isci_device)); 93 idev->isci_port = NULL;
96 isci_device->domain_dev->lldd_dev = NULL; 94 list_del_init(&idev->node);
97 list_del(&isci_device->node); 95
98 96 clear_bit(IDEV_START_PENDING, &idev->flags);
99 clear_bit(IDEV_STOP_PENDING, &isci_device->flags); 97 clear_bit(IDEV_STOP_PENDING, &idev->flags);
100 clear_bit(IDEV_START_PENDING, &isci_device->flags); 98 wake_up(&ihost->eventq);
101 wake_up(&isci_host->eventq);
102 complete(isci_device->cmp);
103 kmem_cache_free(isci_kmem_cache, isci_device);
104} 99}
105 100
106 101
@@ -259,25 +254,27 @@ void isci_remote_device_nuke_requests(
259 * pointer to new isci_remote_device. 254 * pointer to new isci_remote_device.
260 */ 255 */
261static struct isci_remote_device * 256static struct isci_remote_device *
262isci_remote_device_alloc(struct isci_host *isci_host, struct isci_port *port) 257isci_remote_device_alloc(struct isci_host *ihost, struct isci_port *iport)
263{ 258{
264 struct isci_remote_device *isci_device; 259 struct isci_remote_device *idev;
260 int i;
265 261
266 isci_device = kmem_cache_zalloc(isci_kmem_cache, GFP_KERNEL); 262 for (i = 0; i < SCI_MAX_REMOTE_DEVICES; i++) {
263 idev = idev_by_id(ihost, i);
264 if (!test_and_set_bit(IDEV_ALLOCATED, &idev->flags))
265 break;
266 }
267 267
268 if (!isci_device) { 268 if (i >= SCI_MAX_REMOTE_DEVICES) {
269 dev_warn(&isci_host->pdev->dev, "%s: failed\n", __func__); 269 dev_warn(&ihost->pdev->dev, "%s: failed\n", __func__);
270 return NULL; 270 return NULL;
271 } 271 }
272 272
273 INIT_LIST_HEAD(&isci_device->reqs_in_process); 273 BUG_ON(!list_empty(&idev->reqs_in_process));
274 INIT_LIST_HEAD(&isci_device->node); 274 BUG_ON(!list_empty(&idev->node));
275 275 isci_remote_device_change_state(idev, isci_freed);
276 spin_lock_init(&isci_device->state_lock);
277 isci_remote_device_change_state(isci_device, isci_freed);
278
279 return isci_device;
280 276
277 return idev;
281} 278}
282 279
283/** 280/**
@@ -381,24 +378,22 @@ enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_rem
381{ 378{
382 enum sci_status status; 379 enum sci_status status;
383 unsigned long flags; 380 unsigned long flags;
384 DECLARE_COMPLETION_ONSTACK(completion);
385 381
386 dev_dbg(&ihost->pdev->dev, 382 dev_dbg(&ihost->pdev->dev,
387 "%s: isci_device = %p\n", __func__, idev); 383 "%s: isci_device = %p\n", __func__, idev);
388 384
389 isci_remote_device_change_state(idev, isci_stopping); 385 isci_remote_device_change_state(idev, isci_stopping);
390 set_bit(IDEV_STOP_PENDING, &idev->flags); 386 set_bit(IDEV_STOP_PENDING, &idev->flags);
391 idev->cmp = &completion;
392 387
393 spin_lock_irqsave(&ihost->scic_lock, flags); 388 spin_lock_irqsave(&ihost->scic_lock, flags);
394
395 status = scic_remote_device_stop(to_sci_dev(idev), 50); 389 status = scic_remote_device_stop(to_sci_dev(idev), 50);
396
397 spin_unlock_irqrestore(&ihost->scic_lock, flags); 390 spin_unlock_irqrestore(&ihost->scic_lock, flags);
398 391
399 /* Wait for the stop complete callback. */ 392 /* Wait for the stop complete callback. */
400 if (status == SCI_SUCCESS) 393 if (status == SCI_SUCCESS) {
401 wait_for_device_stop(ihost, idev); 394 wait_for_device_stop(ihost, idev);
395 clear_bit(IDEV_ALLOCATED, &idev->flags);
396 }
402 397
403 dev_dbg(&ihost->pdev->dev, 398 dev_dbg(&ihost->pdev->dev,
404 "%s: idev = %p - after completion wait\n", 399 "%s: idev = %p - after completion wait\n",
@@ -469,6 +464,8 @@ int isci_remote_device_found(struct domain_device *domain_dev)
469 return -ENODEV; 464 return -ENODEV;
470 465
471 isci_device = isci_remote_device_alloc(isci_host, isci_port); 466 isci_device = isci_remote_device_alloc(isci_host, isci_port);
467 if (!isci_device)
468 return -ENODEV;
472 469
473 INIT_LIST_HEAD(&isci_device->node); 470 INIT_LIST_HEAD(&isci_device->node);
474 domain_dev->lldd_dev = isci_device; 471 domain_dev->lldd_dev = isci_device;
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h
index 3c22137c9f65..f45a5f064fce 100644
--- a/drivers/scsi/isci/remote_device.h
+++ b/drivers/scsi/isci/remote_device.h
@@ -63,8 +63,8 @@ struct isci_remote_device {
63 enum isci_status status; 63 enum isci_status status;
64 #define IDEV_START_PENDING 0 64 #define IDEV_START_PENDING 0
65 #define IDEV_STOP_PENDING 1 65 #define IDEV_STOP_PENDING 1
66 #define IDEV_ALLOCATED 2
66 unsigned long flags; 67 unsigned long flags;
67 struct completion *cmp;
68 struct isci_port *isci_port; 68 struct isci_port *isci_port;
69 struct domain_device *domain_dev; 69 struct domain_device *domain_dev;
70 struct list_head node; 70 struct list_head node;