diff options
author | James Smart <James.Smart@Emulex.Com> | 2006-08-18 17:33:29 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-09-04 22:22:05 -0400 |
commit | 0f29b966d60e9a4f5ecff9f3832257b38aea4f13 (patch) | |
tree | 0b14cbcdf74aee4676c49353f816ae5f98c504ee /drivers/scsi/scsi_transport_fc.c | |
parent | ae36764a230ff6a278ed93735acf5fcda08f2786 (diff) |
[SCSI] FC transport: Add dev_loss_tmo callbacks, and new fast_io_fail_tmo w/ callback
This patch adds the following functionality to the FC transport:
- dev_loss_tmo LLDD callback :
Called to essentially confirm the deletion of an rport. Thus, it is
called whenever the dev_loss_tmo fires, or when the rport is deleted
due to other circumstances (module unload, etc). It is expected that
the callback will initiate the termination of any outstanding i/o on
the rport.
- fast_io_fail_tmo and LLD callback:
There are some cases where it may take a long while to truly determine
device loss, but the system is in a multipathing configuration that if
the i/o was failed quickly (faster than dev_loss_tmo), it could be
redirected to a different path and completed sooner.
Many thanks to Mike Reed who cleaned up the initial RFC in support
of this post.
The original RFC is at:
http://marc.theaimsgroup.com/?l=linux-scsi&m=115505981027246&w=2
Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/scsi_transport_fc.c')
-rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 134 |
1 files changed, 123 insertions, 11 deletions
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 293188cbff8c..4ab176ed480d 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c | |||
@@ -242,6 +242,7 @@ fc_bitfield_name_search(remote_port_roles, fc_remote_port_role_names) | |||
242 | 242 | ||
243 | 243 | ||
244 | static void fc_timeout_deleted_rport(void *data); | 244 | static void fc_timeout_deleted_rport(void *data); |
245 | static void fc_timeout_fail_rport_io(void *data); | ||
245 | static void fc_scsi_scan_rport(void *data); | 246 | static void fc_scsi_scan_rport(void *data); |
246 | 247 | ||
247 | /* | 248 | /* |
@@ -249,7 +250,7 @@ static void fc_scsi_scan_rport(void *data); | |||
249 | * Increase these values if you add attributes | 250 | * Increase these values if you add attributes |
250 | */ | 251 | */ |
251 | #define FC_STARGET_NUM_ATTRS 3 | 252 | #define FC_STARGET_NUM_ATTRS 3 |
252 | #define FC_RPORT_NUM_ATTRS 9 | 253 | #define FC_RPORT_NUM_ATTRS 10 |
253 | #define FC_HOST_NUM_ATTRS 17 | 254 | #define FC_HOST_NUM_ATTRS 17 |
254 | 255 | ||
255 | struct fc_internal { | 256 | struct fc_internal { |
@@ -622,11 +623,14 @@ store_fc_rport_##field(struct class_device *cdev, const char *buf, \ | |||
622 | struct fc_rport *rport = transport_class_to_rport(cdev); \ | 623 | struct fc_rport *rport = transport_class_to_rport(cdev); \ |
623 | struct Scsi_Host *shost = rport_to_shost(rport); \ | 624 | struct Scsi_Host *shost = rport_to_shost(rport); \ |
624 | struct fc_internal *i = to_fc_internal(shost->transportt); \ | 625 | struct fc_internal *i = to_fc_internal(shost->transportt); \ |
626 | char *cp; \ | ||
625 | if ((rport->port_state == FC_PORTSTATE_BLOCKED) || \ | 627 | if ((rport->port_state == FC_PORTSTATE_BLOCKED) || \ |
626 | (rport->port_state == FC_PORTSTATE_DELETED) || \ | 628 | (rport->port_state == FC_PORTSTATE_DELETED) || \ |
627 | (rport->port_state == FC_PORTSTATE_NOTPRESENT)) \ | 629 | (rport->port_state == FC_PORTSTATE_NOTPRESENT)) \ |
628 | return -EBUSY; \ | 630 | return -EBUSY; \ |
629 | val = simple_strtoul(buf, NULL, 0); \ | 631 | val = simple_strtoul(buf, &cp, 0); \ |
632 | if (*cp && (*cp != '\n')) \ | ||
633 | return -EINVAL; \ | ||
630 | i->f->set_rport_##field(rport, val); \ | 634 | i->f->set_rport_##field(rport, val); \ |
631 | return count; \ | 635 | return count; \ |
632 | } | 636 | } |
@@ -708,6 +712,13 @@ static FC_CLASS_DEVICE_ATTR(rport, title, S_IRUGO, \ | |||
708 | if (i->f->show_rport_##field) \ | 712 | if (i->f->show_rport_##field) \ |
709 | count++ | 713 | count++ |
710 | 714 | ||
715 | #define SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(field) \ | ||
716 | { \ | ||
717 | i->private_rport_attrs[count] = class_device_attr_rport_##field; \ | ||
718 | i->rport_attrs[count] = &i->private_rport_attrs[count]; \ | ||
719 | count++; \ | ||
720 | } | ||
721 | |||
711 | 722 | ||
712 | /* The FC Transport Remote Port Attributes: */ | 723 | /* The FC Transport Remote Port Attributes: */ |
713 | 724 | ||
@@ -740,12 +751,14 @@ store_fc_rport_dev_loss_tmo(struct class_device *cdev, const char *buf, | |||
740 | struct fc_rport *rport = transport_class_to_rport(cdev); | 751 | struct fc_rport *rport = transport_class_to_rport(cdev); |
741 | struct Scsi_Host *shost = rport_to_shost(rport); | 752 | struct Scsi_Host *shost = rport_to_shost(rport); |
742 | struct fc_internal *i = to_fc_internal(shost->transportt); | 753 | struct fc_internal *i = to_fc_internal(shost->transportt); |
754 | char *cp; | ||
743 | if ((rport->port_state == FC_PORTSTATE_BLOCKED) || | 755 | if ((rport->port_state == FC_PORTSTATE_BLOCKED) || |
744 | (rport->port_state == FC_PORTSTATE_DELETED) || | 756 | (rport->port_state == FC_PORTSTATE_DELETED) || |
745 | (rport->port_state == FC_PORTSTATE_NOTPRESENT)) | 757 | (rport->port_state == FC_PORTSTATE_NOTPRESENT)) |
746 | return -EBUSY; | 758 | return -EBUSY; |
747 | val = simple_strtoul(buf, NULL, 0); | 759 | val = simple_strtoul(buf, &cp, 0); |
748 | if ((val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)) | 760 | if ((*cp && (*cp != '\n')) || |
761 | (val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)) | ||
749 | return -EINVAL; | 762 | return -EINVAL; |
750 | i->f->set_rport_dev_loss_tmo(rport, val); | 763 | i->f->set_rport_dev_loss_tmo(rport, val); |
751 | return count; | 764 | return count; |
@@ -795,6 +808,44 @@ static FC_CLASS_DEVICE_ATTR(rport, roles, S_IRUGO, | |||
795 | fc_private_rport_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN); | 808 | fc_private_rport_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN); |
796 | fc_private_rport_rd_attr(scsi_target_id, "%d\n", 20); | 809 | fc_private_rport_rd_attr(scsi_target_id, "%d\n", 20); |
797 | 810 | ||
811 | /* | ||
812 | * fast_io_fail_tmo attribute | ||
813 | */ | ||
814 | static ssize_t | ||
815 | show_fc_rport_fast_io_fail_tmo (struct class_device *cdev, char *buf) | ||
816 | { | ||
817 | struct fc_rport *rport = transport_class_to_rport(cdev); | ||
818 | |||
819 | if (rport->fast_io_fail_tmo == -1) | ||
820 | return snprintf(buf, 5, "off\n"); | ||
821 | return snprintf(buf, 20, "%d\n", rport->fast_io_fail_tmo); | ||
822 | } | ||
823 | |||
824 | static ssize_t | ||
825 | store_fc_rport_fast_io_fail_tmo(struct class_device *cdev, const char *buf, | ||
826 | size_t count) | ||
827 | { | ||
828 | int val; | ||
829 | char *cp; | ||
830 | struct fc_rport *rport = transport_class_to_rport(cdev); | ||
831 | |||
832 | if ((rport->port_state == FC_PORTSTATE_BLOCKED) || | ||
833 | (rport->port_state == FC_PORTSTATE_DELETED) || | ||
834 | (rport->port_state == FC_PORTSTATE_NOTPRESENT)) | ||
835 | return -EBUSY; | ||
836 | if (strncmp(buf, "off", 3) == 0) | ||
837 | rport->fast_io_fail_tmo = -1; | ||
838 | else { | ||
839 | val = simple_strtoul(buf, &cp, 0); | ||
840 | if ((*cp && (*cp != '\n')) || | ||
841 | (val < 0) || (val >= rport->dev_loss_tmo)) | ||
842 | return -EINVAL; | ||
843 | rport->fast_io_fail_tmo = val; | ||
844 | } | ||
845 | return count; | ||
846 | } | ||
847 | static FC_CLASS_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR, | ||
848 | show_fc_rport_fast_io_fail_tmo, store_fc_rport_fast_io_fail_tmo); | ||
798 | 849 | ||
799 | 850 | ||
800 | /* | 851 | /* |
@@ -880,8 +931,11 @@ store_fc_host_##field(struct class_device *cdev, const char *buf, \ | |||
880 | int val; \ | 931 | int val; \ |
881 | struct Scsi_Host *shost = transport_class_to_shost(cdev); \ | 932 | struct Scsi_Host *shost = transport_class_to_shost(cdev); \ |
882 | struct fc_internal *i = to_fc_internal(shost->transportt); \ | 933 | struct fc_internal *i = to_fc_internal(shost->transportt); \ |
934 | char *cp; \ | ||
883 | \ | 935 | \ |
884 | val = simple_strtoul(buf, NULL, 0); \ | 936 | val = simple_strtoul(buf, &cp, 0); \ |
937 | if (*cp && (*cp != '\n')) \ | ||
938 | return -EINVAL; \ | ||
885 | i->f->set_host_##field(shost, val); \ | 939 | i->f->set_host_##field(shost, val); \ |
886 | return count; \ | 940 | return count; \ |
887 | } | 941 | } |
@@ -1481,6 +1535,8 @@ fc_attach_transport(struct fc_function_template *ft) | |||
1481 | SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(roles); | 1535 | SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(roles); |
1482 | SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_state); | 1536 | SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_state); |
1483 | SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(scsi_target_id); | 1537 | SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(scsi_target_id); |
1538 | if (ft->terminate_rport_io) | ||
1539 | SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(fast_io_fail_tmo); | ||
1484 | 1540 | ||
1485 | BUG_ON(count > FC_RPORT_NUM_ATTRS); | 1541 | BUG_ON(count > FC_RPORT_NUM_ATTRS); |
1486 | 1542 | ||
@@ -1552,7 +1608,7 @@ fc_flush_work(struct Scsi_Host *shost) | |||
1552 | * @delay: jiffies to delay the work queuing | 1608 | * @delay: jiffies to delay the work queuing |
1553 | * | 1609 | * |
1554 | * Return value: | 1610 | * Return value: |
1555 | * 0 on success / != 0 for error | 1611 | * 1 on success / 0 already queued / < 0 for error |
1556 | **/ | 1612 | **/ |
1557 | static int | 1613 | static int |
1558 | fc_queue_devloss_work(struct Scsi_Host *shost, struct work_struct *work, | 1614 | fc_queue_devloss_work(struct Scsi_Host *shost, struct work_struct *work, |
@@ -1567,6 +1623,9 @@ fc_queue_devloss_work(struct Scsi_Host *shost, struct work_struct *work, | |||
1567 | return -EINVAL; | 1623 | return -EINVAL; |
1568 | } | 1624 | } |
1569 | 1625 | ||
1626 | if (delay == 0) | ||
1627 | return queue_work(fc_host_devloss_work_q(shost), work); | ||
1628 | |||
1570 | return queue_delayed_work(fc_host_devloss_work_q(shost), work, delay); | 1629 | return queue_delayed_work(fc_host_devloss_work_q(shost), work, delay); |
1571 | } | 1630 | } |
1572 | 1631 | ||
@@ -1659,10 +1718,23 @@ fc_starget_delete(void *data) | |||
1659 | struct fc_rport *rport = (struct fc_rport *)data; | 1718 | struct fc_rport *rport = (struct fc_rport *)data; |
1660 | struct Scsi_Host *shost = rport_to_shost(rport); | 1719 | struct Scsi_Host *shost = rport_to_shost(rport); |
1661 | unsigned long flags; | 1720 | unsigned long flags; |
1721 | struct fc_internal *i = to_fc_internal(shost->transportt); | ||
1722 | |||
1723 | /* | ||
1724 | * Involve the LLDD if possible. All io on the rport is to | ||
1725 | * be terminated, either as part of the dev_loss_tmo callback | ||
1726 | * processing, or via the terminate_rport_io function. | ||
1727 | */ | ||
1728 | if (i->f->dev_loss_tmo_callbk) | ||
1729 | i->f->dev_loss_tmo_callbk(rport); | ||
1730 | else if (i->f->terminate_rport_io) | ||
1731 | i->f->terminate_rport_io(rport); | ||
1662 | 1732 | ||
1663 | spin_lock_irqsave(shost->host_lock, flags); | 1733 | spin_lock_irqsave(shost->host_lock, flags); |
1664 | if (rport->flags & FC_RPORT_DEVLOSS_PENDING) { | 1734 | if (rport->flags & FC_RPORT_DEVLOSS_PENDING) { |
1665 | spin_unlock_irqrestore(shost->host_lock, flags); | 1735 | spin_unlock_irqrestore(shost->host_lock, flags); |
1736 | if (!cancel_delayed_work(&rport->fail_io_work)) | ||
1737 | fc_flush_devloss(shost); | ||
1666 | if (!cancel_delayed_work(&rport->dev_loss_work)) | 1738 | if (!cancel_delayed_work(&rport->dev_loss_work)) |
1667 | fc_flush_devloss(shost); | 1739 | fc_flush_devloss(shost); |
1668 | spin_lock_irqsave(shost->host_lock, flags); | 1740 | spin_lock_irqsave(shost->host_lock, flags); |
@@ -1685,10 +1757,7 @@ fc_rport_final_delete(void *data) | |||
1685 | struct fc_rport *rport = (struct fc_rport *)data; | 1757 | struct fc_rport *rport = (struct fc_rport *)data; |
1686 | struct device *dev = &rport->dev; | 1758 | struct device *dev = &rport->dev; |
1687 | struct Scsi_Host *shost = rport_to_shost(rport); | 1759 | struct Scsi_Host *shost = rport_to_shost(rport); |
1688 | 1760 | struct fc_internal *i = to_fc_internal(shost->transportt); | |
1689 | /* Delete SCSI target and sdevs */ | ||
1690 | if (rport->scsi_target_id != -1) | ||
1691 | fc_starget_delete(data); | ||
1692 | 1761 | ||
1693 | /* | 1762 | /* |
1694 | * if a scan is pending, flush the SCSI Host work_q so that | 1763 | * if a scan is pending, flush the SCSI Host work_q so that |
@@ -1697,6 +1766,14 @@ fc_rport_final_delete(void *data) | |||
1697 | if (rport->flags & FC_RPORT_SCAN_PENDING) | 1766 | if (rport->flags & FC_RPORT_SCAN_PENDING) |
1698 | scsi_flush_work(shost); | 1767 | scsi_flush_work(shost); |
1699 | 1768 | ||
1769 | /* Delete SCSI target and sdevs */ | ||
1770 | if (rport->scsi_target_id != -1) | ||
1771 | fc_starget_delete(data); | ||
1772 | else if (i->f->dev_loss_tmo_callbk) | ||
1773 | i->f->dev_loss_tmo_callbk(rport); | ||
1774 | else if (i->f->terminate_rport_io) | ||
1775 | i->f->terminate_rport_io(rport); | ||
1776 | |||
1700 | transport_remove_device(dev); | 1777 | transport_remove_device(dev); |
1701 | device_del(dev); | 1778 | device_del(dev); |
1702 | transport_destroy_device(dev); | 1779 | transport_destroy_device(dev); |
@@ -1748,8 +1825,10 @@ fc_rport_create(struct Scsi_Host *shost, int channel, | |||
1748 | if (fci->f->dd_fcrport_size) | 1825 | if (fci->f->dd_fcrport_size) |
1749 | rport->dd_data = &rport[1]; | 1826 | rport->dd_data = &rport[1]; |
1750 | rport->channel = channel; | 1827 | rport->channel = channel; |
1828 | rport->fast_io_fail_tmo = -1; | ||
1751 | 1829 | ||
1752 | INIT_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport, rport); | 1830 | INIT_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport, rport); |
1831 | INIT_WORK(&rport->fail_io_work, fc_timeout_fail_rport_io, rport); | ||
1753 | INIT_WORK(&rport->scan_work, fc_scsi_scan_rport, rport); | 1832 | INIT_WORK(&rport->scan_work, fc_scsi_scan_rport, rport); |
1754 | INIT_WORK(&rport->stgt_delete_work, fc_starget_delete, rport); | 1833 | INIT_WORK(&rport->stgt_delete_work, fc_starget_delete, rport); |
1755 | INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete, rport); | 1834 | INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete, rport); |
@@ -1913,11 +1992,13 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, | |||
1913 | /* restart the target */ | 1992 | /* restart the target */ |
1914 | 1993 | ||
1915 | /* | 1994 | /* |
1916 | * Stop the target timer first. Take no action | 1995 | * Stop the target timers first. Take no action |
1917 | * on the del_timer failure as the state | 1996 | * on the del_timer failure as the state |
1918 | * machine state change will validate the | 1997 | * machine state change will validate the |
1919 | * transaction. | 1998 | * transaction. |
1920 | */ | 1999 | */ |
2000 | if (!cancel_delayed_work(&rport->fail_io_work)) | ||
2001 | fc_flush_devloss(shost); | ||
1921 | if (!cancel_delayed_work(work)) | 2002 | if (!cancel_delayed_work(work)) |
1922 | fc_flush_devloss(shost); | 2003 | fc_flush_devloss(shost); |
1923 | 2004 | ||
@@ -2061,6 +2142,7 @@ void | |||
2061 | fc_remote_port_delete(struct fc_rport *rport) | 2142 | fc_remote_port_delete(struct fc_rport *rport) |
2062 | { | 2143 | { |
2063 | struct Scsi_Host *shost = rport_to_shost(rport); | 2144 | struct Scsi_Host *shost = rport_to_shost(rport); |
2145 | struct fc_internal *i = to_fc_internal(shost->transportt); | ||
2064 | int timeout = rport->dev_loss_tmo; | 2146 | int timeout = rport->dev_loss_tmo; |
2065 | unsigned long flags; | 2147 | unsigned long flags; |
2066 | 2148 | ||
@@ -2091,6 +2173,12 @@ fc_remote_port_delete(struct fc_rport *rport) | |||
2091 | 2173 | ||
2092 | scsi_target_block(&rport->dev); | 2174 | scsi_target_block(&rport->dev); |
2093 | 2175 | ||
2176 | /* see if we need to kill io faster than waiting for device loss */ | ||
2177 | if ((rport->fast_io_fail_tmo != -1) && | ||
2178 | (rport->fast_io_fail_tmo < timeout) && (i->f->terminate_rport_io)) | ||
2179 | fc_queue_devloss_work(shost, &rport->fail_io_work, | ||
2180 | rport->fast_io_fail_tmo * HZ); | ||
2181 | |||
2094 | /* cap the length the devices can be blocked until they are deleted */ | 2182 | /* cap the length the devices can be blocked until they are deleted */ |
2095 | fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ); | 2183 | fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ); |
2096 | } | 2184 | } |
@@ -2150,6 +2238,8 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) | |||
2150 | * machine state change will validate the | 2238 | * machine state change will validate the |
2151 | * transaction. | 2239 | * transaction. |
2152 | */ | 2240 | */ |
2241 | if (!cancel_delayed_work(&rport->fail_io_work)) | ||
2242 | fc_flush_devloss(shost); | ||
2153 | if (!cancel_delayed_work(&rport->dev_loss_work)) | 2243 | if (!cancel_delayed_work(&rport->dev_loss_work)) |
2154 | fc_flush_devloss(shost); | 2244 | fc_flush_devloss(shost); |
2155 | 2245 | ||
@@ -2271,6 +2361,28 @@ fc_timeout_deleted_rport(void *data) | |||
2271 | } | 2361 | } |
2272 | 2362 | ||
2273 | /** | 2363 | /** |
2364 | * fc_timeout_fail_rport_io - Timeout handler for a fast io failing on a | ||
2365 | * disconnected SCSI target. | ||
2366 | * | ||
2367 | * @data: rport to terminate io on. | ||
2368 | * | ||
2369 | * Notes: Only requests the failure of the io, not that all are flushed | ||
2370 | * prior to returning. | ||
2371 | **/ | ||
2372 | static void | ||
2373 | fc_timeout_fail_rport_io(void *data) | ||
2374 | { | ||
2375 | struct fc_rport *rport = (struct fc_rport *)data; | ||
2376 | struct Scsi_Host *shost = rport_to_shost(rport); | ||
2377 | struct fc_internal *i = to_fc_internal(shost->transportt); | ||
2378 | |||
2379 | if (rport->port_state != FC_PORTSTATE_BLOCKED) | ||
2380 | return; | ||
2381 | |||
2382 | i->f->terminate_rport_io(rport); | ||
2383 | } | ||
2384 | |||
2385 | /** | ||
2274 | * fc_scsi_scan_rport - called to perform a scsi scan on a remote port. | 2386 | * fc_scsi_scan_rport - called to perform a scsi scan on a remote port. |
2275 | * | 2387 | * |
2276 | * @data: remote port to be scanned. | 2388 | * @data: remote port to be scanned. |