diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/libfc/fc_libfc.c | 60 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_libfc.h | 11 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_lport.c | 65 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_rport.c | 133 |
4 files changed, 233 insertions, 36 deletions
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c index 6a48c28e4420..ae3abef6523e 100644 --- a/drivers/scsi/libfc/fc_libfc.c +++ b/drivers/scsi/libfc/fc_libfc.c | |||
@@ -35,6 +35,23 @@ unsigned int fc_debug_logging; | |||
35 | module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR); | 35 | module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR); |
36 | MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); | 36 | MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); |
37 | 37 | ||
38 | DEFINE_MUTEX(fc_prov_mutex); | ||
39 | |||
40 | /* | ||
41 | * Providers which primarily send requests and PRLIs. | ||
42 | */ | ||
43 | struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = { | ||
44 | [0] = &fc_rport_t0_prov, | ||
45 | [FC_TYPE_FCP] = &fc_rport_fcp_init, | ||
46 | }; | ||
47 | |||
48 | /* | ||
49 | * Providers which receive requests. | ||
50 | */ | ||
51 | struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = { | ||
52 | [FC_TYPE_ELS] = &fc_lport_els_prov, | ||
53 | }; | ||
54 | |||
38 | /** | 55 | /** |
39 | * libfc_init() - Initialize libfc.ko | 56 | * libfc_init() - Initialize libfc.ko |
40 | */ | 57 | */ |
@@ -210,3 +227,46 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp, | |||
210 | fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset); | 227 | fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset); |
211 | } | 228 | } |
212 | EXPORT_SYMBOL(fc_fill_reply_hdr); | 229 | EXPORT_SYMBOL(fc_fill_reply_hdr); |
230 | |||
231 | /** | ||
232 | * fc_fc4_register_provider() - register FC-4 upper-level provider. | ||
233 | * @type: FC-4 type, such as FC_TYPE_FCP | ||
234 | * @prov: structure describing provider including ops vector. | ||
235 | * | ||
236 | * Returns 0 on success, negative error otherwise. | ||
237 | */ | ||
238 | int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov) | ||
239 | { | ||
240 | struct fc4_prov **prov_entry; | ||
241 | int ret = 0; | ||
242 | |||
243 | if (type >= FC_FC4_PROV_SIZE) | ||
244 | return -EINVAL; | ||
245 | mutex_lock(&fc_prov_mutex); | ||
246 | prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type; | ||
247 | if (*prov_entry) | ||
248 | ret = -EBUSY; | ||
249 | else | ||
250 | *prov_entry = prov; | ||
251 | mutex_unlock(&fc_prov_mutex); | ||
252 | return ret; | ||
253 | } | ||
254 | EXPORT_SYMBOL(fc_fc4_register_provider); | ||
255 | |||
256 | /** | ||
257 | * fc_fc4_deregister_provider() - deregister FC-4 upper-level provider. | ||
258 | * @type: FC-4 type, such as FC_TYPE_FCP | ||
259 | * @prov: structure describing provider including ops vector. | ||
260 | */ | ||
261 | void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov) | ||
262 | { | ||
263 | BUG_ON(type >= FC_FC4_PROV_SIZE); | ||
264 | mutex_lock(&fc_prov_mutex); | ||
265 | if (prov->recv) | ||
266 | rcu_assign_pointer(fc_passive_prov[type], NULL); | ||
267 | else | ||
268 | rcu_assign_pointer(fc_active_prov[type], NULL); | ||
269 | mutex_unlock(&fc_prov_mutex); | ||
270 | synchronize_rcu(); | ||
271 | } | ||
272 | EXPORT_SYMBOL(fc_fc4_deregister_provider); | ||
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h index eea0c3541b71..205de285e456 100644 --- a/drivers/scsi/libfc/fc_libfc.h +++ b/drivers/scsi/libfc/fc_libfc.h | |||
@@ -94,6 +94,17 @@ extern unsigned int fc_debug_logging; | |||
94 | (lport)->host->host_no, ##args)) | 94 | (lport)->host->host_no, ##args)) |
95 | 95 | ||
96 | /* | 96 | /* |
97 | * FC-4 Providers. | ||
98 | */ | ||
99 | extern struct fc4_prov *fc_active_prov[]; /* providers without recv */ | ||
100 | extern struct fc4_prov *fc_passive_prov[]; /* providers with recv */ | ||
101 | extern struct mutex fc_prov_mutex; /* lock over table changes */ | ||
102 | |||
103 | extern struct fc4_prov fc_rport_t0_prov; /* type 0 provider */ | ||
104 | extern struct fc4_prov fc_lport_els_prov; /* ELS provider */ | ||
105 | extern struct fc4_prov fc_rport_fcp_init; /* FCP initiator provider */ | ||
106 | |||
107 | /* | ||
97 | * Set up direct-data placement for this I/O request | 108 | * Set up direct-data placement for this I/O request |
98 | */ | 109 | */ |
99 | void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid); | 110 | void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid); |
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index c5a10f94f845..e2cd087e71b2 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c | |||
@@ -849,7 +849,7 @@ out: | |||
849 | } | 849 | } |
850 | 850 | ||
851 | /** | 851 | /** |
852 | * fc_lport_recv_req() - The generic lport request handler | 852 | * fc_lport_recv_els_req() - The generic lport ELS request handler |
853 | * @lport: The local port that received the request | 853 | * @lport: The local port that received the request |
854 | * @fp: The request frame | 854 | * @fp: The request frame |
855 | * | 855 | * |
@@ -859,9 +859,9 @@ out: | |||
859 | * Locking Note: This function should not be called with the lport | 859 | * Locking Note: This function should not be called with the lport |
860 | * lock held becuase it will grab the lock. | 860 | * lock held becuase it will grab the lock. |
861 | */ | 861 | */ |
862 | static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp) | 862 | static void fc_lport_recv_els_req(struct fc_lport *lport, |
863 | struct fc_frame *fp) | ||
863 | { | 864 | { |
864 | struct fc_frame_header *fh = fc_frame_header_get(fp); | ||
865 | void (*recv)(struct fc_lport *, struct fc_frame *); | 865 | void (*recv)(struct fc_lport *, struct fc_frame *); |
866 | 866 | ||
867 | mutex_lock(&lport->lp_mutex); | 867 | mutex_lock(&lport->lp_mutex); |
@@ -873,8 +873,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp) | |||
873 | */ | 873 | */ |
874 | if (!lport->link_up) | 874 | if (!lport->link_up) |
875 | fc_frame_free(fp); | 875 | fc_frame_free(fp); |
876 | else if (fh->fh_type == FC_TYPE_ELS && | 876 | else { |
877 | fh->fh_r_ctl == FC_RCTL_ELS_REQ) { | ||
878 | /* | 877 | /* |
879 | * Check opcode. | 878 | * Check opcode. |
880 | */ | 879 | */ |
@@ -903,14 +902,62 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp) | |||
903 | } | 902 | } |
904 | 903 | ||
905 | recv(lport, fp); | 904 | recv(lport, fp); |
906 | } else { | ||
907 | FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n", | ||
908 | fr_eof(fp)); | ||
909 | fc_frame_free(fp); | ||
910 | } | 905 | } |
911 | mutex_unlock(&lport->lp_mutex); | 906 | mutex_unlock(&lport->lp_mutex); |
912 | } | 907 | } |
913 | 908 | ||
909 | static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len, | ||
910 | const struct fc_els_spp *spp_in, | ||
911 | struct fc_els_spp *spp_out) | ||
912 | { | ||
913 | return FC_SPP_RESP_INVL; | ||
914 | } | ||
915 | |||
916 | struct fc4_prov fc_lport_els_prov = { | ||
917 | .prli = fc_lport_els_prli, | ||
918 | .recv = fc_lport_recv_els_req, | ||
919 | }; | ||
920 | |||
921 | /** | ||
922 | * fc_lport_recv_req() - The generic lport request handler | ||
923 | * @lport: The lport that received the request | ||
924 | * @fp: The frame the request is in | ||
925 | * | ||
926 | * Locking Note: This function should not be called with the lport | ||
927 | * lock held becuase it may grab the lock. | ||
928 | */ | ||
929 | static void fc_lport_recv_req(struct fc_lport *lport, | ||
930 | struct fc_frame *fp) | ||
931 | { | ||
932 | struct fc_frame_header *fh = fc_frame_header_get(fp); | ||
933 | struct fc_seq *sp = fr_seq(fp); | ||
934 | struct fc4_prov *prov; | ||
935 | |||
936 | /* | ||
937 | * Use RCU read lock and module_lock to be sure module doesn't | ||
938 | * deregister and get unloaded while we're calling it. | ||
939 | * try_module_get() is inlined and accepts a NULL parameter. | ||
940 | * Only ELSes and FCP target ops should come through here. | ||
941 | * The locking is unfortunate, and a better scheme is being sought. | ||
942 | */ | ||
943 | |||
944 | rcu_read_lock(); | ||
945 | if (fh->fh_type >= FC_FC4_PROV_SIZE) | ||
946 | goto drop; | ||
947 | prov = rcu_dereference(fc_passive_prov[fh->fh_type]); | ||
948 | if (!prov || !try_module_get(prov->module)) | ||
949 | goto drop; | ||
950 | rcu_read_unlock(); | ||
951 | prov->recv(lport, fp); | ||
952 | module_put(prov->module); | ||
953 | return; | ||
954 | drop: | ||
955 | rcu_read_unlock(); | ||
956 | FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type); | ||
957 | fc_frame_free(fp); | ||
958 | lport->tt.exch_done(sp); | ||
959 | } | ||
960 | |||
914 | /** | 961 | /** |
915 | * fc_lport_reset() - Reset a local port | 962 | * fc_lport_reset() - Reset a local port |
916 | * @lport: The local port which should be reset | 963 | * @lport: The local port which should be reset |
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 309e3e713ea1..a92954c1f42f 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c | |||
@@ -257,6 +257,8 @@ static void fc_rport_work(struct work_struct *work) | |||
257 | struct fc_rport_operations *rport_ops; | 257 | struct fc_rport_operations *rport_ops; |
258 | struct fc_rport_identifiers ids; | 258 | struct fc_rport_identifiers ids; |
259 | struct fc_rport *rport; | 259 | struct fc_rport *rport; |
260 | struct fc4_prov *prov; | ||
261 | u8 type; | ||
260 | 262 | ||
261 | mutex_lock(&rdata->rp_mutex); | 263 | mutex_lock(&rdata->rp_mutex); |
262 | event = rdata->event; | 264 | event = rdata->event; |
@@ -306,6 +308,15 @@ static void fc_rport_work(struct work_struct *work) | |||
306 | case RPORT_EV_FAILED: | 308 | case RPORT_EV_FAILED: |
307 | case RPORT_EV_LOGO: | 309 | case RPORT_EV_LOGO: |
308 | case RPORT_EV_STOP: | 310 | case RPORT_EV_STOP: |
311 | if (rdata->prli_count) { | ||
312 | mutex_lock(&fc_prov_mutex); | ||
313 | for (type = 1; type < FC_FC4_PROV_SIZE; type++) { | ||
314 | prov = fc_passive_prov[type]; | ||
315 | if (prov && prov->prlo) | ||
316 | prov->prlo(rdata); | ||
317 | } | ||
318 | mutex_unlock(&fc_prov_mutex); | ||
319 | } | ||
309 | port_id = rdata->ids.port_id; | 320 | port_id = rdata->ids.port_id; |
310 | mutex_unlock(&rdata->rp_mutex); | 321 | mutex_unlock(&rdata->rp_mutex); |
311 | 322 | ||
@@ -1643,9 +1654,9 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, | |||
1643 | unsigned int len; | 1654 | unsigned int len; |
1644 | unsigned int plen; | 1655 | unsigned int plen; |
1645 | enum fc_els_spp_resp resp; | 1656 | enum fc_els_spp_resp resp; |
1657 | enum fc_els_spp_resp passive; | ||
1646 | struct fc_seq_els_data rjt_data; | 1658 | struct fc_seq_els_data rjt_data; |
1647 | u32 fcp_parm; | 1659 | struct fc4_prov *prov; |
1648 | u32 roles = FC_RPORT_ROLE_UNKNOWN; | ||
1649 | 1660 | ||
1650 | FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n", | 1661 | FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n", |
1651 | fc_rport_state(rdata)); | 1662 | fc_rport_state(rdata)); |
@@ -1679,46 +1690,41 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, | |||
1679 | pp->prli.prli_len = htons(len); | 1690 | pp->prli.prli_len = htons(len); |
1680 | len -= sizeof(struct fc_els_prli); | 1691 | len -= sizeof(struct fc_els_prli); |
1681 | 1692 | ||
1682 | /* reinitialize remote port roles */ | ||
1683 | rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; | ||
1684 | |||
1685 | /* | 1693 | /* |
1686 | * Go through all the service parameter pages and build | 1694 | * Go through all the service parameter pages and build |
1687 | * response. If plen indicates longer SPP than standard, | 1695 | * response. If plen indicates longer SPP than standard, |
1688 | * use that. The entire response has been pre-cleared above. | 1696 | * use that. The entire response has been pre-cleared above. |
1689 | */ | 1697 | */ |
1690 | spp = &pp->spp; | 1698 | spp = &pp->spp; |
1699 | mutex_lock(&fc_prov_mutex); | ||
1691 | while (len >= plen) { | 1700 | while (len >= plen) { |
1692 | spp->spp_type = rspp->spp_type; | 1701 | spp->spp_type = rspp->spp_type; |
1693 | spp->spp_type_ext = rspp->spp_type_ext; | 1702 | spp->spp_type_ext = rspp->spp_type_ext; |
1694 | spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR; | 1703 | resp = 0; |
1695 | resp = FC_SPP_RESP_ACK; | 1704 | |
1696 | 1705 | if (rspp->spp_type < FC_FC4_PROV_SIZE) { | |
1697 | switch (rspp->spp_type) { | 1706 | prov = fc_active_prov[rspp->spp_type]; |
1698 | case 0: /* common to all FC-4 types */ | 1707 | if (prov) |
1699 | break; | 1708 | resp = prov->prli(rdata, plen, rspp, spp); |
1700 | case FC_TYPE_FCP: | 1709 | prov = fc_passive_prov[rspp->spp_type]; |
1701 | fcp_parm = ntohl(rspp->spp_params); | 1710 | if (prov) { |
1702 | if (fcp_parm & FCP_SPPF_RETRY) | 1711 | passive = prov->prli(rdata, plen, rspp, spp); |
1703 | rdata->flags |= FC_RP_FLAGS_RETRY; | 1712 | if (!resp || passive == FC_SPP_RESP_ACK) |
1704 | rdata->supported_classes = FC_COS_CLASS3; | 1713 | resp = passive; |
1705 | if (fcp_parm & FCP_SPPF_INIT_FCN) | 1714 | } |
1706 | roles |= FC_RPORT_ROLE_FCP_INITIATOR; | 1715 | } |
1707 | if (fcp_parm & FCP_SPPF_TARG_FCN) | 1716 | if (!resp) { |
1708 | roles |= FC_RPORT_ROLE_FCP_TARGET; | 1717 | if (spp->spp_flags & FC_SPP_EST_IMG_PAIR) |
1709 | rdata->ids.roles = roles; | 1718 | resp |= FC_SPP_RESP_CONF; |
1710 | 1719 | else | |
1711 | spp->spp_params = htonl(lport->service_params); | 1720 | resp |= FC_SPP_RESP_INVL; |
1712 | break; | ||
1713 | default: | ||
1714 | resp = FC_SPP_RESP_INVL; | ||
1715 | break; | ||
1716 | } | 1721 | } |
1717 | spp->spp_flags |= resp; | 1722 | spp->spp_flags |= resp; |
1718 | len -= plen; | 1723 | len -= plen; |
1719 | rspp = (struct fc_els_spp *)((char *)rspp + plen); | 1724 | rspp = (struct fc_els_spp *)((char *)rspp + plen); |
1720 | spp = (struct fc_els_spp *)((char *)spp + plen); | 1725 | spp = (struct fc_els_spp *)((char *)spp + plen); |
1721 | } | 1726 | } |
1727 | mutex_unlock(&fc_prov_mutex); | ||
1722 | 1728 | ||
1723 | /* | 1729 | /* |
1724 | * Send LS_ACC. If this fails, the originator should retry. | 1730 | * Send LS_ACC. If this fails, the originator should retry. |
@@ -1888,6 +1894,79 @@ int fc_rport_init(struct fc_lport *lport) | |||
1888 | EXPORT_SYMBOL(fc_rport_init); | 1894 | EXPORT_SYMBOL(fc_rport_init); |
1889 | 1895 | ||
1890 | /** | 1896 | /** |
1897 | * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator. | ||
1898 | * @rdata: remote port private | ||
1899 | * @spp_len: service parameter page length | ||
1900 | * @rspp: received service parameter page | ||
1901 | * @spp: response service parameter page | ||
1902 | * | ||
1903 | * Returns the value for the response code to be placed in spp_flags; | ||
1904 | * Returns 0 if not an initiator. | ||
1905 | */ | ||
1906 | static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len, | ||
1907 | const struct fc_els_spp *rspp, | ||
1908 | struct fc_els_spp *spp) | ||
1909 | { | ||
1910 | struct fc_lport *lport = rdata->local_port; | ||
1911 | u32 fcp_parm; | ||
1912 | |||
1913 | fcp_parm = ntohl(rspp->spp_params); | ||
1914 | rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; | ||
1915 | if (fcp_parm & FCP_SPPF_INIT_FCN) | ||
1916 | rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR; | ||
1917 | if (fcp_parm & FCP_SPPF_TARG_FCN) | ||
1918 | rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET; | ||
1919 | if (fcp_parm & FCP_SPPF_RETRY) | ||
1920 | rdata->flags |= FC_RP_FLAGS_RETRY; | ||
1921 | rdata->supported_classes = FC_COS_CLASS3; | ||
1922 | |||
1923 | if (!(lport->service_params & FC_RPORT_ROLE_FCP_INITIATOR)) | ||
1924 | return 0; | ||
1925 | |||
1926 | spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR; | ||
1927 | |||
1928 | /* | ||
1929 | * OR in our service parameters with other providers (target), if any. | ||
1930 | */ | ||
1931 | fcp_parm = ntohl(spp->spp_params); | ||
1932 | spp->spp_params = htonl(fcp_parm | lport->service_params); | ||
1933 | return FC_SPP_RESP_ACK; | ||
1934 | } | ||
1935 | |||
1936 | /* | ||
1937 | * FC-4 provider ops for FCP initiator. | ||
1938 | */ | ||
1939 | struct fc4_prov fc_rport_fcp_init = { | ||
1940 | .prli = fc_rport_fcp_prli, | ||
1941 | }; | ||
1942 | |||
1943 | /** | ||
1944 | * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0 | ||
1945 | * @rdata: remote port private | ||
1946 | * @spp_len: service parameter page length | ||
1947 | * @rspp: received service parameter page | ||
1948 | * @spp: response service parameter page | ||
1949 | */ | ||
1950 | static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len, | ||
1951 | const struct fc_els_spp *rspp, | ||
1952 | struct fc_els_spp *spp) | ||
1953 | { | ||
1954 | if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) | ||
1955 | return FC_SPP_RESP_INVL; | ||
1956 | return FC_SPP_RESP_ACK; | ||
1957 | } | ||
1958 | |||
1959 | /* | ||
1960 | * FC-4 provider ops for type 0 service parameters. | ||
1961 | * | ||
1962 | * This handles the special case of type 0 which is always successful | ||
1963 | * but doesn't do anything otherwise. | ||
1964 | */ | ||
1965 | struct fc4_prov fc_rport_t0_prov = { | ||
1966 | .prli = fc_rport_t0_prli, | ||
1967 | }; | ||
1968 | |||
1969 | /** | ||
1891 | * fc_setup_rport() - Initialize the rport_event_queue | 1970 | * fc_setup_rport() - Initialize the rport_event_queue |
1892 | */ | 1971 | */ |
1893 | int fc_setup_rport(void) | 1972 | int fc_setup_rport(void) |