diff options
author | Steve Ma <steve.ma@intel.com> | 2009-11-03 14:47:34 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-12-04 13:01:06 -0500 |
commit | a51ab39606042e76a483547620699530caa12c40 (patch) | |
tree | d67261f7a13b7c9ad1d9d69d4aeefe899eef0066 /drivers | |
parent | 5868287460b0fc243e828a0b856cd53d8bf45739 (diff) |
[SCSI] libfc, fcoe: Add FC passthrough support
This is the Open-FCoE implementation of the FC
passthrough support via bsg interface.
Passthrough support is added to both N_Ports and
VN_Ports.
Signed-off-by: Steve Ma <steve.ma@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 4 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_lport.c | 267 |
2 files changed, 271 insertions, 0 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index f1c126b798af..8f078d306a0a 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c | |||
@@ -134,6 +134,8 @@ struct fc_function_template fcoe_transport_function = { | |||
134 | .vport_delete = fcoe_vport_destroy, | 134 | .vport_delete = fcoe_vport_destroy, |
135 | .vport_disable = fcoe_vport_disable, | 135 | .vport_disable = fcoe_vport_disable, |
136 | .set_vport_symbolic_name = fcoe_set_vport_symbolic_name, | 136 | .set_vport_symbolic_name = fcoe_set_vport_symbolic_name, |
137 | |||
138 | .bsg_request = fc_lport_bsg_request, | ||
137 | }; | 139 | }; |
138 | 140 | ||
139 | struct fc_function_template fcoe_vport_transport_function = { | 141 | struct fc_function_template fcoe_vport_transport_function = { |
@@ -167,6 +169,8 @@ struct fc_function_template fcoe_vport_transport_function = { | |||
167 | .issue_fc_host_lip = fcoe_reset, | 169 | .issue_fc_host_lip = fcoe_reset, |
168 | 170 | ||
169 | .terminate_rport_io = fc_rport_terminate_io, | 171 | .terminate_rport_io = fc_rport_terminate_io, |
172 | |||
173 | .bsg_request = fc_lport_bsg_request, | ||
170 | }; | 174 | }; |
171 | 175 | ||
172 | static struct scsi_host_template fcoe_shost_template = { | 176 | static struct scsi_host_template fcoe_shost_template = { |
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index dfea6c572dfb..2162e6b0f43e 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c | |||
@@ -94,6 +94,7 @@ | |||
94 | 94 | ||
95 | #include <scsi/libfc.h> | 95 | #include <scsi/libfc.h> |
96 | #include <scsi/fc_encode.h> | 96 | #include <scsi/fc_encode.h> |
97 | #include <linux/scatterlist.h> | ||
97 | 98 | ||
98 | #include "fc_libfc.h" | 99 | #include "fc_libfc.h" |
99 | 100 | ||
@@ -127,6 +128,24 @@ static const char *fc_lport_state_names[] = { | |||
127 | [LPORT_ST_RESET] = "reset", | 128 | [LPORT_ST_RESET] = "reset", |
128 | }; | 129 | }; |
129 | 130 | ||
131 | /** | ||
132 | * struct fc_bsg_info - FC Passthrough managemet structure | ||
133 | * @job: The passthrough job | ||
134 | * @lport: The local port to pass through a command | ||
135 | * @rsp_code: The expected response code | ||
136 | * @sg: job->reply_payload.sg_list | ||
137 | * @nents: job->reply_payload.sg_cnt | ||
138 | * @offset: The offset into the response data | ||
139 | */ | ||
140 | struct fc_bsg_info { | ||
141 | struct fc_bsg_job *job; | ||
142 | struct fc_lport *lport; | ||
143 | u16 rsp_code; | ||
144 | struct scatterlist *sg; | ||
145 | u32 nents; | ||
146 | size_t offset; | ||
147 | }; | ||
148 | |||
130 | static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp) | 149 | static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp) |
131 | { | 150 | { |
132 | fc_frame_free(fp); | 151 | fc_frame_free(fp); |
@@ -1512,3 +1531,251 @@ int fc_lport_init(struct fc_lport *lport) | |||
1512 | return 0; | 1531 | return 0; |
1513 | } | 1532 | } |
1514 | EXPORT_SYMBOL(fc_lport_init); | 1533 | EXPORT_SYMBOL(fc_lport_init); |
1534 | |||
1535 | /** | ||
1536 | * fc_lport_bsg_resp() - The common response handler for fc pass-thru requests | ||
1537 | * @sp: current sequence in the fc pass-thru request exchange | ||
1538 | * @fp: received response frame | ||
1539 | * @info_arg: pointer to struct fc_bsg_info | ||
1540 | */ | ||
1541 | static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp, | ||
1542 | void *info_arg) | ||
1543 | { | ||
1544 | struct fc_bsg_info *info = info_arg; | ||
1545 | struct fc_bsg_job *job = info->job; | ||
1546 | struct fc_lport *lport = info->lport; | ||
1547 | struct fc_frame_header *fh; | ||
1548 | size_t len; | ||
1549 | void *buf; | ||
1550 | |||
1551 | if (IS_ERR(fp)) { | ||
1552 | job->reply->result = (PTR_ERR(fp) == -FC_EX_CLOSED) ? | ||
1553 | -ECONNABORTED : -ETIMEDOUT; | ||
1554 | job->reply_len = sizeof(uint32_t); | ||
1555 | job->state_flags |= FC_RQST_STATE_DONE; | ||
1556 | job->job_done(job); | ||
1557 | kfree(info); | ||
1558 | return; | ||
1559 | } | ||
1560 | |||
1561 | mutex_lock(&lport->lp_mutex); | ||
1562 | fh = fc_frame_header_get(fp); | ||
1563 | len = fr_len(fp) - sizeof(*fh); | ||
1564 | buf = fc_frame_payload_get(fp, 0); | ||
1565 | |||
1566 | if (fr_sof(fp) == FC_SOF_I3 && !ntohs(fh->fh_seq_cnt)) { | ||
1567 | /* Get the response code from the first frame payload */ | ||
1568 | unsigned short cmd = (info->rsp_code == FC_FS_ACC) ? | ||
1569 | ntohs(((struct fc_ct_hdr *)buf)->ct_cmd) : | ||
1570 | (unsigned short)fc_frame_payload_op(fp); | ||
1571 | |||
1572 | /* Save the reply status of the job */ | ||
1573 | job->reply->reply_data.ctels_reply.status = | ||
1574 | (cmd == info->rsp_code) ? | ||
1575 | FC_CTELS_STATUS_OK : FC_CTELS_STATUS_REJECT; | ||
1576 | } | ||
1577 | |||
1578 | job->reply->reply_payload_rcv_len += | ||
1579 | fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents, | ||
1580 | &info->offset, KM_BIO_SRC_IRQ, NULL); | ||
1581 | |||
1582 | if (fr_eof(fp) == FC_EOF_T && | ||
1583 | (ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == | ||
1584 | (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) { | ||
1585 | if (job->reply->reply_payload_rcv_len > | ||
1586 | job->reply_payload.payload_len) | ||
1587 | job->reply->reply_payload_rcv_len = | ||
1588 | job->reply_payload.payload_len; | ||
1589 | job->reply->result = 0; | ||
1590 | job->state_flags |= FC_RQST_STATE_DONE; | ||
1591 | job->job_done(job); | ||
1592 | kfree(info); | ||
1593 | } | ||
1594 | fc_frame_free(fp); | ||
1595 | mutex_unlock(&lport->lp_mutex); | ||
1596 | } | ||
1597 | |||
1598 | /** | ||
1599 | * fc_lport_els_request() - Send ELS pass-thru request | ||
1600 | * @job: The bsg fc pass-thru job structure | ||
1601 | * @lport: The local port sending the request | ||
1602 | * @did: The destination port id. | ||
1603 | * | ||
1604 | * Locking Note: The lport lock is expected to be held before calling | ||
1605 | * this routine. | ||
1606 | */ | ||
1607 | static int fc_lport_els_request(struct fc_bsg_job *job, | ||
1608 | struct fc_lport *lport, | ||
1609 | u32 did, u32 tov) | ||
1610 | { | ||
1611 | struct fc_bsg_info *info; | ||
1612 | struct fc_frame *fp; | ||
1613 | struct fc_frame_header *fh; | ||
1614 | char *pp; | ||
1615 | int len; | ||
1616 | |||
1617 | fp = fc_frame_alloc(lport, sizeof(struct fc_frame_header) + | ||
1618 | job->request_payload.payload_len); | ||
1619 | if (!fp) | ||
1620 | return -ENOMEM; | ||
1621 | |||
1622 | len = job->request_payload.payload_len; | ||
1623 | pp = fc_frame_payload_get(fp, len); | ||
1624 | |||
1625 | sg_copy_to_buffer(job->request_payload.sg_list, | ||
1626 | job->request_payload.sg_cnt, | ||
1627 | pp, len); | ||
1628 | |||
1629 | fh = fc_frame_header_get(fp); | ||
1630 | fh->fh_r_ctl = FC_RCTL_ELS_REQ; | ||
1631 | hton24(fh->fh_d_id, did); | ||
1632 | hton24(fh->fh_s_id, fc_host_port_id(lport->host)); | ||
1633 | fh->fh_type = FC_TYPE_ELS; | ||
1634 | hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ | | ||
1635 | FC_FC_END_SEQ | FC_FC_SEQ_INIT); | ||
1636 | fh->fh_cs_ctl = 0; | ||
1637 | fh->fh_df_ctl = 0; | ||
1638 | fh->fh_parm_offset = 0; | ||
1639 | |||
1640 | info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL); | ||
1641 | if (!info) { | ||
1642 | fc_frame_free(fp); | ||
1643 | return -ENOMEM; | ||
1644 | } | ||
1645 | |||
1646 | info->job = job; | ||
1647 | info->lport = lport; | ||
1648 | info->rsp_code = ELS_LS_ACC; | ||
1649 | info->nents = job->reply_payload.sg_cnt; | ||
1650 | info->sg = job->reply_payload.sg_list; | ||
1651 | |||
1652 | if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, | ||
1653 | NULL, info, tov)) | ||
1654 | return -ECOMM; | ||
1655 | return 0; | ||
1656 | } | ||
1657 | |||
1658 | /** | ||
1659 | * fc_lport_ct_request() - Send CT pass-thru request | ||
1660 | * @job: The bsg fc pass-thru job structure | ||
1661 | * @lport: The local port sending the request | ||
1662 | * @did: The destination FC-ID | ||
1663 | * @tov: The time to wait for a response | ||
1664 | * | ||
1665 | * Locking Note: The lport lock is expected to be held before calling | ||
1666 | * this routine. | ||
1667 | */ | ||
1668 | static int fc_lport_ct_request(struct fc_bsg_job *job, | ||
1669 | struct fc_lport *lport, u32 did, u32 tov) | ||
1670 | { | ||
1671 | struct fc_bsg_info *info; | ||
1672 | struct fc_frame *fp; | ||
1673 | struct fc_frame_header *fh; | ||
1674 | struct fc_ct_req *ct; | ||
1675 | size_t len; | ||
1676 | |||
1677 | fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + | ||
1678 | job->request_payload.payload_len); | ||
1679 | if (!fp) | ||
1680 | return -ENOMEM; | ||
1681 | |||
1682 | len = job->request_payload.payload_len; | ||
1683 | ct = fc_frame_payload_get(fp, len); | ||
1684 | |||
1685 | sg_copy_to_buffer(job->request_payload.sg_list, | ||
1686 | job->request_payload.sg_cnt, | ||
1687 | ct, len); | ||
1688 | |||
1689 | fh = fc_frame_header_get(fp); | ||
1690 | fh->fh_r_ctl = FC_RCTL_DD_UNSOL_CTL; | ||
1691 | hton24(fh->fh_d_id, did); | ||
1692 | hton24(fh->fh_s_id, fc_host_port_id(lport->host)); | ||
1693 | fh->fh_type = FC_TYPE_CT; | ||
1694 | hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ | | ||
1695 | FC_FC_END_SEQ | FC_FC_SEQ_INIT); | ||
1696 | fh->fh_cs_ctl = 0; | ||
1697 | fh->fh_df_ctl = 0; | ||
1698 | fh->fh_parm_offset = 0; | ||
1699 | |||
1700 | info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL); | ||
1701 | if (!info) { | ||
1702 | fc_frame_free(fp); | ||
1703 | return -ENOMEM; | ||
1704 | } | ||
1705 | |||
1706 | info->job = job; | ||
1707 | info->lport = lport; | ||
1708 | info->rsp_code = FC_FS_ACC; | ||
1709 | info->nents = job->reply_payload.sg_cnt; | ||
1710 | info->sg = job->reply_payload.sg_list; | ||
1711 | |||
1712 | if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, | ||
1713 | NULL, info, tov)) | ||
1714 | return -ECOMM; | ||
1715 | return 0; | ||
1716 | } | ||
1717 | |||
1718 | /** | ||
1719 | * fc_lport_bsg_request() - The common entry point for sending | ||
1720 | * fc pass-thru requests | ||
1721 | * @job: The fc pass-thru job structure | ||
1722 | */ | ||
1723 | int fc_lport_bsg_request(struct fc_bsg_job *job) | ||
1724 | { | ||
1725 | struct request *rsp = job->req->next_rq; | ||
1726 | struct Scsi_Host *shost = job->shost; | ||
1727 | struct fc_lport *lport = shost_priv(shost); | ||
1728 | struct fc_rport *rport; | ||
1729 | struct fc_rport_priv *rdata; | ||
1730 | int rc = -EINVAL; | ||
1731 | u32 did; | ||
1732 | |||
1733 | job->reply->reply_payload_rcv_len = 0; | ||
1734 | rsp->resid_len = job->reply_payload.payload_len; | ||
1735 | |||
1736 | mutex_lock(&lport->lp_mutex); | ||
1737 | |||
1738 | switch (job->request->msgcode) { | ||
1739 | case FC_BSG_RPT_ELS: | ||
1740 | rport = job->rport; | ||
1741 | if (!rport) | ||
1742 | break; | ||
1743 | |||
1744 | rdata = rport->dd_data; | ||
1745 | rc = fc_lport_els_request(job, lport, rport->port_id, | ||
1746 | rdata->e_d_tov); | ||
1747 | break; | ||
1748 | |||
1749 | case FC_BSG_RPT_CT: | ||
1750 | rport = job->rport; | ||
1751 | if (!rport) | ||
1752 | break; | ||
1753 | |||
1754 | rdata = rport->dd_data; | ||
1755 | rc = fc_lport_ct_request(job, lport, rport->port_id, | ||
1756 | rdata->e_d_tov); | ||
1757 | break; | ||
1758 | |||
1759 | case FC_BSG_HST_CT: | ||
1760 | did = ntoh24(job->request->rqst_data.h_ct.port_id); | ||
1761 | if (did == FC_FID_DIR_SERV) | ||
1762 | rdata = lport->dns_rp; | ||
1763 | else | ||
1764 | rdata = lport->tt.rport_lookup(lport, did); | ||
1765 | |||
1766 | if (!rdata) | ||
1767 | break; | ||
1768 | |||
1769 | rc = fc_lport_ct_request(job, lport, did, rdata->e_d_tov); | ||
1770 | break; | ||
1771 | |||
1772 | case FC_BSG_HST_ELS_NOLOGIN: | ||
1773 | did = ntoh24(job->request->rqst_data.h_els.port_id); | ||
1774 | rc = fc_lport_els_request(job, lport, did, lport->e_d_tov); | ||
1775 | break; | ||
1776 | } | ||
1777 | |||
1778 | mutex_unlock(&lport->lp_mutex); | ||
1779 | return rc; | ||
1780 | } | ||
1781 | EXPORT_SYMBOL(fc_lport_bsg_request); | ||