diff options
author | Don Brace <don.brace@microsemi.com> | 2016-04-27 18:14:17 -0400 |
---|---|---|
committer | Martin K. Petersen <martin.petersen@oracle.com> | 2016-04-29 19:08:24 -0400 |
commit | ba74fdc411b84064d7abe4b10d0708f6dad03eb2 (patch) | |
tree | 71eea04b9c9745857cd4399304285335904f1b08 /drivers/scsi | |
parent | 064d1b1d2d8e0b90f1a0a49112eca0d5c2a09b16 (diff) |
hpsa: correct handling of HBA device removal
Need to report HBA device removal faster than the
event handler polling interval.
Stop I/O to the removed disk and wait for all
I/O operations to flush before removing the device.
Reviewed-by: Scott Teel <scott.teel@microsemi.com>
Reviewed-by: Kevin Barnett <kevin.barnett@microsemi.com>
Signed-off-by: Don Brace <don.brace@microsemi.com>
Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/hpsa.c | 89 | ||||
-rw-r--r-- | drivers/scsi/hpsa.h | 1 |
2 files changed, 85 insertions, 5 deletions
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 4f8e3285baa9..034624f1ca64 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c | |||
@@ -294,6 +294,9 @@ static void hpsa_disable_rld_caching(struct ctlr_info *h); | |||
294 | static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h, | 294 | static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h, |
295 | struct ReportExtendedLUNdata *buf, int bufsize); | 295 | struct ReportExtendedLUNdata *buf, int bufsize); |
296 | static int hpsa_luns_changed(struct ctlr_info *h); | 296 | static int hpsa_luns_changed(struct ctlr_info *h); |
297 | static bool hpsa_cmd_dev_match(struct ctlr_info *h, struct CommandList *c, | ||
298 | struct hpsa_scsi_dev_t *dev, | ||
299 | unsigned char *scsi3addr); | ||
297 | 300 | ||
298 | static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev) | 301 | static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev) |
299 | { | 302 | { |
@@ -1745,6 +1748,51 @@ static int hpsa_add_device(struct ctlr_info *h, struct hpsa_scsi_dev_t *device) | |||
1745 | return rc; | 1748 | return rc; |
1746 | } | 1749 | } |
1747 | 1750 | ||
1751 | static int hpsa_find_outstanding_commands_for_dev(struct ctlr_info *h, | ||
1752 | struct hpsa_scsi_dev_t *dev) | ||
1753 | { | ||
1754 | int i; | ||
1755 | int count = 0; | ||
1756 | |||
1757 | for (i = 0; i < h->nr_cmds; i++) { | ||
1758 | struct CommandList *c = h->cmd_pool + i; | ||
1759 | int refcount = atomic_inc_return(&c->refcount); | ||
1760 | |||
1761 | if (refcount > 1 && hpsa_cmd_dev_match(h, c, dev, | ||
1762 | dev->scsi3addr)) { | ||
1763 | unsigned long flags; | ||
1764 | |||
1765 | spin_lock_irqsave(&h->lock, flags); /* Implied MB */ | ||
1766 | if (!hpsa_is_cmd_idle(c)) | ||
1767 | ++count; | ||
1768 | spin_unlock_irqrestore(&h->lock, flags); | ||
1769 | } | ||
1770 | |||
1771 | cmd_free(h, c); | ||
1772 | } | ||
1773 | |||
1774 | return count; | ||
1775 | } | ||
1776 | |||
1777 | static void hpsa_wait_for_outstanding_commands_for_dev(struct ctlr_info *h, | ||
1778 | struct hpsa_scsi_dev_t *device) | ||
1779 | { | ||
1780 | int cmds = 0; | ||
1781 | int waits = 0; | ||
1782 | |||
1783 | while (1) { | ||
1784 | cmds = hpsa_find_outstanding_commands_for_dev(h, device); | ||
1785 | if (cmds == 0) | ||
1786 | break; | ||
1787 | if (++waits > 20) | ||
1788 | break; | ||
1789 | dev_warn(&h->pdev->dev, | ||
1790 | "%s: removing device with %d outstanding commands!\n", | ||
1791 | __func__, cmds); | ||
1792 | msleep(1000); | ||
1793 | } | ||
1794 | } | ||
1795 | |||
1748 | static void hpsa_remove_device(struct ctlr_info *h, | 1796 | static void hpsa_remove_device(struct ctlr_info *h, |
1749 | struct hpsa_scsi_dev_t *device) | 1797 | struct hpsa_scsi_dev_t *device) |
1750 | { | 1798 | { |
@@ -1768,8 +1816,13 @@ static void hpsa_remove_device(struct ctlr_info *h, | |||
1768 | hpsa_show_dev_msg(KERN_WARNING, h, device, | 1816 | hpsa_show_dev_msg(KERN_WARNING, h, device, |
1769 | "didn't find device for removal."); | 1817 | "didn't find device for removal."); |
1770 | } | 1818 | } |
1771 | } else /* HBA */ | 1819 | } else { /* HBA */ |
1820 | |||
1821 | device->removed = 1; | ||
1822 | hpsa_wait_for_outstanding_commands_for_dev(h, device); | ||
1823 | |||
1772 | hpsa_remove_sas_device(device); | 1824 | hpsa_remove_sas_device(device); |
1825 | } | ||
1773 | } | 1826 | } |
1774 | 1827 | ||
1775 | static void adjust_hpsa_scsi_table(struct ctlr_info *h, | 1828 | static void adjust_hpsa_scsi_table(struct ctlr_info *h, |
@@ -2171,7 +2224,8 @@ static void hpsa_unmap_sg_chain_block(struct ctlr_info *h, | |||
2171 | static int handle_ioaccel_mode2_error(struct ctlr_info *h, | 2224 | static int handle_ioaccel_mode2_error(struct ctlr_info *h, |
2172 | struct CommandList *c, | 2225 | struct CommandList *c, |
2173 | struct scsi_cmnd *cmd, | 2226 | struct scsi_cmnd *cmd, |
2174 | struct io_accel2_cmd *c2) | 2227 | struct io_accel2_cmd *c2, |
2228 | struct hpsa_scsi_dev_t *dev) | ||
2175 | { | 2229 | { |
2176 | int data_len; | 2230 | int data_len; |
2177 | int retry = 0; | 2231 | int retry = 0; |
@@ -2235,8 +2289,27 @@ static int handle_ioaccel_mode2_error(struct ctlr_info *h, | |||
2235 | case IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE: | 2289 | case IOACCEL2_STATUS_SR_NO_PATH_TO_DEVICE: |
2236 | case IOACCEL2_STATUS_SR_INVALID_DEVICE: | 2290 | case IOACCEL2_STATUS_SR_INVALID_DEVICE: |
2237 | case IOACCEL2_STATUS_SR_IOACCEL_DISABLED: | 2291 | case IOACCEL2_STATUS_SR_IOACCEL_DISABLED: |
2238 | /* We will get an event from ctlr to trigger rescan */ | 2292 | /* |
2239 | retry = 1; | 2293 | * Did an HBA disk disappear? We will eventually |
2294 | * get a state change event from the controller but | ||
2295 | * in the meantime, we need to tell the OS that the | ||
2296 | * HBA disk is no longer there and stop I/O | ||
2297 | * from going down. This allows the potential re-insert | ||
2298 | * of the disk to get the same device node. | ||
2299 | */ | ||
2300 | if (dev->physical_device && dev->expose_device) { | ||
2301 | cmd->result = DID_NO_CONNECT << 16; | ||
2302 | dev->removed = 1; | ||
2303 | h->drv_req_rescan = 1; | ||
2304 | dev_warn(&h->pdev->dev, | ||
2305 | "%s: device is gone!\n", __func__); | ||
2306 | } else | ||
2307 | /* | ||
2308 | * Retry by sending down the RAID path. | ||
2309 | * We will get an event from ctlr to | ||
2310 | * trigger rescan regardless. | ||
2311 | */ | ||
2312 | retry = 1; | ||
2240 | break; | 2313 | break; |
2241 | default: | 2314 | default: |
2242 | retry = 1; | 2315 | retry = 1; |
@@ -2368,7 +2441,7 @@ static void process_ioaccel2_completion(struct ctlr_info *h, | |||
2368 | return hpsa_retry_cmd(h, c); | 2441 | return hpsa_retry_cmd(h, c); |
2369 | } | 2442 | } |
2370 | 2443 | ||
2371 | if (handle_ioaccel_mode2_error(h, c, cmd, c2)) | 2444 | if (handle_ioaccel_mode2_error(h, c, cmd, c2, dev)) |
2372 | return hpsa_retry_cmd(h, c); | 2445 | return hpsa_retry_cmd(h, c); |
2373 | 2446 | ||
2374 | return hpsa_cmd_free_and_done(h, c, cmd); | 2447 | return hpsa_cmd_free_and_done(h, c, cmd); |
@@ -5263,6 +5336,12 @@ static int hpsa_scsi_queue_command(struct Scsi_Host *sh, struct scsi_cmnd *cmd) | |||
5263 | 5336 | ||
5264 | dev = cmd->device->hostdata; | 5337 | dev = cmd->device->hostdata; |
5265 | if (!dev) { | 5338 | if (!dev) { |
5339 | cmd->result = NOT_READY << 16; /* host byte */ | ||
5340 | cmd->scsi_done(cmd); | ||
5341 | return 0; | ||
5342 | } | ||
5343 | |||
5344 | if (dev->removed) { | ||
5266 | cmd->result = DID_NO_CONNECT << 16; | 5345 | cmd->result = DID_NO_CONNECT << 16; |
5267 | cmd->scsi_done(cmd); | 5346 | cmd->scsi_done(cmd); |
5268 | return 0; | 5347 | return 0; |
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index d06bb7417e36..a1487e67f7a1 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h | |||
@@ -63,6 +63,7 @@ struct hpsa_scsi_dev_t { | |||
63 | unsigned char scsi3addr[8]; /* as presented to the HW */ | 63 | unsigned char scsi3addr[8]; /* as presented to the HW */ |
64 | u8 physical_device : 1; | 64 | u8 physical_device : 1; |
65 | u8 expose_device; | 65 | u8 expose_device; |
66 | u8 removed : 1; /* device is marked for death */ | ||
66 | #define RAID_CTLR_LUNID "\0\0\0\0\0\0\0\0" | 67 | #define RAID_CTLR_LUNID "\0\0\0\0\0\0\0\0" |
67 | unsigned char device_id[16]; /* from inquiry pg. 0x83 */ | 68 | unsigned char device_id[16]; /* from inquiry pg. 0x83 */ |
68 | u64 sas_address; | 69 | u64 sas_address; |