diff options
Diffstat (limited to 'drivers/target/tcm_fc/tfc_conf.c')
-rw-r--r-- | drivers/target/tcm_fc/tfc_conf.c | 677 |
1 files changed, 677 insertions, 0 deletions
diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c new file mode 100644 index 000000000000..fcdbbffe88cc --- /dev/null +++ b/drivers/target/tcm_fc/tfc_conf.c | |||
@@ -0,0 +1,677 @@ | |||
1 | /******************************************************************************* | ||
2 | * Filename: tcm_fc.c | ||
3 | * | ||
4 | * This file contains the configfs implementation for TCM_fc fabric node. | ||
5 | * Based on tcm_loop_configfs.c | ||
6 | * | ||
7 | * Copyright (c) 2010 Cisco Systems, Inc. | ||
8 | * Copyright (c) 2009,2010 Rising Tide, Inc. | ||
9 | * Copyright (c) 2009,2010 Linux-iSCSI.org | ||
10 | * | ||
11 | * Copyright (c) 2009,2010 Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | ****************************************************************************/ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/moduleparam.h> | ||
26 | #include <linux/version.h> | ||
27 | #include <generated/utsrelease.h> | ||
28 | #include <linux/utsname.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/kthread.h> | ||
32 | #include <linux/types.h> | ||
33 | #include <linux/string.h> | ||
34 | #include <linux/configfs.h> | ||
35 | #include <linux/ctype.h> | ||
36 | #include <asm/unaligned.h> | ||
37 | #include <scsi/scsi.h> | ||
38 | #include <scsi/scsi_host.h> | ||
39 | #include <scsi/scsi_device.h> | ||
40 | #include <scsi/scsi_cmnd.h> | ||
41 | #include <scsi/libfc.h> | ||
42 | |||
43 | #include <target/target_core_base.h> | ||
44 | #include <target/target_core_transport.h> | ||
45 | #include <target/target_core_fabric_ops.h> | ||
46 | #include <target/target_core_fabric_configfs.h> | ||
47 | #include <target/target_core_fabric_lib.h> | ||
48 | #include <target/target_core_device.h> | ||
49 | #include <target/target_core_tpg.h> | ||
50 | #include <target/target_core_configfs.h> | ||
51 | #include <target/target_core_base.h> | ||
52 | #include <target/configfs_macros.h> | ||
53 | |||
54 | #include "tcm_fc.h" | ||
55 | |||
56 | struct target_fabric_configfs *ft_configfs; | ||
57 | |||
58 | LIST_HEAD(ft_lport_list); | ||
59 | DEFINE_MUTEX(ft_lport_lock); | ||
60 | |||
61 | unsigned int ft_debug_logging; | ||
62 | module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR); | ||
63 | MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); | ||
64 | |||
65 | /* | ||
66 | * Parse WWN. | ||
67 | * If strict, we require lower-case hex and colon separators to be sure | ||
68 | * the name is the same as what would be generated by ft_format_wwn() | ||
69 | * so the name and wwn are mapped one-to-one. | ||
70 | */ | ||
71 | static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict) | ||
72 | { | ||
73 | const char *cp; | ||
74 | char c; | ||
75 | u32 nibble; | ||
76 | u32 byte = 0; | ||
77 | u32 pos = 0; | ||
78 | u32 err; | ||
79 | |||
80 | *wwn = 0; | ||
81 | for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) { | ||
82 | c = *cp; | ||
83 | if (c == '\n' && cp[1] == '\0') | ||
84 | continue; | ||
85 | if (strict && pos++ == 2 && byte++ < 7) { | ||
86 | pos = 0; | ||
87 | if (c == ':') | ||
88 | continue; | ||
89 | err = 1; | ||
90 | goto fail; | ||
91 | } | ||
92 | if (c == '\0') { | ||
93 | err = 2; | ||
94 | if (strict && byte != 8) | ||
95 | goto fail; | ||
96 | return cp - name; | ||
97 | } | ||
98 | err = 3; | ||
99 | if (isdigit(c)) | ||
100 | nibble = c - '0'; | ||
101 | else if (isxdigit(c) && (islower(c) || !strict)) | ||
102 | nibble = tolower(c) - 'a' + 10; | ||
103 | else | ||
104 | goto fail; | ||
105 | *wwn = (*wwn << 4) | nibble; | ||
106 | } | ||
107 | err = 4; | ||
108 | fail: | ||
109 | FT_CONF_DBG("err %u len %zu pos %u byte %u\n", | ||
110 | err, cp - name, pos, byte); | ||
111 | return -1; | ||
112 | } | ||
113 | |||
114 | ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn) | ||
115 | { | ||
116 | u8 b[8]; | ||
117 | |||
118 | put_unaligned_be64(wwn, b); | ||
119 | return snprintf(buf, len, | ||
120 | "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", | ||
121 | b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); | ||
122 | } | ||
123 | |||
124 | static ssize_t ft_wwn_show(void *arg, char *buf) | ||
125 | { | ||
126 | u64 *wwn = arg; | ||
127 | ssize_t len; | ||
128 | |||
129 | len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn); | ||
130 | buf[len++] = '\n'; | ||
131 | return len; | ||
132 | } | ||
133 | |||
134 | static ssize_t ft_wwn_store(void *arg, const char *buf, size_t len) | ||
135 | { | ||
136 | ssize_t ret; | ||
137 | u64 wwn; | ||
138 | |||
139 | ret = ft_parse_wwn(buf, &wwn, 0); | ||
140 | if (ret > 0) | ||
141 | *(u64 *)arg = wwn; | ||
142 | return ret; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * ACL auth ops. | ||
147 | */ | ||
148 | |||
149 | static ssize_t ft_nacl_show_port_name( | ||
150 | struct se_node_acl *se_nacl, | ||
151 | char *page) | ||
152 | { | ||
153 | struct ft_node_acl *acl = container_of(se_nacl, | ||
154 | struct ft_node_acl, se_node_acl); | ||
155 | |||
156 | return ft_wwn_show(&acl->node_auth.port_name, page); | ||
157 | } | ||
158 | |||
159 | static ssize_t ft_nacl_store_port_name( | ||
160 | struct se_node_acl *se_nacl, | ||
161 | const char *page, | ||
162 | size_t count) | ||
163 | { | ||
164 | struct ft_node_acl *acl = container_of(se_nacl, | ||
165 | struct ft_node_acl, se_node_acl); | ||
166 | |||
167 | return ft_wwn_store(&acl->node_auth.port_name, page, count); | ||
168 | } | ||
169 | |||
170 | TF_NACL_BASE_ATTR(ft, port_name, S_IRUGO | S_IWUSR); | ||
171 | |||
172 | static ssize_t ft_nacl_show_node_name( | ||
173 | struct se_node_acl *se_nacl, | ||
174 | char *page) | ||
175 | { | ||
176 | struct ft_node_acl *acl = container_of(se_nacl, | ||
177 | struct ft_node_acl, se_node_acl); | ||
178 | |||
179 | return ft_wwn_show(&acl->node_auth.node_name, page); | ||
180 | } | ||
181 | |||
182 | static ssize_t ft_nacl_store_node_name( | ||
183 | struct se_node_acl *se_nacl, | ||
184 | const char *page, | ||
185 | size_t count) | ||
186 | { | ||
187 | struct ft_node_acl *acl = container_of(se_nacl, | ||
188 | struct ft_node_acl, se_node_acl); | ||
189 | |||
190 | return ft_wwn_store(&acl->node_auth.node_name, page, count); | ||
191 | } | ||
192 | |||
193 | TF_NACL_BASE_ATTR(ft, node_name, S_IRUGO | S_IWUSR); | ||
194 | |||
195 | static struct configfs_attribute *ft_nacl_base_attrs[] = { | ||
196 | &ft_nacl_port_name.attr, | ||
197 | &ft_nacl_node_name.attr, | ||
198 | NULL, | ||
199 | }; | ||
200 | |||
201 | /* | ||
202 | * ACL ops. | ||
203 | */ | ||
204 | |||
205 | /* | ||
206 | * Add ACL for an initiator. The ACL is named arbitrarily. | ||
207 | * The port_name and/or node_name are attributes. | ||
208 | */ | ||
209 | static struct se_node_acl *ft_add_acl( | ||
210 | struct se_portal_group *se_tpg, | ||
211 | struct config_group *group, | ||
212 | const char *name) | ||
213 | { | ||
214 | struct ft_node_acl *acl; | ||
215 | struct ft_tpg *tpg; | ||
216 | u64 wwpn; | ||
217 | u32 q_depth; | ||
218 | |||
219 | FT_CONF_DBG("add acl %s\n", name); | ||
220 | tpg = container_of(se_tpg, struct ft_tpg, se_tpg); | ||
221 | |||
222 | if (ft_parse_wwn(name, &wwpn, 1) < 0) | ||
223 | return ERR_PTR(-EINVAL); | ||
224 | |||
225 | acl = kzalloc(sizeof(struct ft_node_acl), GFP_KERNEL); | ||
226 | if (!(acl)) | ||
227 | return ERR_PTR(-ENOMEM); | ||
228 | acl->node_auth.port_name = wwpn; | ||
229 | |||
230 | q_depth = 32; /* XXX bogus default - get from tpg? */ | ||
231 | return core_tpg_add_initiator_node_acl(&tpg->se_tpg, | ||
232 | &acl->se_node_acl, name, q_depth); | ||
233 | } | ||
234 | |||
235 | static void ft_del_acl(struct se_node_acl *se_acl) | ||
236 | { | ||
237 | struct se_portal_group *se_tpg = se_acl->se_tpg; | ||
238 | struct ft_tpg *tpg; | ||
239 | struct ft_node_acl *acl = container_of(se_acl, | ||
240 | struct ft_node_acl, se_node_acl); | ||
241 | |||
242 | FT_CONF_DBG("del acl %s\n", | ||
243 | config_item_name(&se_acl->acl_group.cg_item)); | ||
244 | |||
245 | tpg = container_of(se_tpg, struct ft_tpg, se_tpg); | ||
246 | FT_CONF_DBG("del acl %p se_acl %p tpg %p se_tpg %p\n", | ||
247 | acl, se_acl, tpg, &tpg->se_tpg); | ||
248 | |||
249 | core_tpg_del_initiator_node_acl(&tpg->se_tpg, se_acl, 1); | ||
250 | kfree(acl); | ||
251 | } | ||
252 | |||
253 | struct ft_node_acl *ft_acl_get(struct ft_tpg *tpg, struct fc_rport_priv *rdata) | ||
254 | { | ||
255 | struct ft_node_acl *found = NULL; | ||
256 | struct ft_node_acl *acl; | ||
257 | struct se_portal_group *se_tpg = &tpg->se_tpg; | ||
258 | struct se_node_acl *se_acl; | ||
259 | |||
260 | spin_lock_bh(&se_tpg->acl_node_lock); | ||
261 | list_for_each_entry(se_acl, &se_tpg->acl_node_list, acl_list) { | ||
262 | acl = container_of(se_acl, struct ft_node_acl, se_node_acl); | ||
263 | FT_CONF_DBG("acl %p port_name %llx\n", | ||
264 | acl, (unsigned long long)acl->node_auth.port_name); | ||
265 | if (acl->node_auth.port_name == rdata->ids.port_name || | ||
266 | acl->node_auth.node_name == rdata->ids.node_name) { | ||
267 | FT_CONF_DBG("acl %p port_name %llx matched\n", acl, | ||
268 | (unsigned long long)rdata->ids.port_name); | ||
269 | found = acl; | ||
270 | /* XXX need to hold onto ACL */ | ||
271 | break; | ||
272 | } | ||
273 | } | ||
274 | spin_unlock_bh(&se_tpg->acl_node_lock); | ||
275 | return found; | ||
276 | } | ||
277 | |||
278 | struct se_node_acl *ft_tpg_alloc_fabric_acl(struct se_portal_group *se_tpg) | ||
279 | { | ||
280 | struct ft_node_acl *acl; | ||
281 | |||
282 | acl = kzalloc(sizeof(*acl), GFP_KERNEL); | ||
283 | if (!(acl)) { | ||
284 | printk(KERN_ERR "Unable to allocate struct ft_node_acl\n"); | ||
285 | return NULL; | ||
286 | } | ||
287 | FT_CONF_DBG("acl %p\n", acl); | ||
288 | return &acl->se_node_acl; | ||
289 | } | ||
290 | |||
291 | static void ft_tpg_release_fabric_acl(struct se_portal_group *se_tpg, | ||
292 | struct se_node_acl *se_acl) | ||
293 | { | ||
294 | struct ft_node_acl *acl = container_of(se_acl, | ||
295 | struct ft_node_acl, se_node_acl); | ||
296 | |||
297 | FT_CONF_DBG(KERN_INFO "acl %p\n", acl); | ||
298 | kfree(acl); | ||
299 | } | ||
300 | |||
301 | /* | ||
302 | * local_port port_group (tpg) ops. | ||
303 | */ | ||
304 | static struct se_portal_group *ft_add_tpg( | ||
305 | struct se_wwn *wwn, | ||
306 | struct config_group *group, | ||
307 | const char *name) | ||
308 | { | ||
309 | struct ft_lport_acl *lacl; | ||
310 | struct ft_tpg *tpg; | ||
311 | unsigned long index; | ||
312 | int ret; | ||
313 | |||
314 | FT_CONF_DBG("tcm_fc: add tpg %s\n", name); | ||
315 | |||
316 | /* | ||
317 | * Name must be "tpgt_" followed by the index. | ||
318 | */ | ||
319 | if (strstr(name, "tpgt_") != name) | ||
320 | return NULL; | ||
321 | if (strict_strtoul(name + 5, 10, &index) || index > UINT_MAX) | ||
322 | return NULL; | ||
323 | |||
324 | lacl = container_of(wwn, struct ft_lport_acl, fc_lport_wwn); | ||
325 | tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); | ||
326 | if (!tpg) | ||
327 | return NULL; | ||
328 | tpg->index = index; | ||
329 | tpg->lport_acl = lacl; | ||
330 | INIT_LIST_HEAD(&tpg->lun_list); | ||
331 | transport_init_queue_obj(&tpg->qobj); | ||
332 | |||
333 | ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg, | ||
334 | (void *)tpg, TRANSPORT_TPG_TYPE_NORMAL); | ||
335 | if (ret < 0) { | ||
336 | kfree(tpg); | ||
337 | return NULL; | ||
338 | } | ||
339 | |||
340 | tpg->thread = kthread_run(ft_thread, tpg, "ft_tpg%lu", index); | ||
341 | if (IS_ERR(tpg->thread)) { | ||
342 | kfree(tpg); | ||
343 | return NULL; | ||
344 | } | ||
345 | |||
346 | mutex_lock(&ft_lport_lock); | ||
347 | list_add_tail(&tpg->list, &lacl->tpg_list); | ||
348 | mutex_unlock(&ft_lport_lock); | ||
349 | |||
350 | return &tpg->se_tpg; | ||
351 | } | ||
352 | |||
353 | static void ft_del_tpg(struct se_portal_group *se_tpg) | ||
354 | { | ||
355 | struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg); | ||
356 | |||
357 | FT_CONF_DBG("del tpg %s\n", | ||
358 | config_item_name(&tpg->se_tpg.tpg_group.cg_item)); | ||
359 | |||
360 | kthread_stop(tpg->thread); | ||
361 | |||
362 | /* Wait for sessions to be freed thru RCU, for BUG_ON below */ | ||
363 | synchronize_rcu(); | ||
364 | |||
365 | mutex_lock(&ft_lport_lock); | ||
366 | list_del(&tpg->list); | ||
367 | if (tpg->tport) { | ||
368 | tpg->tport->tpg = NULL; | ||
369 | tpg->tport = NULL; | ||
370 | } | ||
371 | mutex_unlock(&ft_lport_lock); | ||
372 | |||
373 | core_tpg_deregister(se_tpg); | ||
374 | kfree(tpg); | ||
375 | } | ||
376 | |||
377 | /* | ||
378 | * Verify that an lport is configured to use the tcm_fc module, and return | ||
379 | * the target port group that should be used. | ||
380 | * | ||
381 | * The caller holds ft_lport_lock. | ||
382 | */ | ||
383 | struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport) | ||
384 | { | ||
385 | struct ft_lport_acl *lacl; | ||
386 | struct ft_tpg *tpg; | ||
387 | |||
388 | list_for_each_entry(lacl, &ft_lport_list, list) { | ||
389 | if (lacl->wwpn == lport->wwpn) { | ||
390 | list_for_each_entry(tpg, &lacl->tpg_list, list) | ||
391 | return tpg; /* XXX for now return first entry */ | ||
392 | return NULL; | ||
393 | } | ||
394 | } | ||
395 | return NULL; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * target config instance ops. | ||
400 | */ | ||
401 | |||
402 | /* | ||
403 | * Add lport to allowed config. | ||
404 | * The name is the WWPN in lower-case ASCII, colon-separated bytes. | ||
405 | */ | ||
406 | static struct se_wwn *ft_add_lport( | ||
407 | struct target_fabric_configfs *tf, | ||
408 | struct config_group *group, | ||
409 | const char *name) | ||
410 | { | ||
411 | struct ft_lport_acl *lacl; | ||
412 | struct ft_lport_acl *old_lacl; | ||
413 | u64 wwpn; | ||
414 | |||
415 | FT_CONF_DBG("add lport %s\n", name); | ||
416 | if (ft_parse_wwn(name, &wwpn, 1) < 0) | ||
417 | return NULL; | ||
418 | lacl = kzalloc(sizeof(*lacl), GFP_KERNEL); | ||
419 | if (!lacl) | ||
420 | return NULL; | ||
421 | lacl->wwpn = wwpn; | ||
422 | INIT_LIST_HEAD(&lacl->tpg_list); | ||
423 | |||
424 | mutex_lock(&ft_lport_lock); | ||
425 | list_for_each_entry(old_lacl, &ft_lport_list, list) { | ||
426 | if (old_lacl->wwpn == wwpn) { | ||
427 | mutex_unlock(&ft_lport_lock); | ||
428 | kfree(lacl); | ||
429 | return NULL; | ||
430 | } | ||
431 | } | ||
432 | list_add_tail(&lacl->list, &ft_lport_list); | ||
433 | ft_format_wwn(lacl->name, sizeof(lacl->name), wwpn); | ||
434 | mutex_unlock(&ft_lport_lock); | ||
435 | |||
436 | return &lacl->fc_lport_wwn; | ||
437 | } | ||
438 | |||
439 | static void ft_del_lport(struct se_wwn *wwn) | ||
440 | { | ||
441 | struct ft_lport_acl *lacl = container_of(wwn, | ||
442 | struct ft_lport_acl, fc_lport_wwn); | ||
443 | |||
444 | FT_CONF_DBG("del lport %s\n", | ||
445 | config_item_name(&wwn->wwn_group.cg_item)); | ||
446 | mutex_lock(&ft_lport_lock); | ||
447 | list_del(&lacl->list); | ||
448 | mutex_unlock(&ft_lport_lock); | ||
449 | |||
450 | kfree(lacl); | ||
451 | } | ||
452 | |||
453 | static ssize_t ft_wwn_show_attr_version( | ||
454 | struct target_fabric_configfs *tf, | ||
455 | char *page) | ||
456 | { | ||
457 | return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on " | ||
458 | ""UTS_RELEASE"\n", utsname()->sysname, utsname()->machine); | ||
459 | } | ||
460 | |||
461 | TF_WWN_ATTR_RO(ft, version); | ||
462 | |||
463 | static struct configfs_attribute *ft_wwn_attrs[] = { | ||
464 | &ft_wwn_version.attr, | ||
465 | NULL, | ||
466 | }; | ||
467 | |||
468 | static char *ft_get_fabric_name(void) | ||
469 | { | ||
470 | return "fc"; | ||
471 | } | ||
472 | |||
473 | static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg) | ||
474 | { | ||
475 | struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr; | ||
476 | |||
477 | return tpg->lport_acl->name; | ||
478 | } | ||
479 | |||
480 | static u16 ft_get_tag(struct se_portal_group *se_tpg) | ||
481 | { | ||
482 | struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr; | ||
483 | |||
484 | /* | ||
485 | * This tag is used when forming SCSI Name identifier in EVPD=1 0x83 | ||
486 | * to represent the SCSI Target Port. | ||
487 | */ | ||
488 | return tpg->index; | ||
489 | } | ||
490 | |||
491 | static u32 ft_get_default_depth(struct se_portal_group *se_tpg) | ||
492 | { | ||
493 | return 1; | ||
494 | } | ||
495 | |||
496 | static int ft_check_false(struct se_portal_group *se_tpg) | ||
497 | { | ||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | static void ft_set_default_node_attr(struct se_node_acl *se_nacl) | ||
502 | { | ||
503 | } | ||
504 | |||
505 | static u16 ft_get_fabric_sense_len(void) | ||
506 | { | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static u16 ft_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_len) | ||
511 | { | ||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg) | ||
516 | { | ||
517 | struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr; | ||
518 | |||
519 | return tpg->index; | ||
520 | } | ||
521 | |||
522 | static u64 ft_pack_lun(unsigned int index) | ||
523 | { | ||
524 | WARN_ON(index >= 256); | ||
525 | /* Caller wants this byte-swapped */ | ||
526 | return cpu_to_le64((index & 0xff) << 8); | ||
527 | } | ||
528 | |||
529 | static struct target_core_fabric_ops ft_fabric_ops = { | ||
530 | .get_fabric_name = ft_get_fabric_name, | ||
531 | .get_fabric_proto_ident = fc_get_fabric_proto_ident, | ||
532 | .tpg_get_wwn = ft_get_fabric_wwn, | ||
533 | .tpg_get_tag = ft_get_tag, | ||
534 | .tpg_get_default_depth = ft_get_default_depth, | ||
535 | .tpg_get_pr_transport_id = fc_get_pr_transport_id, | ||
536 | .tpg_get_pr_transport_id_len = fc_get_pr_transport_id_len, | ||
537 | .tpg_parse_pr_out_transport_id = fc_parse_pr_out_transport_id, | ||
538 | .tpg_check_demo_mode = ft_check_false, | ||
539 | .tpg_check_demo_mode_cache = ft_check_false, | ||
540 | .tpg_check_demo_mode_write_protect = ft_check_false, | ||
541 | .tpg_check_prod_mode_write_protect = ft_check_false, | ||
542 | .tpg_alloc_fabric_acl = ft_tpg_alloc_fabric_acl, | ||
543 | .tpg_release_fabric_acl = ft_tpg_release_fabric_acl, | ||
544 | .tpg_get_inst_index = ft_tpg_get_inst_index, | ||
545 | .check_stop_free = ft_check_stop_free, | ||
546 | .release_cmd_to_pool = ft_release_cmd, | ||
547 | .release_cmd_direct = ft_release_cmd, | ||
548 | .shutdown_session = ft_sess_shutdown, | ||
549 | .close_session = ft_sess_close, | ||
550 | .stop_session = ft_sess_stop, | ||
551 | .fall_back_to_erl0 = ft_sess_set_erl0, | ||
552 | .sess_logged_in = ft_sess_logged_in, | ||
553 | .sess_get_index = ft_sess_get_index, | ||
554 | .sess_get_initiator_sid = NULL, | ||
555 | .write_pending = ft_write_pending, | ||
556 | .write_pending_status = ft_write_pending_status, | ||
557 | .set_default_node_attributes = ft_set_default_node_attr, | ||
558 | .get_task_tag = ft_get_task_tag, | ||
559 | .get_cmd_state = ft_get_cmd_state, | ||
560 | .new_cmd_failure = ft_new_cmd_failure, | ||
561 | .queue_data_in = ft_queue_data_in, | ||
562 | .queue_status = ft_queue_status, | ||
563 | .queue_tm_rsp = ft_queue_tm_resp, | ||
564 | .get_fabric_sense_len = ft_get_fabric_sense_len, | ||
565 | .set_fabric_sense_len = ft_set_fabric_sense_len, | ||
566 | .is_state_remove = ft_is_state_remove, | ||
567 | .pack_lun = ft_pack_lun, | ||
568 | /* | ||
569 | * Setup function pointers for generic logic in | ||
570 | * target_core_fabric_configfs.c | ||
571 | */ | ||
572 | .fabric_make_wwn = &ft_add_lport, | ||
573 | .fabric_drop_wwn = &ft_del_lport, | ||
574 | .fabric_make_tpg = &ft_add_tpg, | ||
575 | .fabric_drop_tpg = &ft_del_tpg, | ||
576 | .fabric_post_link = NULL, | ||
577 | .fabric_pre_unlink = NULL, | ||
578 | .fabric_make_np = NULL, | ||
579 | .fabric_drop_np = NULL, | ||
580 | .fabric_make_nodeacl = &ft_add_acl, | ||
581 | .fabric_drop_nodeacl = &ft_del_acl, | ||
582 | }; | ||
583 | |||
584 | int ft_register_configfs(void) | ||
585 | { | ||
586 | struct target_fabric_configfs *fabric; | ||
587 | int ret; | ||
588 | |||
589 | /* | ||
590 | * Register the top level struct config_item_type with TCM core | ||
591 | */ | ||
592 | fabric = target_fabric_configfs_init(THIS_MODULE, "fc"); | ||
593 | if (!fabric) { | ||
594 | printk(KERN_INFO "%s: target_fabric_configfs_init() failed!\n", | ||
595 | __func__); | ||
596 | return -1; | ||
597 | } | ||
598 | fabric->tf_ops = ft_fabric_ops; | ||
599 | |||
600 | /* Allowing support for task_sg_chaining */ | ||
601 | fabric->tf_ops.task_sg_chaining = 1; | ||
602 | |||
603 | /* | ||
604 | * Setup default attribute lists for various fabric->tf_cit_tmpl | ||
605 | */ | ||
606 | TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = ft_wwn_attrs; | ||
607 | TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = NULL; | ||
608 | TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL; | ||
609 | TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL; | ||
610 | TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL; | ||
611 | TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = | ||
612 | ft_nacl_base_attrs; | ||
613 | TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; | ||
614 | TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL; | ||
615 | TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL; | ||
616 | /* | ||
617 | * register the fabric for use within TCM | ||
618 | */ | ||
619 | ret = target_fabric_configfs_register(fabric); | ||
620 | if (ret < 0) { | ||
621 | FT_CONF_DBG("target_fabric_configfs_register() for" | ||
622 | " FC Target failed!\n"); | ||
623 | printk(KERN_INFO | ||
624 | "%s: target_fabric_configfs_register() failed!\n", | ||
625 | __func__); | ||
626 | target_fabric_configfs_free(fabric); | ||
627 | return -1; | ||
628 | } | ||
629 | |||
630 | /* | ||
631 | * Setup our local pointer to *fabric. | ||
632 | */ | ||
633 | ft_configfs = fabric; | ||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | void ft_deregister_configfs(void) | ||
638 | { | ||
639 | if (!ft_configfs) | ||
640 | return; | ||
641 | target_fabric_configfs_deregister(ft_configfs); | ||
642 | ft_configfs = NULL; | ||
643 | } | ||
644 | |||
645 | static struct notifier_block ft_notifier = { | ||
646 | .notifier_call = ft_lport_notify | ||
647 | }; | ||
648 | |||
649 | static int __init ft_init(void) | ||
650 | { | ||
651 | if (ft_register_configfs()) | ||
652 | return -1; | ||
653 | if (fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov)) { | ||
654 | ft_deregister_configfs(); | ||
655 | return -1; | ||
656 | } | ||
657 | blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier); | ||
658 | fc_lport_iterate(ft_lport_add, NULL); | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static void __exit ft_exit(void) | ||
663 | { | ||
664 | blocking_notifier_chain_unregister(&fc_lport_notifier_head, | ||
665 | &ft_notifier); | ||
666 | fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov); | ||
667 | fc_lport_iterate(ft_lport_del, NULL); | ||
668 | ft_deregister_configfs(); | ||
669 | synchronize_rcu(); | ||
670 | } | ||
671 | |||
672 | #ifdef MODULE | ||
673 | MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION); | ||
674 | MODULE_LICENSE("GPL"); | ||
675 | module_init(ft_init); | ||
676 | module_exit(ft_exit); | ||
677 | #endif /* MODULE */ | ||