aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/fcoe/fcoe.c4
-rw-r--r--drivers/scsi/libfc/fc_lport.c267
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
139struct fc_function_template fcoe_vport_transport_function = { 141struct 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
172static struct scsi_host_template fcoe_shost_template = { 176static 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 */
140struct 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
130static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp) 149static 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}
1514EXPORT_SYMBOL(fc_lport_init); 1533EXPORT_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 */
1541static 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 */
1607static 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 */
1668static 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 */
1723int 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}
1781EXPORT_SYMBOL(fc_lport_bsg_request);