diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-06-13 17:39:44 -0700 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2011-07-03 04:04:51 -0700 |
commit | 209fae14fabfd48525e5630bebbbd4ca15090c60 (patch) | |
tree | b251b9b394b3493cc15242ea31002abcb4e9bb59 /drivers/scsi/isci/remote_device.h | |
parent | 360b03ed178a4fe3971b0a098d8feeb53333481b (diff) | |
download | blackbird-op-linux-209fae14fabfd48525e5630bebbbd4ca15090c60.tar.gz blackbird-op-linux-209fae14fabfd48525e5630bebbbd4ca15090c60.zip |
isci: atomic device lookup and reference counting
We have unsafe references to remote devices that are notified to
disappear at lldd_dev_gone. In order to clean this up we need a single
canonical source for device lookups and stable references once a lookup
succeeds. Towards that end guarantee that domain_device.lldd_dev is
NULL as soon as we start the process of stopping a device. Any code
path that wants to safely lookup a remote device must do so through
task->dev->lldd_dev (isci_lookup_device()).
For in-flight references outside of scic_lock we need reference counting
to ensure that the device is not recycled before we are done with it.
Simplify device back references to just scic_sds_request.target_device
which is now the only permissible internal reference that is maintained
relative to the reference count.
There were two occasions where we wanted new i/o's to be treated as
SAS_TASK_UNDELIVERED but where the domain_dev->lldd_dev link is still
intact. Introduce a 'gone' flag to prevent i/o while waiting for libsas
to take action on the port down event.
One 'core' leftover is that we currently call
scic_remote_device_destruct() from isci_remote_device_deconstruct()
which is called when the 'core' says the device is stopped. It would be
more natural for the final put to trigger
isci_remote_device_deconstruct() but this implementation is deferred as
it requires other changes.
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/scsi/isci/remote_device.h')
-rw-r--r-- | drivers/scsi/isci/remote_device.h | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h index 2b6a5bb7bd60..05842b5f1e3b 100644 --- a/drivers/scsi/isci/remote_device.h +++ b/drivers/scsi/isci/remote_device.h @@ -56,6 +56,7 @@ #ifndef _ISCI_REMOTE_DEVICE_H_ #define _ISCI_REMOTE_DEVICE_H_ #include <scsi/libsas.h> +#include <linux/kref.h> #include "scu_remote_node_context.h" #include "remote_node_context.h" #include "port.h" @@ -134,7 +135,9 @@ struct isci_remote_device { #define IDEV_STOP_PENDING 1 #define IDEV_ALLOCATED 2 #define IDEV_EH 3 + #define IDEV_GONE 4 unsigned long flags; + struct kref kref; struct isci_port *isci_port; struct domain_device *domain_dev; struct list_head node; @@ -145,6 +148,26 @@ struct isci_remote_device { #define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000 +/* device reference routines must be called under scic_lock */ +static inline struct isci_remote_device *isci_lookup_device(struct domain_device *dev) +{ + struct isci_remote_device *idev = dev->lldd_dev; + + if (idev && !test_bit(IDEV_GONE, &idev->flags)) { + kref_get(&idev->kref); + return idev; + } + + return NULL; +} + +void isci_remote_device_release(struct kref *kref); +static inline void isci_put_device(struct isci_remote_device *idev) +{ + if (idev) + kref_put(&idev->kref, isci_remote_device_release); +} + enum sci_status isci_remote_device_stop(struct isci_host *ihost, struct isci_remote_device *idev); void isci_remote_device_nuke_requests(struct isci_host *ihost, |