diff options
author | Brian King <brking@linux.vnet.ibm.com> | 2009-10-19 16:07:54 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-12-04 13:00:21 -0500 |
commit | d31429e1517c007781dfc68aed9b39cb5d3350a1 (patch) | |
tree | 27ded5e5e1ee28a3d24cdd25d11f99cea6bfbc6f /drivers/scsi/ibmvscsi/ibmvfc.c | |
parent | 4a5c4a5ed2b8b7fac68368e7ab8cb415dd006418 (diff) |
[SCSI] ibmvfc: Add FC Passthru support
Adds support for FC passthru via BSG.
Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvfc.c')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 2c73b831544c..bc9beb8c587c 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <scsi/scsi_device.h> | 39 | #include <scsi/scsi_device.h> |
40 | #include <scsi/scsi_tcq.h> | 40 | #include <scsi/scsi_tcq.h> |
41 | #include <scsi/scsi_transport_fc.h> | 41 | #include <scsi/scsi_transport_fc.h> |
42 | #include <scsi/scsi_bsg_fc.h> | ||
42 | #include "ibmvfc.h" | 43 | #include "ibmvfc.h" |
43 | 44 | ||
44 | static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT; | 45 | static unsigned int init_timeout = IBMVFC_INIT_TIMEOUT; |
@@ -1675,6 +1676,276 @@ static void ibmvfc_sync_completion(struct ibmvfc_event *evt) | |||
1675 | } | 1676 | } |
1676 | 1677 | ||
1677 | /** | 1678 | /** |
1679 | * ibmvfc_bsg_timeout_done - Completion handler for cancelling BSG commands | ||
1680 | * @evt: struct ibmvfc_event | ||
1681 | * | ||
1682 | **/ | ||
1683 | static void ibmvfc_bsg_timeout_done(struct ibmvfc_event *evt) | ||
1684 | { | ||
1685 | struct ibmvfc_host *vhost = evt->vhost; | ||
1686 | |||
1687 | ibmvfc_free_event(evt); | ||
1688 | vhost->aborting_passthru = 0; | ||
1689 | dev_info(vhost->dev, "Passthru command cancelled\n"); | ||
1690 | } | ||
1691 | |||
1692 | /** | ||
1693 | * ibmvfc_bsg_timeout - Handle a BSG timeout | ||
1694 | * @job: struct fc_bsg_job that timed out | ||
1695 | * | ||
1696 | * Returns: | ||
1697 | * 0 on success / other on failure | ||
1698 | **/ | ||
1699 | static int ibmvfc_bsg_timeout(struct fc_bsg_job *job) | ||
1700 | { | ||
1701 | struct ibmvfc_host *vhost = shost_priv(job->shost); | ||
1702 | unsigned long port_id = (unsigned long)job->dd_data; | ||
1703 | struct ibmvfc_event *evt; | ||
1704 | struct ibmvfc_tmf *tmf; | ||
1705 | unsigned long flags; | ||
1706 | int rc; | ||
1707 | |||
1708 | ENTER; | ||
1709 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
1710 | if (vhost->aborting_passthru || vhost->state != IBMVFC_ACTIVE) { | ||
1711 | __ibmvfc_reset_host(vhost); | ||
1712 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1713 | return 0; | ||
1714 | } | ||
1715 | |||
1716 | vhost->aborting_passthru = 1; | ||
1717 | evt = ibmvfc_get_event(vhost); | ||
1718 | ibmvfc_init_event(evt, ibmvfc_bsg_timeout_done, IBMVFC_MAD_FORMAT); | ||
1719 | |||
1720 | tmf = &evt->iu.tmf; | ||
1721 | memset(tmf, 0, sizeof(*tmf)); | ||
1722 | tmf->common.version = 1; | ||
1723 | tmf->common.opcode = IBMVFC_TMF_MAD; | ||
1724 | tmf->common.length = sizeof(*tmf); | ||
1725 | tmf->scsi_id = port_id; | ||
1726 | tmf->cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY; | ||
1727 | tmf->my_cancel_key = IBMVFC_INTERNAL_CANCEL_KEY; | ||
1728 | rc = ibmvfc_send_event(evt, vhost, default_timeout); | ||
1729 | |||
1730 | if (rc != 0) { | ||
1731 | vhost->aborting_passthru = 0; | ||
1732 | dev_err(vhost->dev, "Failed to send cancel event. rc=%d\n", rc); | ||
1733 | rc = -EIO; | ||
1734 | } else | ||
1735 | dev_info(vhost->dev, "Cancelling passthru command to port id 0x%lx\n", | ||
1736 | port_id); | ||
1737 | |||
1738 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1739 | |||
1740 | LEAVE; | ||
1741 | return rc; | ||
1742 | } | ||
1743 | |||
1744 | /** | ||
1745 | * ibmvfc_bsg_plogi - PLOGI into a target to handle a BSG command | ||
1746 | * @vhost: struct ibmvfc_host to send command | ||
1747 | * @port_id: port ID to send command | ||
1748 | * | ||
1749 | * Returns: | ||
1750 | * 0 on success / other on failure | ||
1751 | **/ | ||
1752 | static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id) | ||
1753 | { | ||
1754 | struct ibmvfc_port_login *plogi; | ||
1755 | struct ibmvfc_target *tgt; | ||
1756 | struct ibmvfc_event *evt; | ||
1757 | union ibmvfc_iu rsp_iu; | ||
1758 | unsigned long flags; | ||
1759 | int rc = 0, issue_login = 1; | ||
1760 | |||
1761 | ENTER; | ||
1762 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
1763 | list_for_each_entry(tgt, &vhost->targets, queue) { | ||
1764 | if (tgt->scsi_id == port_id) { | ||
1765 | issue_login = 0; | ||
1766 | break; | ||
1767 | } | ||
1768 | } | ||
1769 | |||
1770 | if (!issue_login) | ||
1771 | goto unlock_out; | ||
1772 | if (unlikely((rc = ibmvfc_host_chkready(vhost)))) | ||
1773 | goto unlock_out; | ||
1774 | |||
1775 | evt = ibmvfc_get_event(vhost); | ||
1776 | ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); | ||
1777 | plogi = &evt->iu.plogi; | ||
1778 | memset(plogi, 0, sizeof(*plogi)); | ||
1779 | plogi->common.version = 1; | ||
1780 | plogi->common.opcode = IBMVFC_PORT_LOGIN; | ||
1781 | plogi->common.length = sizeof(*plogi); | ||
1782 | plogi->scsi_id = port_id; | ||
1783 | evt->sync_iu = &rsp_iu; | ||
1784 | init_completion(&evt->comp); | ||
1785 | |||
1786 | rc = ibmvfc_send_event(evt, vhost, default_timeout); | ||
1787 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1788 | |||
1789 | if (rc) | ||
1790 | return -EIO; | ||
1791 | |||
1792 | wait_for_completion(&evt->comp); | ||
1793 | |||
1794 | if (rsp_iu.plogi.common.status) | ||
1795 | rc = -EIO; | ||
1796 | |||
1797 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
1798 | ibmvfc_free_event(evt); | ||
1799 | unlock_out: | ||
1800 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1801 | LEAVE; | ||
1802 | return rc; | ||
1803 | } | ||
1804 | |||
1805 | /** | ||
1806 | * ibmvfc_bsg_request - Handle a BSG request | ||
1807 | * @job: struct fc_bsg_job to be executed | ||
1808 | * | ||
1809 | * Returns: | ||
1810 | * 0 on success / other on failure | ||
1811 | **/ | ||
1812 | static int ibmvfc_bsg_request(struct fc_bsg_job *job) | ||
1813 | { | ||
1814 | struct ibmvfc_host *vhost = shost_priv(job->shost); | ||
1815 | struct fc_rport *rport = job->rport; | ||
1816 | struct ibmvfc_passthru_mad *mad; | ||
1817 | struct ibmvfc_event *evt; | ||
1818 | union ibmvfc_iu rsp_iu; | ||
1819 | unsigned long flags, port_id = -1; | ||
1820 | unsigned int code = job->request->msgcode; | ||
1821 | int rc = 0, req_seg, rsp_seg, issue_login = 0; | ||
1822 | u32 fc_flags, rsp_len; | ||
1823 | |||
1824 | ENTER; | ||
1825 | job->reply->reply_payload_rcv_len = 0; | ||
1826 | if (rport) | ||
1827 | port_id = rport->port_id; | ||
1828 | |||
1829 | switch (code) { | ||
1830 | case FC_BSG_HST_ELS_NOLOGIN: | ||
1831 | port_id = (job->request->rqst_data.h_els.port_id[0] << 16) | | ||
1832 | (job->request->rqst_data.h_els.port_id[1] << 8) | | ||
1833 | job->request->rqst_data.h_els.port_id[2]; | ||
1834 | case FC_BSG_RPT_ELS: | ||
1835 | fc_flags = IBMVFC_FC_ELS; | ||
1836 | break; | ||
1837 | case FC_BSG_HST_CT: | ||
1838 | issue_login = 1; | ||
1839 | port_id = (job->request->rqst_data.h_ct.port_id[0] << 16) | | ||
1840 | (job->request->rqst_data.h_ct.port_id[1] << 8) | | ||
1841 | job->request->rqst_data.h_ct.port_id[2]; | ||
1842 | case FC_BSG_RPT_CT: | ||
1843 | fc_flags = IBMVFC_FC_CT_IU; | ||
1844 | break; | ||
1845 | default: | ||
1846 | return -ENOTSUPP; | ||
1847 | }; | ||
1848 | |||
1849 | if (port_id == -1) | ||
1850 | return -EINVAL; | ||
1851 | if (!mutex_trylock(&vhost->passthru_mutex)) | ||
1852 | return -EBUSY; | ||
1853 | |||
1854 | job->dd_data = (void *)port_id; | ||
1855 | req_seg = dma_map_sg(vhost->dev, job->request_payload.sg_list, | ||
1856 | job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
1857 | |||
1858 | if (!req_seg) { | ||
1859 | mutex_unlock(&vhost->passthru_mutex); | ||
1860 | return -ENOMEM; | ||
1861 | } | ||
1862 | |||
1863 | rsp_seg = dma_map_sg(vhost->dev, job->reply_payload.sg_list, | ||
1864 | job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
1865 | |||
1866 | if (!rsp_seg) { | ||
1867 | dma_unmap_sg(vhost->dev, job->request_payload.sg_list, | ||
1868 | job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
1869 | mutex_unlock(&vhost->passthru_mutex); | ||
1870 | return -ENOMEM; | ||
1871 | } | ||
1872 | |||
1873 | if (req_seg > 1 || rsp_seg > 1) { | ||
1874 | rc = -EINVAL; | ||
1875 | goto out; | ||
1876 | } | ||
1877 | |||
1878 | if (issue_login) | ||
1879 | rc = ibmvfc_bsg_plogi(vhost, port_id); | ||
1880 | |||
1881 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
1882 | |||
1883 | if (unlikely(rc || (rport && (rc = fc_remote_port_chkready(rport)))) || | ||
1884 | unlikely((rc = ibmvfc_host_chkready(vhost)))) { | ||
1885 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1886 | goto out; | ||
1887 | } | ||
1888 | |||
1889 | evt = ibmvfc_get_event(vhost); | ||
1890 | ibmvfc_init_event(evt, ibmvfc_sync_completion, IBMVFC_MAD_FORMAT); | ||
1891 | mad = &evt->iu.passthru; | ||
1892 | |||
1893 | memset(mad, 0, sizeof(*mad)); | ||
1894 | mad->common.version = 1; | ||
1895 | mad->common.opcode = IBMVFC_PASSTHRU; | ||
1896 | mad->common.length = sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu); | ||
1897 | |||
1898 | mad->cmd_ioba.va = (u64)evt->crq.ioba + | ||
1899 | offsetof(struct ibmvfc_passthru_mad, iu); | ||
1900 | mad->cmd_ioba.len = sizeof(mad->iu); | ||
1901 | |||
1902 | mad->iu.cmd_len = job->request_payload.payload_len; | ||
1903 | mad->iu.rsp_len = job->reply_payload.payload_len; | ||
1904 | mad->iu.flags = fc_flags; | ||
1905 | mad->iu.cancel_key = IBMVFC_PASSTHRU_CANCEL_KEY; | ||
1906 | |||
1907 | mad->iu.cmd.va = sg_dma_address(job->request_payload.sg_list); | ||
1908 | mad->iu.cmd.len = sg_dma_len(job->request_payload.sg_list); | ||
1909 | mad->iu.rsp.va = sg_dma_address(job->reply_payload.sg_list); | ||
1910 | mad->iu.rsp.len = sg_dma_len(job->reply_payload.sg_list); | ||
1911 | mad->iu.scsi_id = port_id; | ||
1912 | mad->iu.tag = (u64)evt; | ||
1913 | rsp_len = mad->iu.rsp.len; | ||
1914 | |||
1915 | evt->sync_iu = &rsp_iu; | ||
1916 | init_completion(&evt->comp); | ||
1917 | rc = ibmvfc_send_event(evt, vhost, 0); | ||
1918 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1919 | |||
1920 | if (rc) { | ||
1921 | rc = -EIO; | ||
1922 | goto out; | ||
1923 | } | ||
1924 | |||
1925 | wait_for_completion(&evt->comp); | ||
1926 | |||
1927 | if (rsp_iu.passthru.common.status) | ||
1928 | rc = -EIO; | ||
1929 | else | ||
1930 | job->reply->reply_payload_rcv_len = rsp_len; | ||
1931 | |||
1932 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
1933 | ibmvfc_free_event(evt); | ||
1934 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
1935 | job->reply->result = rc; | ||
1936 | job->job_done(job); | ||
1937 | rc = 0; | ||
1938 | out: | ||
1939 | dma_unmap_sg(vhost->dev, job->request_payload.sg_list, | ||
1940 | job->request_payload.sg_cnt, DMA_TO_DEVICE); | ||
1941 | dma_unmap_sg(vhost->dev, job->reply_payload.sg_list, | ||
1942 | job->reply_payload.sg_cnt, DMA_FROM_DEVICE); | ||
1943 | mutex_unlock(&vhost->passthru_mutex); | ||
1944 | LEAVE; | ||
1945 | return rc; | ||
1946 | } | ||
1947 | |||
1948 | /** | ||
1678 | * ibmvfc_reset_device - Reset the device with the specified reset type | 1949 | * ibmvfc_reset_device - Reset the device with the specified reset type |
1679 | * @sdev: scsi device to reset | 1950 | * @sdev: scsi device to reset |
1680 | * @type: reset type | 1951 | * @type: reset type |
@@ -3918,6 +4189,8 @@ static void ibmvfc_tgt_add_rport(struct ibmvfc_target *tgt) | |||
3918 | rport->supported_classes |= FC_COS_CLASS2; | 4189 | rport->supported_classes |= FC_COS_CLASS2; |
3919 | if (tgt->service_parms.class3_parms[0] & 0x80000000) | 4190 | if (tgt->service_parms.class3_parms[0] & 0x80000000) |
3920 | rport->supported_classes |= FC_COS_CLASS3; | 4191 | rport->supported_classes |= FC_COS_CLASS3; |
4192 | if (rport->rqst_q) | ||
4193 | blk_queue_max_hw_segments(rport->rqst_q, 1); | ||
3921 | } else | 4194 | } else |
3922 | tgt_dbg(tgt, "rport add failed\n"); | 4195 | tgt_dbg(tgt, "rport add failed\n"); |
3923 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | 4196 | spin_unlock_irqrestore(vhost->host->host_lock, flags); |
@@ -4357,6 +4630,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |||
4357 | init_waitqueue_head(&vhost->work_wait_q); | 4630 | init_waitqueue_head(&vhost->work_wait_q); |
4358 | init_waitqueue_head(&vhost->init_wait_q); | 4631 | init_waitqueue_head(&vhost->init_wait_q); |
4359 | INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread); | 4632 | INIT_WORK(&vhost->rport_add_work_q, ibmvfc_rport_add_thread); |
4633 | mutex_init(&vhost->passthru_mutex); | ||
4360 | 4634 | ||
4361 | if ((rc = ibmvfc_alloc_mem(vhost))) | 4635 | if ((rc = ibmvfc_alloc_mem(vhost))) |
4362 | goto free_scsi_host; | 4636 | goto free_scsi_host; |
@@ -4389,6 +4663,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |||
4389 | goto remove_shost; | 4663 | goto remove_shost; |
4390 | } | 4664 | } |
4391 | 4665 | ||
4666 | if (shost_to_fc_host(shost)->rqst_q) | ||
4667 | blk_queue_max_hw_segments(shost_to_fc_host(shost)->rqst_q, 1); | ||
4392 | dev_set_drvdata(dev, vhost); | 4668 | dev_set_drvdata(dev, vhost); |
4393 | spin_lock(&ibmvfc_driver_lock); | 4669 | spin_lock(&ibmvfc_driver_lock); |
4394 | list_add_tail(&vhost->queue, &ibmvfc_head); | 4670 | list_add_tail(&vhost->queue, &ibmvfc_head); |
@@ -4517,6 +4793,9 @@ static struct fc_function_template ibmvfc_transport_functions = { | |||
4517 | 4793 | ||
4518 | .get_starget_port_id = ibmvfc_get_starget_port_id, | 4794 | .get_starget_port_id = ibmvfc_get_starget_port_id, |
4519 | .show_starget_port_id = 1, | 4795 | .show_starget_port_id = 1, |
4796 | |||
4797 | .bsg_request = ibmvfc_bsg_request, | ||
4798 | .bsg_timeout = ibmvfc_bsg_timeout, | ||
4520 | }; | 4799 | }; |
4521 | 4800 | ||
4522 | /** | 4801 | /** |