Commit | Line | Data |
---|---|---|
8866a5d9 RL |
1 | /* |
2 | * Copyright(c) 2009 Intel Corporation. All rights reserved. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms and conditions of the GNU General Public License, | |
6 | * version 2, as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
11 | * more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License along with | |
14 | * this program; if not, write to the Free Software Foundation, Inc., | |
15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
16 | * | |
17 | * Maintained at www.Open-FCoE.org | |
18 | */ | |
19 | ||
20 | #include <linux/kernel.h> | |
21 | #include <linux/types.h> | |
22 | #include <linux/scatterlist.h> | |
23 | #include <linux/crc32.h> | |
acf3368f | 24 | #include <linux/module.h> |
8866a5d9 RL |
25 | |
26 | #include <scsi/libfc.h> | |
24f089e2 | 27 | #include <scsi/fc_encode.h> |
8866a5d9 RL |
28 | |
29 | #include "fc_libfc.h" | |
30 | ||
31 | MODULE_AUTHOR("Open-FCoE.org"); | |
32 | MODULE_DESCRIPTION("libfc"); | |
33 | MODULE_LICENSE("GPL v2"); | |
34 | ||
35 | unsigned int fc_debug_logging; | |
36 | module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR); | |
37 | MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); | |
93e6d5ab | 38 | |
96ad8464 | 39 | DEFINE_MUTEX(fc_prov_mutex); |
70d53b04 JE |
40 | static LIST_HEAD(fc_local_ports); |
41 | struct blocking_notifier_head fc_lport_notifier_head = | |
42 | BLOCKING_NOTIFIER_INIT(fc_lport_notifier_head); | |
43 | EXPORT_SYMBOL(fc_lport_notifier_head); | |
96ad8464 JE |
44 | |
45 | /* | |
46 | * Providers which primarily send requests and PRLIs. | |
47 | */ | |
48 | struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = { | |
49 | [0] = &fc_rport_t0_prov, | |
50 | [FC_TYPE_FCP] = &fc_rport_fcp_init, | |
51 | }; | |
52 | ||
53 | /* | |
54 | * Providers which receive requests. | |
55 | */ | |
56 | struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = { | |
57 | [FC_TYPE_ELS] = &fc_lport_els_prov, | |
58 | }; | |
59 | ||
93e6d5ab RL |
60 | /** |
61 | * libfc_init() - Initialize libfc.ko | |
62 | */ | |
63 | static int __init libfc_init(void) | |
64 | { | |
65 | int rc = 0; | |
66 | ||
67 | rc = fc_setup_fcp(); | |
68 | if (rc) | |
69 | return rc; | |
70 | ||
71 | rc = fc_setup_exch_mgr(); | |
72 | if (rc) | |
73 | goto destroy_pkt_cache; | |
74 | ||
75 | rc = fc_setup_rport(); | |
76 | if (rc) | |
77 | goto destroy_em; | |
78 | ||
79 | return rc; | |
80 | destroy_em: | |
81 | fc_destroy_exch_mgr(); | |
82 | destroy_pkt_cache: | |
83 | fc_destroy_fcp(); | |
84 | return rc; | |
85 | } | |
86 | module_init(libfc_init); | |
87 | ||
88 | /** | |
89 | * libfc_exit() - Tear down libfc.ko | |
90 | */ | |
91 | static void __exit libfc_exit(void) | |
92 | { | |
93 | fc_destroy_fcp(); | |
94 | fc_destroy_exch_mgr(); | |
95 | fc_destroy_rport(); | |
96 | } | |
97 | module_exit(libfc_exit); | |
58682874 RL |
98 | |
99 | /** | |
100 | * fc_copy_buffer_to_sglist() - This routine copies the data of a buffer | |
3a3b42bf | 101 | * into a scatter-gather list (SG list). |
58682874 RL |
102 | * |
103 | * @buf: pointer to the data buffer. | |
104 | * @len: the byte-length of the data buffer. | |
105 | * @sg: pointer to the pointer of the SG list. | |
106 | * @nents: pointer to the remaining number of entries in the SG list. | |
107 | * @offset: pointer to the current offset in the SG list. | |
58682874 | 108 | * @crc: pointer to the 32-bit crc value. |
3a3b42bf | 109 | * If crc is NULL, CRC is not calculated. |
58682874 RL |
110 | */ |
111 | u32 fc_copy_buffer_to_sglist(void *buf, size_t len, | |
112 | struct scatterlist *sg, | |
113 | u32 *nents, size_t *offset, | |
77dfce07 | 114 | u32 *crc) |
58682874 RL |
115 | { |
116 | size_t remaining = len; | |
117 | u32 copy_len = 0; | |
118 | ||
119 | while (remaining > 0 && sg) { | |
120 | size_t off, sg_bytes; | |
121 | void *page_addr; | |
122 | ||
123 | if (*offset >= sg->length) { | |
124 | /* | |
125 | * Check for end and drop resources | |
126 | * from the last iteration. | |
127 | */ | |
128 | if (!(*nents)) | |
129 | break; | |
130 | --(*nents); | |
131 | *offset -= sg->length; | |
132 | sg = sg_next(sg); | |
133 | continue; | |
134 | } | |
135 | sg_bytes = min(remaining, sg->length - *offset); | |
136 | ||
137 | /* | |
138 | * The scatterlist item may be bigger than PAGE_SIZE, | |
139 | * but we are limited to mapping PAGE_SIZE at a time. | |
140 | */ | |
141 | off = *offset + sg->offset; | |
142 | sg_bytes = min(sg_bytes, | |
143 | (size_t)(PAGE_SIZE - (off & ~PAGE_MASK))); | |
77dfce07 | 144 | page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT)); |
58682874 RL |
145 | if (crc) |
146 | *crc = crc32(*crc, buf, sg_bytes); | |
147 | memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, sg_bytes); | |
77dfce07 | 148 | kunmap_atomic(page_addr); |
58682874 RL |
149 | buf += sg_bytes; |
150 | *offset += sg_bytes; | |
151 | remaining -= sg_bytes; | |
152 | copy_len += sg_bytes; | |
153 | } | |
154 | return copy_len; | |
155 | } | |
24f089e2 JE |
156 | |
157 | /** | |
158 | * fc_fill_hdr() - fill FC header fields based on request | |
159 | * @fp: reply frame containing header to be filled in | |
160 | * @in_fp: request frame containing header to use in filling in reply | |
161 | * @r_ctl: R_CTL value for header | |
162 | * @f_ctl: F_CTL value for header, with 0 pad | |
163 | * @seq_cnt: sequence count for the header, ignored if frame has a sequence | |
164 | * @parm_offset: parameter / offset value | |
165 | */ | |
166 | void fc_fill_hdr(struct fc_frame *fp, const struct fc_frame *in_fp, | |
167 | enum fc_rctl r_ctl, u32 f_ctl, u16 seq_cnt, u32 parm_offset) | |
168 | { | |
169 | struct fc_frame_header *fh; | |
170 | struct fc_frame_header *in_fh; | |
171 | struct fc_seq *sp; | |
172 | u32 fill; | |
173 | ||
174 | fh = __fc_frame_header_get(fp); | |
175 | in_fh = __fc_frame_header_get(in_fp); | |
176 | ||
177 | if (f_ctl & FC_FC_END_SEQ) { | |
178 | fill = -fr_len(fp) & 3; | |
179 | if (fill) { | |
180 | /* TODO, this may be a problem with fragmented skb */ | |
181 | memset(skb_put(fp_skb(fp), fill), 0, fill); | |
182 | f_ctl |= fill; | |
183 | } | |
184 | fr_eof(fp) = FC_EOF_T; | |
185 | } else { | |
186 | WARN_ON(fr_len(fp) % 4 != 0); /* no pad to non last frame */ | |
187 | fr_eof(fp) = FC_EOF_N; | |
188 | } | |
189 | ||
190 | fh->fh_r_ctl = r_ctl; | |
191 | memcpy(fh->fh_d_id, in_fh->fh_s_id, sizeof(fh->fh_d_id)); | |
192 | memcpy(fh->fh_s_id, in_fh->fh_d_id, sizeof(fh->fh_s_id)); | |
193 | fh->fh_type = in_fh->fh_type; | |
194 | hton24(fh->fh_f_ctl, f_ctl); | |
195 | fh->fh_ox_id = in_fh->fh_ox_id; | |
196 | fh->fh_rx_id = in_fh->fh_rx_id; | |
197 | fh->fh_cs_ctl = 0; | |
198 | fh->fh_df_ctl = 0; | |
199 | fh->fh_parm_offset = htonl(parm_offset); | |
200 | ||
201 | sp = fr_seq(in_fp); | |
202 | if (sp) { | |
203 | fr_seq(fp) = sp; | |
204 | fh->fh_seq_id = sp->id; | |
205 | seq_cnt = sp->cnt; | |
206 | } else { | |
207 | fh->fh_seq_id = 0; | |
208 | } | |
209 | fh->fh_seq_cnt = ntohs(seq_cnt); | |
210 | fr_sof(fp) = seq_cnt ? FC_SOF_N3 : FC_SOF_I3; | |
211 | fr_encaps(fp) = fr_encaps(in_fp); | |
212 | } | |
213 | EXPORT_SYMBOL(fc_fill_hdr); | |
214 | ||
215 | /** | |
216 | * fc_fill_reply_hdr() - fill FC reply header fields based on request | |
217 | * @fp: reply frame containing header to be filled in | |
218 | * @in_fp: request frame containing header to use in filling in reply | |
219 | * @r_ctl: R_CTL value for reply | |
220 | * @parm_offset: parameter / offset value | |
221 | */ | |
222 | void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp, | |
223 | enum fc_rctl r_ctl, u32 parm_offset) | |
224 | { | |
225 | struct fc_seq *sp; | |
226 | ||
227 | sp = fr_seq(in_fp); | |
228 | if (sp) | |
229 | fr_seq(fp) = fr_dev(in_fp)->tt.seq_start_next(sp); | |
230 | fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset); | |
231 | } | |
232 | EXPORT_SYMBOL(fc_fill_reply_hdr); | |
96ad8464 | 233 | |
acc1a921 KP |
234 | /** |
235 | * fc_fc4_conf_lport_params() - Modify "service_params" of specified lport | |
236 | * if there is service provider (target provider) registered with libfc | |
237 | * for specified "fc_ft_type" | |
238 | * @lport: Local port which service_params needs to be modified | |
239 | * @type: FC-4 type, such as FC_TYPE_FCP | |
240 | */ | |
241 | void fc_fc4_conf_lport_params(struct fc_lport *lport, enum fc_fh_type type) | |
242 | { | |
243 | struct fc4_prov *prov_entry; | |
244 | BUG_ON(type >= FC_FC4_PROV_SIZE); | |
245 | BUG_ON(!lport); | |
246 | prov_entry = fc_passive_prov[type]; | |
247 | if (type == FC_TYPE_FCP) { | |
248 | if (prov_entry && prov_entry->recv) | |
249 | lport->service_params |= FCP_SPPF_TARG_FCN; | |
250 | } | |
251 | } | |
252 | ||
70d53b04 JE |
253 | void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg) |
254 | { | |
255 | struct fc_lport *lport; | |
256 | ||
257 | mutex_lock(&fc_prov_mutex); | |
258 | list_for_each_entry(lport, &fc_local_ports, lport_list) | |
259 | notify(lport, arg); | |
260 | mutex_unlock(&fc_prov_mutex); | |
261 | } | |
262 | EXPORT_SYMBOL(fc_lport_iterate); | |
263 | ||
96ad8464 JE |
264 | /** |
265 | * fc_fc4_register_provider() - register FC-4 upper-level provider. | |
266 | * @type: FC-4 type, such as FC_TYPE_FCP | |
267 | * @prov: structure describing provider including ops vector. | |
268 | * | |
269 | * Returns 0 on success, negative error otherwise. | |
270 | */ | |
271 | int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov) | |
272 | { | |
273 | struct fc4_prov **prov_entry; | |
274 | int ret = 0; | |
275 | ||
276 | if (type >= FC_FC4_PROV_SIZE) | |
277 | return -EINVAL; | |
278 | mutex_lock(&fc_prov_mutex); | |
279 | prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type; | |
280 | if (*prov_entry) | |
281 | ret = -EBUSY; | |
282 | else | |
283 | *prov_entry = prov; | |
284 | mutex_unlock(&fc_prov_mutex); | |
285 | return ret; | |
286 | } | |
287 | EXPORT_SYMBOL(fc_fc4_register_provider); | |
288 | ||
289 | /** | |
290 | * fc_fc4_deregister_provider() - deregister FC-4 upper-level provider. | |
291 | * @type: FC-4 type, such as FC_TYPE_FCP | |
292 | * @prov: structure describing provider including ops vector. | |
293 | */ | |
294 | void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov) | |
295 | { | |
296 | BUG_ON(type >= FC_FC4_PROV_SIZE); | |
297 | mutex_lock(&fc_prov_mutex); | |
298 | if (prov->recv) | |
299 | rcu_assign_pointer(fc_passive_prov[type], NULL); | |
300 | else | |
301 | rcu_assign_pointer(fc_active_prov[type], NULL); | |
302 | mutex_unlock(&fc_prov_mutex); | |
303 | synchronize_rcu(); | |
304 | } | |
305 | EXPORT_SYMBOL(fc_fc4_deregister_provider); | |
70d53b04 JE |
306 | |
307 | /** | |
308 | * fc_fc4_add_lport() - add new local port to list and run notifiers. | |
309 | * @lport: The new local port. | |
310 | */ | |
311 | void fc_fc4_add_lport(struct fc_lport *lport) | |
312 | { | |
313 | mutex_lock(&fc_prov_mutex); | |
314 | list_add_tail(&lport->lport_list, &fc_local_ports); | |
315 | blocking_notifier_call_chain(&fc_lport_notifier_head, | |
316 | FC_LPORT_EV_ADD, lport); | |
317 | mutex_unlock(&fc_prov_mutex); | |
318 | } | |
319 | ||
320 | /** | |
321 | * fc_fc4_del_lport() - remove local port from list and run notifiers. | |
322 | * @lport: The new local port. | |
323 | */ | |
324 | void fc_fc4_del_lport(struct fc_lport *lport) | |
325 | { | |
326 | mutex_lock(&fc_prov_mutex); | |
327 | list_del(&lport->lport_list); | |
328 | blocking_notifier_call_chain(&fc_lport_notifier_head, | |
329 | FC_LPORT_EV_DEL, lport); | |
330 | mutex_unlock(&fc_prov_mutex); | |
331 | } |