Root/
1 | /* |
2 | * iSCSI transport class definitions |
3 | * |
4 | * Copyright (C) IBM Corporation, 2004 |
5 | * Copyright (C) Mike Christie, 2004 - 2005 |
6 | * Copyright (C) Dmitry Yusupov, 2004 - 2005 |
7 | * Copyright (C) Alex Aizman, 2004 - 2005 |
8 | * |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by |
11 | * the Free Software Foundation; either version 2 of the License, or |
12 | * (at your option) any later version. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22 | */ |
23 | #include <linux/module.h> |
24 | #include <linux/mutex.h> |
25 | #include <linux/slab.h> |
26 | #include <linux/bsg-lib.h> |
27 | #include <linux/idr.h> |
28 | #include <linux/list.h> |
29 | #include <net/tcp.h> |
30 | #include <scsi/scsi.h> |
31 | #include <scsi/scsi_host.h> |
32 | #include <scsi/scsi_device.h> |
33 | #include <scsi/scsi_transport.h> |
34 | #include <scsi/scsi_transport_iscsi.h> |
35 | #include <scsi/iscsi_if.h> |
36 | #include <scsi/scsi_cmnd.h> |
37 | #include <scsi/scsi_bsg_iscsi.h> |
38 | |
39 | #define ISCSI_TRANSPORT_VERSION "2.0-870" |
40 | |
41 | static int dbg_session; |
42 | module_param_named(debug_session, dbg_session, int, |
43 | S_IRUGO | S_IWUSR); |
44 | MODULE_PARM_DESC(debug_session, |
45 | "Turn on debugging for sessions in scsi_transport_iscsi " |
46 | "module. Set to 1 to turn on, and zero to turn off. Default " |
47 | "is off."); |
48 | |
49 | static int dbg_conn; |
50 | module_param_named(debug_conn, dbg_conn, int, |
51 | S_IRUGO | S_IWUSR); |
52 | MODULE_PARM_DESC(debug_conn, |
53 | "Turn on debugging for connections in scsi_transport_iscsi " |
54 | "module. Set to 1 to turn on, and zero to turn off. Default " |
55 | "is off."); |
56 | |
57 | #define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...) \ |
58 | do { \ |
59 | if (dbg_session) \ |
60 | iscsi_cls_session_printk(KERN_INFO, _session, \ |
61 | "%s: " dbg_fmt, \ |
62 | __func__, ##arg); \ |
63 | } while (0); |
64 | |
65 | #define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...) \ |
66 | do { \ |
67 | if (dbg_conn) \ |
68 | iscsi_cls_conn_printk(KERN_INFO, _conn, \ |
69 | "%s: " dbg_fmt, \ |
70 | __func__, ##arg); \ |
71 | } while (0); |
72 | |
73 | struct iscsi_internal { |
74 | struct scsi_transport_template t; |
75 | struct iscsi_transport *iscsi_transport; |
76 | struct list_head list; |
77 | struct device dev; |
78 | |
79 | struct transport_container conn_cont; |
80 | struct transport_container session_cont; |
81 | }; |
82 | |
83 | static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ |
84 | static struct workqueue_struct *iscsi_eh_timer_workq; |
85 | |
86 | static DEFINE_IDA(iscsi_sess_ida); |
87 | /* |
88 | * list of registered transports and lock that must |
89 | * be held while accessing list. The iscsi_transport_lock must |
90 | * be acquired after the rx_queue_mutex. |
91 | */ |
92 | static LIST_HEAD(iscsi_transports); |
93 | static DEFINE_SPINLOCK(iscsi_transport_lock); |
94 | |
95 | #define to_iscsi_internal(tmpl) \ |
96 | container_of(tmpl, struct iscsi_internal, t) |
97 | |
98 | #define dev_to_iscsi_internal(_dev) \ |
99 | container_of(_dev, struct iscsi_internal, dev) |
100 | |
101 | static void iscsi_transport_release(struct device *dev) |
102 | { |
103 | struct iscsi_internal *priv = dev_to_iscsi_internal(dev); |
104 | kfree(priv); |
105 | } |
106 | |
107 | /* |
108 | * iscsi_transport_class represents the iscsi_transports that are |
109 | * registered. |
110 | */ |
111 | static struct class iscsi_transport_class = { |
112 | .name = "iscsi_transport", |
113 | .dev_release = iscsi_transport_release, |
114 | }; |
115 | |
116 | static ssize_t |
117 | show_transport_handle(struct device *dev, struct device_attribute *attr, |
118 | char *buf) |
119 | { |
120 | struct iscsi_internal *priv = dev_to_iscsi_internal(dev); |
121 | return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport)); |
122 | } |
123 | static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL); |
124 | |
125 | #define show_transport_attr(name, format) \ |
126 | static ssize_t \ |
127 | show_transport_##name(struct device *dev, \ |
128 | struct device_attribute *attr,char *buf) \ |
129 | { \ |
130 | struct iscsi_internal *priv = dev_to_iscsi_internal(dev); \ |
131 | return sprintf(buf, format"\n", priv->iscsi_transport->name); \ |
132 | } \ |
133 | static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL); |
134 | |
135 | show_transport_attr(caps, "0x%x"); |
136 | |
137 | static struct attribute *iscsi_transport_attrs[] = { |
138 | &dev_attr_handle.attr, |
139 | &dev_attr_caps.attr, |
140 | NULL, |
141 | }; |
142 | |
143 | static struct attribute_group iscsi_transport_group = { |
144 | .attrs = iscsi_transport_attrs, |
145 | }; |
146 | |
147 | /* |
148 | * iSCSI endpoint attrs |
149 | */ |
150 | #define iscsi_dev_to_endpoint(_dev) \ |
151 | container_of(_dev, struct iscsi_endpoint, dev) |
152 | |
153 | #define ISCSI_ATTR(_prefix,_name,_mode,_show,_store) \ |
154 | struct device_attribute dev_attr_##_prefix##_##_name = \ |
155 | __ATTR(_name,_mode,_show,_store) |
156 | |
157 | static void iscsi_endpoint_release(struct device *dev) |
158 | { |
159 | struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); |
160 | kfree(ep); |
161 | } |
162 | |
163 | static struct class iscsi_endpoint_class = { |
164 | .name = "iscsi_endpoint", |
165 | .dev_release = iscsi_endpoint_release, |
166 | }; |
167 | |
168 | static ssize_t |
169 | show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf) |
170 | { |
171 | struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); |
172 | return sprintf(buf, "%llu\n", (unsigned long long) ep->id); |
173 | } |
174 | static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL); |
175 | |
176 | static struct attribute *iscsi_endpoint_attrs[] = { |
177 | &dev_attr_ep_handle.attr, |
178 | NULL, |
179 | }; |
180 | |
181 | static struct attribute_group iscsi_endpoint_group = { |
182 | .attrs = iscsi_endpoint_attrs, |
183 | }; |
184 | |
185 | #define ISCSI_MAX_EPID -1 |
186 | |
187 | static int iscsi_match_epid(struct device *dev, const void *data) |
188 | { |
189 | struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); |
190 | const uint64_t *epid = data; |
191 | |
192 | return *epid == ep->id; |
193 | } |
194 | |
195 | struct iscsi_endpoint * |
196 | iscsi_create_endpoint(int dd_size) |
197 | { |
198 | struct device *dev; |
199 | struct iscsi_endpoint *ep; |
200 | uint64_t id; |
201 | int err; |
202 | |
203 | for (id = 1; id < ISCSI_MAX_EPID; id++) { |
204 | dev = class_find_device(&iscsi_endpoint_class, NULL, &id, |
205 | iscsi_match_epid); |
206 | if (!dev) |
207 | break; |
208 | } |
209 | if (id == ISCSI_MAX_EPID) { |
210 | printk(KERN_ERR "Too many connections. Max supported %u\n", |
211 | ISCSI_MAX_EPID - 1); |
212 | return NULL; |
213 | } |
214 | |
215 | ep = kzalloc(sizeof(*ep) + dd_size, GFP_KERNEL); |
216 | if (!ep) |
217 | return NULL; |
218 | |
219 | ep->id = id; |
220 | ep->dev.class = &iscsi_endpoint_class; |
221 | dev_set_name(&ep->dev, "ep-%llu", (unsigned long long) id); |
222 | err = device_register(&ep->dev); |
223 | if (err) |
224 | goto free_ep; |
225 | |
226 | err = sysfs_create_group(&ep->dev.kobj, &iscsi_endpoint_group); |
227 | if (err) |
228 | goto unregister_dev; |
229 | |
230 | if (dd_size) |
231 | ep->dd_data = &ep[1]; |
232 | return ep; |
233 | |
234 | unregister_dev: |
235 | device_unregister(&ep->dev); |
236 | return NULL; |
237 | |
238 | free_ep: |
239 | kfree(ep); |
240 | return NULL; |
241 | } |
242 | EXPORT_SYMBOL_GPL(iscsi_create_endpoint); |
243 | |
244 | void iscsi_destroy_endpoint(struct iscsi_endpoint *ep) |
245 | { |
246 | sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group); |
247 | device_unregister(&ep->dev); |
248 | } |
249 | EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint); |
250 | |
251 | struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) |
252 | { |
253 | struct iscsi_endpoint *ep; |
254 | struct device *dev; |
255 | |
256 | dev = class_find_device(&iscsi_endpoint_class, NULL, &handle, |
257 | iscsi_match_epid); |
258 | if (!dev) |
259 | return NULL; |
260 | |
261 | ep = iscsi_dev_to_endpoint(dev); |
262 | /* |
263 | * we can drop this now because the interface will prevent |
264 | * removals and lookups from racing. |
265 | */ |
266 | put_device(dev); |
267 | return ep; |
268 | } |
269 | EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint); |
270 | |
271 | /* |
272 | * Interface to display network param to sysfs |
273 | */ |
274 | |
275 | static void iscsi_iface_release(struct device *dev) |
276 | { |
277 | struct iscsi_iface *iface = iscsi_dev_to_iface(dev); |
278 | struct device *parent = iface->dev.parent; |
279 | |
280 | kfree(iface); |
281 | put_device(parent); |
282 | } |
283 | |
284 | |
285 | static struct class iscsi_iface_class = { |
286 | .name = "iscsi_iface", |
287 | .dev_release = iscsi_iface_release, |
288 | }; |
289 | |
290 | #define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store) \ |
291 | struct device_attribute dev_attr_##_prefix##_##_name = \ |
292 | __ATTR(_name, _mode, _show, _store) |
293 | |
294 | /* iface attrs show */ |
295 | #define iscsi_iface_attr_show(type, name, param_type, param) \ |
296 | static ssize_t \ |
297 | show_##type##_##name(struct device *dev, struct device_attribute *attr, \ |
298 | char *buf) \ |
299 | { \ |
300 | struct iscsi_iface *iface = iscsi_dev_to_iface(dev); \ |
301 | struct iscsi_transport *t = iface->transport; \ |
302 | return t->get_iface_param(iface, param_type, param, buf); \ |
303 | } \ |
304 | |
305 | #define iscsi_iface_net_attr(type, name, param) \ |
306 | iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param) \ |
307 | static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL); |
308 | |
309 | /* generic read only ipvi4 attribute */ |
310 | iscsi_iface_net_attr(ipv4_iface, ipaddress, ISCSI_NET_PARAM_IPV4_ADDR); |
311 | iscsi_iface_net_attr(ipv4_iface, gateway, ISCSI_NET_PARAM_IPV4_GW); |
312 | iscsi_iface_net_attr(ipv4_iface, subnet, ISCSI_NET_PARAM_IPV4_SUBNET); |
313 | iscsi_iface_net_attr(ipv4_iface, bootproto, ISCSI_NET_PARAM_IPV4_BOOTPROTO); |
314 | |
315 | /* generic read only ipv6 attribute */ |
316 | iscsi_iface_net_attr(ipv6_iface, ipaddress, ISCSI_NET_PARAM_IPV6_ADDR); |
317 | iscsi_iface_net_attr(ipv6_iface, link_local_addr, ISCSI_NET_PARAM_IPV6_LINKLOCAL); |
318 | iscsi_iface_net_attr(ipv6_iface, router_addr, ISCSI_NET_PARAM_IPV6_ROUTER); |
319 | iscsi_iface_net_attr(ipv6_iface, ipaddr_autocfg, |
320 | ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG); |
321 | iscsi_iface_net_attr(ipv6_iface, link_local_autocfg, |
322 | ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG); |
323 | |
324 | /* common read only iface attribute */ |
325 | iscsi_iface_net_attr(iface, enabled, ISCSI_NET_PARAM_IFACE_ENABLE); |
326 | iscsi_iface_net_attr(iface, vlan_id, ISCSI_NET_PARAM_VLAN_ID); |
327 | iscsi_iface_net_attr(iface, vlan_priority, ISCSI_NET_PARAM_VLAN_PRIORITY); |
328 | iscsi_iface_net_attr(iface, vlan_enabled, ISCSI_NET_PARAM_VLAN_ENABLED); |
329 | iscsi_iface_net_attr(iface, mtu, ISCSI_NET_PARAM_MTU); |
330 | iscsi_iface_net_attr(iface, port, ISCSI_NET_PARAM_PORT); |
331 | |
332 | static umode_t iscsi_iface_attr_is_visible(struct kobject *kobj, |
333 | struct attribute *attr, int i) |
334 | { |
335 | struct device *dev = container_of(kobj, struct device, kobj); |
336 | struct iscsi_iface *iface = iscsi_dev_to_iface(dev); |
337 | struct iscsi_transport *t = iface->transport; |
338 | int param; |
339 | |
340 | if (attr == &dev_attr_iface_enabled.attr) |
341 | param = ISCSI_NET_PARAM_IFACE_ENABLE; |
342 | else if (attr == &dev_attr_iface_vlan_id.attr) |
343 | param = ISCSI_NET_PARAM_VLAN_ID; |
344 | else if (attr == &dev_attr_iface_vlan_priority.attr) |
345 | param = ISCSI_NET_PARAM_VLAN_PRIORITY; |
346 | else if (attr == &dev_attr_iface_vlan_enabled.attr) |
347 | param = ISCSI_NET_PARAM_VLAN_ENABLED; |
348 | else if (attr == &dev_attr_iface_mtu.attr) |
349 | param = ISCSI_NET_PARAM_MTU; |
350 | else if (attr == &dev_attr_iface_port.attr) |
351 | param = ISCSI_NET_PARAM_PORT; |
352 | else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) { |
353 | if (attr == &dev_attr_ipv4_iface_ipaddress.attr) |
354 | param = ISCSI_NET_PARAM_IPV4_ADDR; |
355 | else if (attr == &dev_attr_ipv4_iface_gateway.attr) |
356 | param = ISCSI_NET_PARAM_IPV4_GW; |
357 | else if (attr == &dev_attr_ipv4_iface_subnet.attr) |
358 | param = ISCSI_NET_PARAM_IPV4_SUBNET; |
359 | else if (attr == &dev_attr_ipv4_iface_bootproto.attr) |
360 | param = ISCSI_NET_PARAM_IPV4_BOOTPROTO; |
361 | else |
362 | return 0; |
363 | } else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) { |
364 | if (attr == &dev_attr_ipv6_iface_ipaddress.attr) |
365 | param = ISCSI_NET_PARAM_IPV6_ADDR; |
366 | else if (attr == &dev_attr_ipv6_iface_link_local_addr.attr) |
367 | param = ISCSI_NET_PARAM_IPV6_LINKLOCAL; |
368 | else if (attr == &dev_attr_ipv6_iface_router_addr.attr) |
369 | param = ISCSI_NET_PARAM_IPV6_ROUTER; |
370 | else if (attr == &dev_attr_ipv6_iface_ipaddr_autocfg.attr) |
371 | param = ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG; |
372 | else if (attr == &dev_attr_ipv6_iface_link_local_autocfg.attr) |
373 | param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG; |
374 | else |
375 | return 0; |
376 | } else { |
377 | WARN_ONCE(1, "Invalid iface attr"); |
378 | return 0; |
379 | } |
380 | |
381 | return t->attr_is_visible(ISCSI_NET_PARAM, param); |
382 | } |
383 | |
384 | static struct attribute *iscsi_iface_attrs[] = { |
385 | &dev_attr_iface_enabled.attr, |
386 | &dev_attr_iface_vlan_id.attr, |
387 | &dev_attr_iface_vlan_priority.attr, |
388 | &dev_attr_iface_vlan_enabled.attr, |
389 | &dev_attr_ipv4_iface_ipaddress.attr, |
390 | &dev_attr_ipv4_iface_gateway.attr, |
391 | &dev_attr_ipv4_iface_subnet.attr, |
392 | &dev_attr_ipv4_iface_bootproto.attr, |
393 | &dev_attr_ipv6_iface_ipaddress.attr, |
394 | &dev_attr_ipv6_iface_link_local_addr.attr, |
395 | &dev_attr_ipv6_iface_router_addr.attr, |
396 | &dev_attr_ipv6_iface_ipaddr_autocfg.attr, |
397 | &dev_attr_ipv6_iface_link_local_autocfg.attr, |
398 | &dev_attr_iface_mtu.attr, |
399 | &dev_attr_iface_port.attr, |
400 | NULL, |
401 | }; |
402 | |
403 | static struct attribute_group iscsi_iface_group = { |
404 | .attrs = iscsi_iface_attrs, |
405 | .is_visible = iscsi_iface_attr_is_visible, |
406 | }; |
407 | |
408 | struct iscsi_iface * |
409 | iscsi_create_iface(struct Scsi_Host *shost, struct iscsi_transport *transport, |
410 | uint32_t iface_type, uint32_t iface_num, int dd_size) |
411 | { |
412 | struct iscsi_iface *iface; |
413 | int err; |
414 | |
415 | iface = kzalloc(sizeof(*iface) + dd_size, GFP_KERNEL); |
416 | if (!iface) |
417 | return NULL; |
418 | |
419 | iface->transport = transport; |
420 | iface->iface_type = iface_type; |
421 | iface->iface_num = iface_num; |
422 | iface->dev.release = iscsi_iface_release; |
423 | iface->dev.class = &iscsi_iface_class; |
424 | /* parent reference released in iscsi_iface_release */ |
425 | iface->dev.parent = get_device(&shost->shost_gendev); |
426 | if (iface_type == ISCSI_IFACE_TYPE_IPV4) |
427 | dev_set_name(&iface->dev, "ipv4-iface-%u-%u", shost->host_no, |
428 | iface_num); |
429 | else |
430 | dev_set_name(&iface->dev, "ipv6-iface-%u-%u", shost->host_no, |
431 | iface_num); |
432 | |
433 | err = device_register(&iface->dev); |
434 | if (err) |
435 | goto free_iface; |
436 | |
437 | err = sysfs_create_group(&iface->dev.kobj, &iscsi_iface_group); |
438 | if (err) |
439 | goto unreg_iface; |
440 | |
441 | if (dd_size) |
442 | iface->dd_data = &iface[1]; |
443 | return iface; |
444 | |
445 | unreg_iface: |
446 | device_unregister(&iface->dev); |
447 | return NULL; |
448 | |
449 | free_iface: |
450 | put_device(iface->dev.parent); |
451 | kfree(iface); |
452 | return NULL; |
453 | } |
454 | EXPORT_SYMBOL_GPL(iscsi_create_iface); |
455 | |
456 | void iscsi_destroy_iface(struct iscsi_iface *iface) |
457 | { |
458 | sysfs_remove_group(&iface->dev.kobj, &iscsi_iface_group); |
459 | device_unregister(&iface->dev); |
460 | } |
461 | EXPORT_SYMBOL_GPL(iscsi_destroy_iface); |
462 | |
463 | /* |
464 | * Interface to display flash node params to sysfs |
465 | */ |
466 | |
467 | #define ISCSI_FLASHNODE_ATTR(_prefix, _name, _mode, _show, _store) \ |
468 | struct device_attribute dev_attr_##_prefix##_##_name = \ |
469 | __ATTR(_name, _mode, _show, _store) |
470 | |
471 | /* flash node session attrs show */ |
472 | #define iscsi_flashnode_sess_attr_show(type, name, param) \ |
473 | static ssize_t \ |
474 | show_##type##_##name(struct device *dev, struct device_attribute *attr, \ |
475 | char *buf) \ |
476 | { \ |
477 | struct iscsi_bus_flash_session *fnode_sess = \ |
478 | iscsi_dev_to_flash_session(dev);\ |
479 | struct iscsi_transport *t = fnode_sess->transport; \ |
480 | return t->get_flashnode_param(fnode_sess, param, buf); \ |
481 | } \ |
482 | |
483 | |
484 | #define iscsi_flashnode_sess_attr(type, name, param) \ |
485 | iscsi_flashnode_sess_attr_show(type, name, param) \ |
486 | static ISCSI_FLASHNODE_ATTR(type, name, S_IRUGO, \ |
487 | show_##type##_##name, NULL); |
488 | |
489 | /* Flash node session attributes */ |
490 | |
491 | iscsi_flashnode_sess_attr(fnode, auto_snd_tgt_disable, |
492 | ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE); |
493 | iscsi_flashnode_sess_attr(fnode, discovery_session, |
494 | ISCSI_FLASHNODE_DISCOVERY_SESS); |
495 | iscsi_flashnode_sess_attr(fnode, portal_type, ISCSI_FLASHNODE_PORTAL_TYPE); |
496 | iscsi_flashnode_sess_attr(fnode, entry_enable, ISCSI_FLASHNODE_ENTRY_EN); |
497 | iscsi_flashnode_sess_attr(fnode, immediate_data, ISCSI_FLASHNODE_IMM_DATA_EN); |
498 | iscsi_flashnode_sess_attr(fnode, initial_r2t, ISCSI_FLASHNODE_INITIAL_R2T_EN); |
499 | iscsi_flashnode_sess_attr(fnode, data_seq_in_order, |
500 | ISCSI_FLASHNODE_DATASEQ_INORDER); |
501 | iscsi_flashnode_sess_attr(fnode, data_pdu_in_order, |
502 | ISCSI_FLASHNODE_PDU_INORDER); |
503 | iscsi_flashnode_sess_attr(fnode, chap_auth, ISCSI_FLASHNODE_CHAP_AUTH_EN); |
504 | iscsi_flashnode_sess_attr(fnode, discovery_logout, |
505 | ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN); |
506 | iscsi_flashnode_sess_attr(fnode, bidi_chap, ISCSI_FLASHNODE_BIDI_CHAP_EN); |
507 | iscsi_flashnode_sess_attr(fnode, discovery_auth_optional, |
508 | ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL); |
509 | iscsi_flashnode_sess_attr(fnode, erl, ISCSI_FLASHNODE_ERL); |
510 | iscsi_flashnode_sess_attr(fnode, first_burst_len, ISCSI_FLASHNODE_FIRST_BURST); |
511 | iscsi_flashnode_sess_attr(fnode, def_time2wait, ISCSI_FLASHNODE_DEF_TIME2WAIT); |
512 | iscsi_flashnode_sess_attr(fnode, def_time2retain, |
513 | ISCSI_FLASHNODE_DEF_TIME2RETAIN); |
514 | iscsi_flashnode_sess_attr(fnode, max_outstanding_r2t, ISCSI_FLASHNODE_MAX_R2T); |
515 | iscsi_flashnode_sess_attr(fnode, isid, ISCSI_FLASHNODE_ISID); |
516 | iscsi_flashnode_sess_attr(fnode, tsid, ISCSI_FLASHNODE_TSID); |
517 | iscsi_flashnode_sess_attr(fnode, max_burst_len, ISCSI_FLASHNODE_MAX_BURST); |
518 | iscsi_flashnode_sess_attr(fnode, def_taskmgmt_tmo, |
519 | ISCSI_FLASHNODE_DEF_TASKMGMT_TMO); |
520 | iscsi_flashnode_sess_attr(fnode, targetalias, ISCSI_FLASHNODE_ALIAS); |
521 | iscsi_flashnode_sess_attr(fnode, targetname, ISCSI_FLASHNODE_NAME); |
522 | iscsi_flashnode_sess_attr(fnode, tpgt, ISCSI_FLASHNODE_TPGT); |
523 | iscsi_flashnode_sess_attr(fnode, discovery_parent_idx, |
524 | ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX); |
525 | iscsi_flashnode_sess_attr(fnode, discovery_parent_type, |
526 | ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE); |
527 | iscsi_flashnode_sess_attr(fnode, chap_in_idx, ISCSI_FLASHNODE_CHAP_IN_IDX); |
528 | iscsi_flashnode_sess_attr(fnode, chap_out_idx, ISCSI_FLASHNODE_CHAP_OUT_IDX); |
529 | iscsi_flashnode_sess_attr(fnode, username, ISCSI_FLASHNODE_USERNAME); |
530 | iscsi_flashnode_sess_attr(fnode, username_in, ISCSI_FLASHNODE_USERNAME_IN); |
531 | iscsi_flashnode_sess_attr(fnode, password, ISCSI_FLASHNODE_PASSWORD); |
532 | iscsi_flashnode_sess_attr(fnode, password_in, ISCSI_FLASHNODE_PASSWORD_IN); |
533 | iscsi_flashnode_sess_attr(fnode, is_boot_target, ISCSI_FLASHNODE_IS_BOOT_TGT); |
534 | |
535 | static struct attribute *iscsi_flashnode_sess_attrs[] = { |
536 | &dev_attr_fnode_auto_snd_tgt_disable.attr, |
537 | &dev_attr_fnode_discovery_session.attr, |
538 | &dev_attr_fnode_portal_type.attr, |
539 | &dev_attr_fnode_entry_enable.attr, |
540 | &dev_attr_fnode_immediate_data.attr, |
541 | &dev_attr_fnode_initial_r2t.attr, |
542 | &dev_attr_fnode_data_seq_in_order.attr, |
543 | &dev_attr_fnode_data_pdu_in_order.attr, |
544 | &dev_attr_fnode_chap_auth.attr, |
545 | &dev_attr_fnode_discovery_logout.attr, |
546 | &dev_attr_fnode_bidi_chap.attr, |
547 | &dev_attr_fnode_discovery_auth_optional.attr, |
548 | &dev_attr_fnode_erl.attr, |
549 | &dev_attr_fnode_first_burst_len.attr, |
550 | &dev_attr_fnode_def_time2wait.attr, |
551 | &dev_attr_fnode_def_time2retain.attr, |
552 | &dev_attr_fnode_max_outstanding_r2t.attr, |
553 | &dev_attr_fnode_isid.attr, |
554 | &dev_attr_fnode_tsid.attr, |
555 | &dev_attr_fnode_max_burst_len.attr, |
556 | &dev_attr_fnode_def_taskmgmt_tmo.attr, |
557 | &dev_attr_fnode_targetalias.attr, |
558 | &dev_attr_fnode_targetname.attr, |
559 | &dev_attr_fnode_tpgt.attr, |
560 | &dev_attr_fnode_discovery_parent_idx.attr, |
561 | &dev_attr_fnode_discovery_parent_type.attr, |
562 | &dev_attr_fnode_chap_in_idx.attr, |
563 | &dev_attr_fnode_chap_out_idx.attr, |
564 | &dev_attr_fnode_username.attr, |
565 | &dev_attr_fnode_username_in.attr, |
566 | &dev_attr_fnode_password.attr, |
567 | &dev_attr_fnode_password_in.attr, |
568 | &dev_attr_fnode_is_boot_target.attr, |
569 | NULL, |
570 | }; |
571 | |
572 | static umode_t iscsi_flashnode_sess_attr_is_visible(struct kobject *kobj, |
573 | struct attribute *attr, |
574 | int i) |
575 | { |
576 | struct device *dev = container_of(kobj, struct device, kobj); |
577 | struct iscsi_bus_flash_session *fnode_sess = |
578 | iscsi_dev_to_flash_session(dev); |
579 | struct iscsi_transport *t = fnode_sess->transport; |
580 | int param; |
581 | |
582 | if (attr == &dev_attr_fnode_auto_snd_tgt_disable.attr) { |
583 | param = ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE; |
584 | } else if (attr == &dev_attr_fnode_discovery_session.attr) { |
585 | param = ISCSI_FLASHNODE_DISCOVERY_SESS; |
586 | } else if (attr == &dev_attr_fnode_portal_type.attr) { |
587 | param = ISCSI_FLASHNODE_PORTAL_TYPE; |
588 | } else if (attr == &dev_attr_fnode_entry_enable.attr) { |
589 | param = ISCSI_FLASHNODE_ENTRY_EN; |
590 | } else if (attr == &dev_attr_fnode_immediate_data.attr) { |
591 | param = ISCSI_FLASHNODE_IMM_DATA_EN; |
592 | } else if (attr == &dev_attr_fnode_initial_r2t.attr) { |
593 | param = ISCSI_FLASHNODE_INITIAL_R2T_EN; |
594 | } else if (attr == &dev_attr_fnode_data_seq_in_order.attr) { |
595 | param = ISCSI_FLASHNODE_DATASEQ_INORDER; |
596 | } else if (attr == &dev_attr_fnode_data_pdu_in_order.attr) { |
597 | param = ISCSI_FLASHNODE_PDU_INORDER; |
598 | } else if (attr == &dev_attr_fnode_chap_auth.attr) { |
599 | param = ISCSI_FLASHNODE_CHAP_AUTH_EN; |
600 | } else if (attr == &dev_attr_fnode_discovery_logout.attr) { |
601 | param = ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN; |
602 | } else if (attr == &dev_attr_fnode_bidi_chap.attr) { |
603 | param = ISCSI_FLASHNODE_BIDI_CHAP_EN; |
604 | } else if (attr == &dev_attr_fnode_discovery_auth_optional.attr) { |
605 | param = ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL; |
606 | } else if (attr == &dev_attr_fnode_erl.attr) { |
607 | param = ISCSI_FLASHNODE_ERL; |
608 | } else if (attr == &dev_attr_fnode_first_burst_len.attr) { |
609 | param = ISCSI_FLASHNODE_FIRST_BURST; |
610 | } else if (attr == &dev_attr_fnode_def_time2wait.attr) { |
611 | param = ISCSI_FLASHNODE_DEF_TIME2WAIT; |
612 | } else if (attr == &dev_attr_fnode_def_time2retain.attr) { |
613 | param = ISCSI_FLASHNODE_DEF_TIME2RETAIN; |
614 | } else if (attr == &dev_attr_fnode_max_outstanding_r2t.attr) { |
615 | param = ISCSI_FLASHNODE_MAX_R2T; |
616 | } else if (attr == &dev_attr_fnode_isid.attr) { |
617 | param = ISCSI_FLASHNODE_ISID; |
618 | } else if (attr == &dev_attr_fnode_tsid.attr) { |
619 | param = ISCSI_FLASHNODE_TSID; |
620 | } else if (attr == &dev_attr_fnode_max_burst_len.attr) { |
621 | param = ISCSI_FLASHNODE_MAX_BURST; |
622 | } else if (attr == &dev_attr_fnode_def_taskmgmt_tmo.attr) { |
623 | param = ISCSI_FLASHNODE_DEF_TASKMGMT_TMO; |
624 | } else if (attr == &dev_attr_fnode_targetalias.attr) { |
625 | param = ISCSI_FLASHNODE_ALIAS; |
626 | } else if (attr == &dev_attr_fnode_targetname.attr) { |
627 | param = ISCSI_FLASHNODE_NAME; |
628 | } else if (attr == &dev_attr_fnode_tpgt.attr) { |
629 | param = ISCSI_FLASHNODE_TPGT; |
630 | } else if (attr == &dev_attr_fnode_discovery_parent_idx.attr) { |
631 | param = ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX; |
632 | } else if (attr == &dev_attr_fnode_discovery_parent_type.attr) { |
633 | param = ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE; |
634 | } else if (attr == &dev_attr_fnode_chap_in_idx.attr) { |
635 | param = ISCSI_FLASHNODE_CHAP_IN_IDX; |
636 | } else if (attr == &dev_attr_fnode_chap_out_idx.attr) { |
637 | param = ISCSI_FLASHNODE_CHAP_OUT_IDX; |
638 | } else if (attr == &dev_attr_fnode_username.attr) { |
639 | param = ISCSI_FLASHNODE_USERNAME; |
640 | } else if (attr == &dev_attr_fnode_username_in.attr) { |
641 | param = ISCSI_FLASHNODE_USERNAME_IN; |
642 | } else if (attr == &dev_attr_fnode_password.attr) { |
643 | param = ISCSI_FLASHNODE_PASSWORD; |
644 | } else if (attr == &dev_attr_fnode_password_in.attr) { |
645 | param = ISCSI_FLASHNODE_PASSWORD_IN; |
646 | } else if (attr == &dev_attr_fnode_is_boot_target.attr) { |
647 | param = ISCSI_FLASHNODE_IS_BOOT_TGT; |
648 | } else { |
649 | WARN_ONCE(1, "Invalid flashnode session attr"); |
650 | return 0; |
651 | } |
652 | |
653 | return t->attr_is_visible(ISCSI_FLASHNODE_PARAM, param); |
654 | } |
655 | |
656 | static struct attribute_group iscsi_flashnode_sess_attr_group = { |
657 | .attrs = iscsi_flashnode_sess_attrs, |
658 | .is_visible = iscsi_flashnode_sess_attr_is_visible, |
659 | }; |
660 | |
661 | static const struct attribute_group *iscsi_flashnode_sess_attr_groups[] = { |
662 | &iscsi_flashnode_sess_attr_group, |
663 | NULL, |
664 | }; |
665 | |
666 | static void iscsi_flashnode_sess_release(struct device *dev) |
667 | { |
668 | struct iscsi_bus_flash_session *fnode_sess = |
669 | iscsi_dev_to_flash_session(dev); |
670 | |
671 | kfree(fnode_sess->targetname); |
672 | kfree(fnode_sess->targetalias); |
673 | kfree(fnode_sess->portal_type); |
674 | kfree(fnode_sess); |
675 | } |
676 | |
677 | struct device_type iscsi_flashnode_sess_dev_type = { |
678 | .name = "iscsi_flashnode_sess_dev_type", |
679 | .groups = iscsi_flashnode_sess_attr_groups, |
680 | .release = iscsi_flashnode_sess_release, |
681 | }; |
682 | |
683 | /* flash node connection attrs show */ |
684 | #define iscsi_flashnode_conn_attr_show(type, name, param) \ |
685 | static ssize_t \ |
686 | show_##type##_##name(struct device *dev, struct device_attribute *attr, \ |
687 | char *buf) \ |
688 | { \ |
689 | struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev);\ |
690 | struct iscsi_bus_flash_session *fnode_sess = \ |
691 | iscsi_flash_conn_to_flash_session(fnode_conn);\ |
692 | struct iscsi_transport *t = fnode_conn->transport; \ |
693 | return t->get_flashnode_param(fnode_sess, param, buf); \ |
694 | } \ |
695 | |
696 | |
697 | #define iscsi_flashnode_conn_attr(type, name, param) \ |
698 | iscsi_flashnode_conn_attr_show(type, name, param) \ |
699 | static ISCSI_FLASHNODE_ATTR(type, name, S_IRUGO, \ |
700 | show_##type##_##name, NULL); |
701 | |
702 | /* Flash node connection attributes */ |
703 | |
704 | iscsi_flashnode_conn_attr(fnode, is_fw_assigned_ipv6, |
705 | ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6); |
706 | iscsi_flashnode_conn_attr(fnode, header_digest, ISCSI_FLASHNODE_HDR_DGST_EN); |
707 | iscsi_flashnode_conn_attr(fnode, data_digest, ISCSI_FLASHNODE_DATA_DGST_EN); |
708 | iscsi_flashnode_conn_attr(fnode, snack_req, ISCSI_FLASHNODE_SNACK_REQ_EN); |
709 | iscsi_flashnode_conn_attr(fnode, tcp_timestamp_stat, |
710 | ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT); |
711 | iscsi_flashnode_conn_attr(fnode, tcp_nagle_disable, |
712 | ISCSI_FLASHNODE_TCP_NAGLE_DISABLE); |
713 | iscsi_flashnode_conn_attr(fnode, tcp_wsf_disable, |
714 | ISCSI_FLASHNODE_TCP_WSF_DISABLE); |
715 | iscsi_flashnode_conn_attr(fnode, tcp_timer_scale, |
716 | ISCSI_FLASHNODE_TCP_TIMER_SCALE); |
717 | iscsi_flashnode_conn_attr(fnode, tcp_timestamp_enable, |
718 | ISCSI_FLASHNODE_TCP_TIMESTAMP_EN); |
719 | iscsi_flashnode_conn_attr(fnode, fragment_disable, |
720 | ISCSI_FLASHNODE_IP_FRAG_DISABLE); |
721 | iscsi_flashnode_conn_attr(fnode, keepalive_tmo, ISCSI_FLASHNODE_KEEPALIVE_TMO); |
722 | iscsi_flashnode_conn_attr(fnode, port, ISCSI_FLASHNODE_PORT); |
723 | iscsi_flashnode_conn_attr(fnode, ipaddress, ISCSI_FLASHNODE_IPADDR); |
724 | iscsi_flashnode_conn_attr(fnode, max_recv_dlength, |
725 | ISCSI_FLASHNODE_MAX_RECV_DLENGTH); |
726 | iscsi_flashnode_conn_attr(fnode, max_xmit_dlength, |
727 | ISCSI_FLASHNODE_MAX_XMIT_DLENGTH); |
728 | iscsi_flashnode_conn_attr(fnode, local_port, ISCSI_FLASHNODE_LOCAL_PORT); |
729 | iscsi_flashnode_conn_attr(fnode, ipv4_tos, ISCSI_FLASHNODE_IPV4_TOS); |
730 | iscsi_flashnode_conn_attr(fnode, ipv6_traffic_class, ISCSI_FLASHNODE_IPV6_TC); |
731 | iscsi_flashnode_conn_attr(fnode, ipv6_flow_label, |
732 | ISCSI_FLASHNODE_IPV6_FLOW_LABEL); |
733 | iscsi_flashnode_conn_attr(fnode, redirect_ipaddr, |
734 | ISCSI_FLASHNODE_REDIRECT_IPADDR); |
735 | iscsi_flashnode_conn_attr(fnode, max_segment_size, |
736 | ISCSI_FLASHNODE_MAX_SEGMENT_SIZE); |
737 | iscsi_flashnode_conn_attr(fnode, link_local_ipv6, |
738 | ISCSI_FLASHNODE_LINK_LOCAL_IPV6); |
739 | iscsi_flashnode_conn_attr(fnode, tcp_xmit_wsf, ISCSI_FLASHNODE_TCP_XMIT_WSF); |
740 | iscsi_flashnode_conn_attr(fnode, tcp_recv_wsf, ISCSI_FLASHNODE_TCP_RECV_WSF); |
741 | iscsi_flashnode_conn_attr(fnode, statsn, ISCSI_FLASHNODE_STATSN); |
742 | iscsi_flashnode_conn_attr(fnode, exp_statsn, ISCSI_FLASHNODE_EXP_STATSN); |
743 | |
744 | static struct attribute *iscsi_flashnode_conn_attrs[] = { |
745 | &dev_attr_fnode_is_fw_assigned_ipv6.attr, |
746 | &dev_attr_fnode_header_digest.attr, |
747 | &dev_attr_fnode_data_digest.attr, |
748 | &dev_attr_fnode_snack_req.attr, |
749 | &dev_attr_fnode_tcp_timestamp_stat.attr, |
750 | &dev_attr_fnode_tcp_nagle_disable.attr, |
751 | &dev_attr_fnode_tcp_wsf_disable.attr, |
752 | &dev_attr_fnode_tcp_timer_scale.attr, |
753 | &dev_attr_fnode_tcp_timestamp_enable.attr, |
754 | &dev_attr_fnode_fragment_disable.attr, |
755 | &dev_attr_fnode_max_recv_dlength.attr, |
756 | &dev_attr_fnode_max_xmit_dlength.attr, |
757 | &dev_attr_fnode_keepalive_tmo.attr, |
758 | &dev_attr_fnode_port.attr, |
759 | &dev_attr_fnode_ipaddress.attr, |
760 | &dev_attr_fnode_redirect_ipaddr.attr, |
761 | &dev_attr_fnode_max_segment_size.attr, |
762 | &dev_attr_fnode_local_port.attr, |
763 | &dev_attr_fnode_ipv4_tos.attr, |
764 | &dev_attr_fnode_ipv6_traffic_class.attr, |
765 | &dev_attr_fnode_ipv6_flow_label.attr, |
766 | &dev_attr_fnode_link_local_ipv6.attr, |
767 | &dev_attr_fnode_tcp_xmit_wsf.attr, |
768 | &dev_attr_fnode_tcp_recv_wsf.attr, |
769 | &dev_attr_fnode_statsn.attr, |
770 | &dev_attr_fnode_exp_statsn.attr, |
771 | NULL, |
772 | }; |
773 | |
774 | static umode_t iscsi_flashnode_conn_attr_is_visible(struct kobject *kobj, |
775 | struct attribute *attr, |
776 | int i) |
777 | { |
778 | struct device *dev = container_of(kobj, struct device, kobj); |
779 | struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev); |
780 | struct iscsi_transport *t = fnode_conn->transport; |
781 | int param; |
782 | |
783 | if (attr == &dev_attr_fnode_is_fw_assigned_ipv6.attr) { |
784 | param = ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6; |
785 | } else if (attr == &dev_attr_fnode_header_digest.attr) { |
786 | param = ISCSI_FLASHNODE_HDR_DGST_EN; |
787 | } else if (attr == &dev_attr_fnode_data_digest.attr) { |
788 | param = ISCSI_FLASHNODE_DATA_DGST_EN; |
789 | } else if (attr == &dev_attr_fnode_snack_req.attr) { |
790 | param = ISCSI_FLASHNODE_SNACK_REQ_EN; |
791 | } else if (attr == &dev_attr_fnode_tcp_timestamp_stat.attr) { |
792 | param = ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT; |
793 | } else if (attr == &dev_attr_fnode_tcp_nagle_disable.attr) { |
794 | param = ISCSI_FLASHNODE_TCP_NAGLE_DISABLE; |
795 | } else if (attr == &dev_attr_fnode_tcp_wsf_disable.attr) { |
796 | param = ISCSI_FLASHNODE_TCP_WSF_DISABLE; |
797 | } else if (attr == &dev_attr_fnode_tcp_timer_scale.attr) { |
798 | param = ISCSI_FLASHNODE_TCP_TIMER_SCALE; |
799 | } else if (attr == &dev_attr_fnode_tcp_timestamp_enable.attr) { |
800 | param = ISCSI_FLASHNODE_TCP_TIMESTAMP_EN; |
801 | } else if (attr == &dev_attr_fnode_fragment_disable.attr) { |
802 | param = ISCSI_FLASHNODE_IP_FRAG_DISABLE; |
803 | } else if (attr == &dev_attr_fnode_max_recv_dlength.attr) { |
804 | param = ISCSI_FLASHNODE_MAX_RECV_DLENGTH; |
805 | } else if (attr == &dev_attr_fnode_max_xmit_dlength.attr) { |
806 | param = ISCSI_FLASHNODE_MAX_XMIT_DLENGTH; |
807 | } else if (attr == &dev_attr_fnode_keepalive_tmo.attr) { |
808 | param = ISCSI_FLASHNODE_KEEPALIVE_TMO; |
809 | } else if (attr == &dev_attr_fnode_port.attr) { |
810 | param = ISCSI_FLASHNODE_PORT; |
811 | } else if (attr == &dev_attr_fnode_ipaddress.attr) { |
812 | param = ISCSI_FLASHNODE_IPADDR; |
813 | } else if (attr == &dev_attr_fnode_redirect_ipaddr.attr) { |
814 | param = ISCSI_FLASHNODE_REDIRECT_IPADDR; |
815 | } else if (attr == &dev_attr_fnode_max_segment_size.attr) { |
816 | param = ISCSI_FLASHNODE_MAX_SEGMENT_SIZE; |
817 | } else if (attr == &dev_attr_fnode_local_port.attr) { |
818 | param = ISCSI_FLASHNODE_LOCAL_PORT; |
819 | } else if (attr == &dev_attr_fnode_ipv4_tos.attr) { |
820 | param = ISCSI_FLASHNODE_IPV4_TOS; |
821 | } else if (attr == &dev_attr_fnode_ipv6_traffic_class.attr) { |
822 | param = ISCSI_FLASHNODE_IPV6_TC; |
823 | } else if (attr == &dev_attr_fnode_ipv6_flow_label.attr) { |
824 | param = ISCSI_FLASHNODE_IPV6_FLOW_LABEL; |
825 | } else if (attr == &dev_attr_fnode_link_local_ipv6.attr) { |
826 | param = ISCSI_FLASHNODE_LINK_LOCAL_IPV6; |
827 | } else if (attr == &dev_attr_fnode_tcp_xmit_wsf.attr) { |
828 | param = ISCSI_FLASHNODE_TCP_XMIT_WSF; |
829 | } else if (attr == &dev_attr_fnode_tcp_recv_wsf.attr) { |
830 | param = ISCSI_FLASHNODE_TCP_RECV_WSF; |
831 | } else if (attr == &dev_attr_fnode_statsn.attr) { |
832 | param = ISCSI_FLASHNODE_STATSN; |
833 | } else if (attr == &dev_attr_fnode_exp_statsn.attr) { |
834 | param = ISCSI_FLASHNODE_EXP_STATSN; |
835 | } else { |
836 | WARN_ONCE(1, "Invalid flashnode connection attr"); |
837 | return 0; |
838 | } |
839 | |
840 | return t->attr_is_visible(ISCSI_FLASHNODE_PARAM, param); |
841 | } |
842 | |
843 | static struct attribute_group iscsi_flashnode_conn_attr_group = { |
844 | .attrs = iscsi_flashnode_conn_attrs, |
845 | .is_visible = iscsi_flashnode_conn_attr_is_visible, |
846 | }; |
847 | |
848 | static const struct attribute_group *iscsi_flashnode_conn_attr_groups[] = { |
849 | &iscsi_flashnode_conn_attr_group, |
850 | NULL, |
851 | }; |
852 | |
853 | static void iscsi_flashnode_conn_release(struct device *dev) |
854 | { |
855 | struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev); |
856 | |
857 | kfree(fnode_conn->ipaddress); |
858 | kfree(fnode_conn->redirect_ipaddr); |
859 | kfree(fnode_conn->link_local_ipv6_addr); |
860 | kfree(fnode_conn); |
861 | } |
862 | |
863 | struct device_type iscsi_flashnode_conn_dev_type = { |
864 | .name = "iscsi_flashnode_conn_dev_type", |
865 | .groups = iscsi_flashnode_conn_attr_groups, |
866 | .release = iscsi_flashnode_conn_release, |
867 | }; |
868 | |
869 | struct bus_type iscsi_flashnode_bus; |
870 | |
871 | int iscsi_flashnode_bus_match(struct device *dev, |
872 | struct device_driver *drv) |
873 | { |
874 | if (dev->bus == &iscsi_flashnode_bus) |
875 | return 1; |
876 | return 0; |
877 | } |
878 | EXPORT_SYMBOL_GPL(iscsi_flashnode_bus_match); |
879 | |
880 | struct bus_type iscsi_flashnode_bus = { |
881 | .name = "iscsi_flashnode", |
882 | .match = &iscsi_flashnode_bus_match, |
883 | }; |
884 | |
885 | /** |
886 | * iscsi_create_flashnode_sess - Add flashnode session entry in sysfs |
887 | * @shost: pointer to host data |
888 | * @index: index of flashnode to add in sysfs |
889 | * @transport: pointer to transport data |
890 | * @dd_size: total size to allocate |
891 | * |
892 | * Adds a sysfs entry for the flashnode session attributes |
893 | * |
894 | * Returns: |
895 | * pointer to allocated flashnode sess on sucess |
896 | * %NULL on failure |
897 | */ |
898 | struct iscsi_bus_flash_session * |
899 | iscsi_create_flashnode_sess(struct Scsi_Host *shost, int index, |
900 | struct iscsi_transport *transport, |
901 | int dd_size) |
902 | { |
903 | struct iscsi_bus_flash_session *fnode_sess; |
904 | int err; |
905 | |
906 | fnode_sess = kzalloc(sizeof(*fnode_sess) + dd_size, GFP_KERNEL); |
907 | if (!fnode_sess) |
908 | return NULL; |
909 | |
910 | fnode_sess->transport = transport; |
911 | fnode_sess->target_id = index; |
912 | fnode_sess->dev.type = &iscsi_flashnode_sess_dev_type; |
913 | fnode_sess->dev.bus = &iscsi_flashnode_bus; |
914 | fnode_sess->dev.parent = &shost->shost_gendev; |
915 | dev_set_name(&fnode_sess->dev, "flashnode_sess-%u:%u", |
916 | shost->host_no, index); |
917 | |
918 | err = device_register(&fnode_sess->dev); |
919 | if (err) |
920 | goto free_fnode_sess; |
921 | |
922 | if (dd_size) |
923 | fnode_sess->dd_data = &fnode_sess[1]; |
924 | |
925 | return fnode_sess; |
926 | |
927 | free_fnode_sess: |
928 | kfree(fnode_sess); |
929 | return NULL; |
930 | } |
931 | EXPORT_SYMBOL_GPL(iscsi_create_flashnode_sess); |
932 | |
933 | /** |
934 | * iscsi_create_flashnode_conn - Add flashnode conn entry in sysfs |
935 | * @shost: pointer to host data |
936 | * @fnode_sess: pointer to the parent flashnode session entry |
937 | * @transport: pointer to transport data |
938 | * @dd_size: total size to allocate |
939 | * |
940 | * Adds a sysfs entry for the flashnode connection attributes |
941 | * |
942 | * Returns: |
943 | * pointer to allocated flashnode conn on success |
944 | * %NULL on failure |
945 | */ |
946 | struct iscsi_bus_flash_conn * |
947 | iscsi_create_flashnode_conn(struct Scsi_Host *shost, |
948 | struct iscsi_bus_flash_session *fnode_sess, |
949 | struct iscsi_transport *transport, |
950 | int dd_size) |
951 | { |
952 | struct iscsi_bus_flash_conn *fnode_conn; |
953 | int err; |
954 | |
955 | fnode_conn = kzalloc(sizeof(*fnode_conn) + dd_size, GFP_KERNEL); |
956 | if (!fnode_conn) |
957 | return NULL; |
958 | |
959 | fnode_conn->transport = transport; |
960 | fnode_conn->dev.type = &iscsi_flashnode_conn_dev_type; |
961 | fnode_conn->dev.bus = &iscsi_flashnode_bus; |
962 | fnode_conn->dev.parent = &fnode_sess->dev; |
963 | dev_set_name(&fnode_conn->dev, "flashnode_conn-%u:%u:0", |
964 | shost->host_no, fnode_sess->target_id); |
965 | |
966 | err = device_register(&fnode_conn->dev); |
967 | if (err) |
968 | goto free_fnode_conn; |
969 | |
970 | if (dd_size) |
971 | fnode_conn->dd_data = &fnode_conn[1]; |
972 | |
973 | return fnode_conn; |
974 | |
975 | free_fnode_conn: |
976 | kfree(fnode_conn); |
977 | return NULL; |
978 | } |
979 | EXPORT_SYMBOL_GPL(iscsi_create_flashnode_conn); |
980 | |
981 | /** |
982 | * iscsi_is_flashnode_conn_dev - verify passed device is to be flashnode conn |
983 | * @dev: device to verify |
984 | * @data: pointer to data containing value to use for verification |
985 | * |
986 | * Verifies if the passed device is flashnode conn device |
987 | * |
988 | * Returns: |
989 | * 1 on success |
990 | * 0 on failure |
991 | */ |
992 | int iscsi_is_flashnode_conn_dev(struct device *dev, void *data) |
993 | { |
994 | return dev->bus == &iscsi_flashnode_bus; |
995 | } |
996 | EXPORT_SYMBOL_GPL(iscsi_is_flashnode_conn_dev); |
997 | |
998 | static int iscsi_destroy_flashnode_conn(struct iscsi_bus_flash_conn *fnode_conn) |
999 | { |
1000 | device_unregister(&fnode_conn->dev); |
1001 | return 0; |
1002 | } |
1003 | |
1004 | static int flashnode_match_index(struct device *dev, void *data) |
1005 | { |
1006 | struct iscsi_bus_flash_session *fnode_sess = NULL; |
1007 | int ret = 0; |
1008 | |
1009 | if (!iscsi_flashnode_bus_match(dev, NULL)) |
1010 | goto exit_match_index; |
1011 | |
1012 | fnode_sess = iscsi_dev_to_flash_session(dev); |
1013 | ret = (fnode_sess->target_id == *((int *)data)) ? 1 : 0; |
1014 | |
1015 | exit_match_index: |
1016 | return ret; |
1017 | } |
1018 | |
1019 | /** |
1020 | * iscsi_get_flashnode_by_index -finds flashnode session entry by index |
1021 | * @shost: pointer to host data |
1022 | * @idx: index to match |
1023 | * |
1024 | * Finds the flashnode session object for the passed index |
1025 | * |
1026 | * Returns: |
1027 | * pointer to found flashnode session object on success |
1028 | * %NULL on failure |
1029 | */ |
1030 | static struct iscsi_bus_flash_session * |
1031 | iscsi_get_flashnode_by_index(struct Scsi_Host *shost, uint32_t idx) |
1032 | { |
1033 | struct iscsi_bus_flash_session *fnode_sess = NULL; |
1034 | struct device *dev; |
1035 | |
1036 | dev = device_find_child(&shost->shost_gendev, &idx, |
1037 | flashnode_match_index); |
1038 | if (dev) |
1039 | fnode_sess = iscsi_dev_to_flash_session(dev); |
1040 | |
1041 | return fnode_sess; |
1042 | } |
1043 | |
1044 | /** |
1045 | * iscsi_find_flashnode_sess - finds flashnode session entry |
1046 | * @shost: pointer to host data |
1047 | * @data: pointer to data containing value to use for comparison |
1048 | * @fn: function pointer that does actual comparison |
1049 | * |
1050 | * Finds the flashnode session object comparing the data passed using logic |
1051 | * defined in passed function pointer |
1052 | * |
1053 | * Returns: |
1054 | * pointer to found flashnode session device object on success |
1055 | * %NULL on failure |
1056 | */ |
1057 | struct device * |
1058 | iscsi_find_flashnode_sess(struct Scsi_Host *shost, void *data, |
1059 | int (*fn)(struct device *dev, void *data)) |
1060 | { |
1061 | return device_find_child(&shost->shost_gendev, data, fn); |
1062 | } |
1063 | EXPORT_SYMBOL_GPL(iscsi_find_flashnode_sess); |
1064 | |
1065 | /** |
1066 | * iscsi_find_flashnode_conn - finds flashnode connection entry |
1067 | * @fnode_sess: pointer to parent flashnode session entry |
1068 | * |
1069 | * Finds the flashnode connection object comparing the data passed using logic |
1070 | * defined in passed function pointer |
1071 | * |
1072 | * Returns: |
1073 | * pointer to found flashnode connection device object on success |
1074 | * %NULL on failure |
1075 | */ |
1076 | struct device * |
1077 | iscsi_find_flashnode_conn(struct iscsi_bus_flash_session *fnode_sess) |
1078 | { |
1079 | return device_find_child(&fnode_sess->dev, NULL, |
1080 | iscsi_is_flashnode_conn_dev); |
1081 | } |
1082 | EXPORT_SYMBOL_GPL(iscsi_find_flashnode_conn); |
1083 | |
1084 | static int iscsi_iter_destroy_flashnode_conn_fn(struct device *dev, void *data) |
1085 | { |
1086 | if (!iscsi_is_flashnode_conn_dev(dev, NULL)) |
1087 | return 0; |
1088 | |
1089 | return iscsi_destroy_flashnode_conn(iscsi_dev_to_flash_conn(dev)); |
1090 | } |
1091 | |
1092 | /** |
1093 | * iscsi_destroy_flashnode_sess - destory flashnode session entry |
1094 | * @fnode_sess: pointer to flashnode session entry to be destroyed |
1095 | * |
1096 | * Deletes the flashnode session entry and all children flashnode connection |
1097 | * entries from sysfs |
1098 | */ |
1099 | void iscsi_destroy_flashnode_sess(struct iscsi_bus_flash_session *fnode_sess) |
1100 | { |
1101 | int err; |
1102 | |
1103 | err = device_for_each_child(&fnode_sess->dev, NULL, |
1104 | iscsi_iter_destroy_flashnode_conn_fn); |
1105 | if (err) |
1106 | pr_err("Could not delete all connections for %s. Error %d.\n", |
1107 | fnode_sess->dev.kobj.name, err); |
1108 | |
1109 | device_unregister(&fnode_sess->dev); |
1110 | } |
1111 | EXPORT_SYMBOL_GPL(iscsi_destroy_flashnode_sess); |
1112 | |
1113 | static int iscsi_iter_destroy_flashnode_fn(struct device *dev, void *data) |
1114 | { |
1115 | if (!iscsi_flashnode_bus_match(dev, NULL)) |
1116 | return 0; |
1117 | |
1118 | iscsi_destroy_flashnode_sess(iscsi_dev_to_flash_session(dev)); |
1119 | return 0; |
1120 | } |
1121 | |
1122 | /** |
1123 | * iscsi_destroy_all_flashnode - destory all flashnode session entries |
1124 | * @shost: pointer to host data |
1125 | * |
1126 | * Destroys all the flashnode session entries and all corresponding children |
1127 | * flashnode connection entries from sysfs |
1128 | */ |
1129 | void iscsi_destroy_all_flashnode(struct Scsi_Host *shost) |
1130 | { |
1131 | device_for_each_child(&shost->shost_gendev, NULL, |
1132 | iscsi_iter_destroy_flashnode_fn); |
1133 | } |
1134 | EXPORT_SYMBOL_GPL(iscsi_destroy_all_flashnode); |
1135 | |
1136 | /* |
1137 | * BSG support |
1138 | */ |
1139 | /** |
1140 | * iscsi_bsg_host_dispatch - Dispatch command to LLD. |
1141 | * @job: bsg job to be processed |
1142 | */ |
1143 | static int iscsi_bsg_host_dispatch(struct bsg_job *job) |
1144 | { |
1145 | struct Scsi_Host *shost = iscsi_job_to_shost(job); |
1146 | struct iscsi_bsg_request *req = job->request; |
1147 | struct iscsi_bsg_reply *reply = job->reply; |
1148 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); |
1149 | int cmdlen = sizeof(uint32_t); /* start with length of msgcode */ |
1150 | int ret; |
1151 | |
1152 | /* check if we have the msgcode value at least */ |
1153 | if (job->request_len < sizeof(uint32_t)) { |
1154 | ret = -ENOMSG; |
1155 | goto fail_host_msg; |
1156 | } |
1157 | |
1158 | /* Validate the host command */ |
1159 | switch (req->msgcode) { |
1160 | case ISCSI_BSG_HST_VENDOR: |
1161 | cmdlen += sizeof(struct iscsi_bsg_host_vendor); |
1162 | if ((shost->hostt->vendor_id == 0L) || |
1163 | (req->rqst_data.h_vendor.vendor_id != |
1164 | shost->hostt->vendor_id)) { |
1165 | ret = -ESRCH; |
1166 | goto fail_host_msg; |
1167 | } |
1168 | break; |
1169 | default: |
1170 | ret = -EBADR; |
1171 | goto fail_host_msg; |
1172 | } |
1173 | |
1174 | /* check if we really have all the request data needed */ |
1175 | if (job->request_len < cmdlen) { |
1176 | ret = -ENOMSG; |
1177 | goto fail_host_msg; |
1178 | } |
1179 | |
1180 | ret = i->iscsi_transport->bsg_request(job); |
1181 | if (!ret) |
1182 | return 0; |
1183 | |
1184 | fail_host_msg: |
1185 | /* return the errno failure code as the only status */ |
1186 | BUG_ON(job->reply_len < sizeof(uint32_t)); |
1187 | reply->reply_payload_rcv_len = 0; |
1188 | reply->result = ret; |
1189 | job->reply_len = sizeof(uint32_t); |
1190 | bsg_job_done(job, ret, 0); |
1191 | return 0; |
1192 | } |
1193 | |
1194 | /** |
1195 | * iscsi_bsg_host_add - Create and add the bsg hooks to receive requests |
1196 | * @shost: shost for iscsi_host |
1197 | * @ihost: iscsi_cls_host adding the structures to |
1198 | */ |
1199 | static int |
1200 | iscsi_bsg_host_add(struct Scsi_Host *shost, struct iscsi_cls_host *ihost) |
1201 | { |
1202 | struct device *dev = &shost->shost_gendev; |
1203 | struct iscsi_internal *i = to_iscsi_internal(shost->transportt); |
1204 | struct request_queue *q; |
1205 | char bsg_name[20]; |
1206 | int ret; |
1207 | |
1208 | if (!i->iscsi_transport->bsg_request) |
1209 | return -ENOTSUPP; |
1210 | |
1211 | snprintf(bsg_name, sizeof(bsg_name), "iscsi_host%d", shost->host_no); |
1212 | |
1213 | q = __scsi_alloc_queue(shost, bsg_request_fn); |
1214 | if (!q) |
1215 | return -ENOMEM; |
1216 | |
1217 | ret = bsg_setup_queue(dev, q, bsg_name, iscsi_bsg_host_dispatch, 0); |
1218 | if (ret) { |
1219 | shost_printk(KERN_ERR, shost, "bsg interface failed to " |
1220 | "initialize - no request queue\n"); |
1221 | blk_cleanup_queue(q); |
1222 | return ret; |
1223 | } |
1224 | |
1225 | ihost->bsg_q = q; |
1226 | return 0; |
1227 | } |
1228 | |
1229 | static int iscsi_setup_host(struct transport_container *tc, struct device *dev, |
1230 | struct device *cdev) |
1231 | { |
1232 | struct Scsi_Host *shost = dev_to_shost(dev); |
1233 | struct iscsi_cls_host *ihost = shost->shost_data; |
1234 | |
1235 | memset(ihost, 0, sizeof(*ihost)); |
1236 | atomic_set(&ihost->nr_scans, 0); |
1237 | mutex_init(&ihost->mutex); |
1238 | |
1239 | iscsi_bsg_host_add(shost, ihost); |
1240 | /* ignore any bsg add error - we just can't do sgio */ |
1241 | |
1242 | return 0; |
1243 | } |
1244 | |
1245 | static int iscsi_remove_host(struct transport_container *tc, |
1246 | struct device *dev, struct device *cdev) |
1247 | { |
1248 | struct Scsi_Host *shost = dev_to_shost(dev); |
1249 | struct iscsi_cls_host *ihost = shost->shost_data; |
1250 | |
1251 | if (ihost->bsg_q) { |
1252 | bsg_unregister_queue(ihost->bsg_q); |
1253 | blk_cleanup_queue(ihost->bsg_q); |
1254 | } |
1255 | return 0; |
1256 | } |
1257 | |
1258 | static DECLARE_TRANSPORT_CLASS(iscsi_host_class, |
1259 | "iscsi_host", |
1260 | iscsi_setup_host, |
1261 | iscsi_remove_host, |
1262 | NULL); |
1263 | |
1264 | static DECLARE_TRANSPORT_CLASS(iscsi_session_class, |
1265 | "iscsi_session", |
1266 | NULL, |
1267 | NULL, |
1268 | NULL); |
1269 | |
1270 | static DECLARE_TRANSPORT_CLASS(iscsi_connection_class, |
1271 | "iscsi_connection", |
1272 | NULL, |
1273 | NULL, |
1274 | NULL); |
1275 | |
1276 | static struct sock *nls; |
1277 | static DEFINE_MUTEX(rx_queue_mutex); |
1278 | |
1279 | static LIST_HEAD(sesslist); |
1280 | static DEFINE_SPINLOCK(sesslock); |
1281 | static LIST_HEAD(connlist); |
1282 | static DEFINE_SPINLOCK(connlock); |
1283 | |
1284 | static uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn) |
1285 | { |
1286 | struct iscsi_cls_session *sess = iscsi_dev_to_session(conn->dev.parent); |
1287 | return sess->sid; |
1288 | } |
1289 | |
1290 | /* |
1291 | * Returns the matching session to a given sid |
1292 | */ |
1293 | static struct iscsi_cls_session *iscsi_session_lookup(uint32_t sid) |
1294 | { |
1295 | unsigned long flags; |
1296 | struct iscsi_cls_session *sess; |
1297 | |
1298 | spin_lock_irqsave(&sesslock, flags); |
1299 | list_for_each_entry(sess, &sesslist, sess_list) { |
1300 | if (sess->sid == sid) { |
1301 | spin_unlock_irqrestore(&sesslock, flags); |
1302 | return sess; |
1303 | } |
1304 | } |
1305 | spin_unlock_irqrestore(&sesslock, flags); |
1306 | return NULL; |
1307 | } |
1308 | |
1309 | /* |
1310 | * Returns the matching connection to a given sid / cid tuple |
1311 | */ |
1312 | static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid) |
1313 | { |
1314 | unsigned long flags; |
1315 | struct iscsi_cls_conn *conn; |
1316 | |
1317 | spin_lock_irqsave(&connlock, flags); |
1318 | list_for_each_entry(conn, &connlist, conn_list) { |
1319 | if ((conn->cid == cid) && (iscsi_conn_get_sid(conn) == sid)) { |
1320 | spin_unlock_irqrestore(&connlock, flags); |
1321 | return conn; |
1322 | } |
1323 | } |
1324 | spin_unlock_irqrestore(&connlock, flags); |
1325 | return NULL; |
1326 | } |
1327 | |
1328 | /* |
1329 | * The following functions can be used by LLDs that allocate |
1330 | * their own scsi_hosts or by software iscsi LLDs |
1331 | */ |
1332 | static struct { |
1333 | int value; |
1334 | char *name; |
1335 | } iscsi_session_state_names[] = { |
1336 | { ISCSI_SESSION_LOGGED_IN, "LOGGED_IN" }, |
1337 | { ISCSI_SESSION_FAILED, "FAILED" }, |
1338 | { ISCSI_SESSION_FREE, "FREE" }, |
1339 | }; |
1340 | |
1341 | static const char *iscsi_session_state_name(int state) |
1342 | { |
1343 | int i; |
1344 | char *name = NULL; |
1345 | |
1346 | for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) { |
1347 | if (iscsi_session_state_names[i].value == state) { |
1348 | name = iscsi_session_state_names[i].name; |
1349 | break; |
1350 | } |
1351 | } |
1352 | return name; |
1353 | } |
1354 | |
1355 | int iscsi_session_chkready(struct iscsi_cls_session *session) |
1356 | { |
1357 | unsigned long flags; |
1358 | int err; |
1359 | |
1360 | spin_lock_irqsave(&session->lock, flags); |
1361 | switch (session->state) { |
1362 | case ISCSI_SESSION_LOGGED_IN: |
1363 | err = 0; |
1364 | break; |
1365 | case ISCSI_SESSION_FAILED: |
1366 | err = DID_IMM_RETRY << 16; |
1367 | break; |
1368 | case ISCSI_SESSION_FREE: |
1369 | err = DID_TRANSPORT_FAILFAST << 16; |
1370 | break; |
1371 | default: |
1372 | err = DID_NO_CONNECT << 16; |
1373 | break; |
1374 | } |
1375 | spin_unlock_irqrestore(&session->lock, flags); |
1376 | return err; |
1377 | } |
1378 | EXPORT_SYMBOL_GPL(iscsi_session_chkready); |
1379 | |
1380 | int iscsi_is_session_online(struct iscsi_cls_session *session) |
1381 | { |
1382 | unsigned long flags; |
1383 | int ret = 0; |
1384 | |
1385 | spin_lock_irqsave(&session->lock, flags); |
1386 | if (session->state == ISCSI_SESSION_LOGGED_IN) |
1387 | ret = 1; |
1388 | spin_unlock_irqrestore(&session->lock, flags); |
1389 | return ret; |
1390 | } |
1391 | EXPORT_SYMBOL_GPL(iscsi_is_session_online); |
1392 | |
1393 | static void iscsi_session_release(struct device *dev) |
1394 | { |
1395 | struct iscsi_cls_session *session = iscsi_dev_to_session(dev); |
1396 | struct Scsi_Host *shost; |
1397 | |
1398 | shost = iscsi_session_to_shost(session); |
1399 | scsi_host_put(shost); |
1400 | ISCSI_DBG_TRANS_SESSION(session, "Completing session release\n"); |
1401 | kfree(session); |
1402 | } |
1403 | |
1404 | int iscsi_is_session_dev(const struct device *dev) |
1405 | { |
1406 | return dev->release == iscsi_session_release; |
1407 | } |
1408 | EXPORT_SYMBOL_GPL(iscsi_is_session_dev); |
1409 | |
1410 | static int iscsi_iter_session_fn(struct device *dev, void *data) |
1411 | { |
1412 | void (* fn) (struct iscsi_cls_session *) = data; |
1413 | |
1414 | if (!iscsi_is_session_dev(dev)) |
1415 | return 0; |
1416 | fn(iscsi_dev_to_session(dev)); |
1417 | return 0; |
1418 | } |
1419 | |
1420 | void iscsi_host_for_each_session(struct Scsi_Host *shost, |
1421 | void (*fn)(struct iscsi_cls_session *)) |
1422 | { |
1423 | device_for_each_child(&shost->shost_gendev, fn, |
1424 | iscsi_iter_session_fn); |
1425 | } |
1426 | EXPORT_SYMBOL_GPL(iscsi_host_for_each_session); |
1427 | |
1428 | /** |
1429 | * iscsi_scan_finished - helper to report when running scans are done |
1430 | * @shost: scsi host |
1431 | * @time: scan run time |
1432 | * |
1433 | * This function can be used by drives like qla4xxx to report to the scsi |
1434 | * layer when the scans it kicked off at module load time are done. |
1435 | */ |
1436 | int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time) |
1437 | { |
1438 | struct iscsi_cls_host *ihost = shost->shost_data; |
1439 | /* |
1440 | * qla4xxx will have kicked off some session unblocks before calling |
1441 | * scsi_scan_host, so just wait for them to complete. |
1442 | */ |
1443 | return !atomic_read(&ihost->nr_scans); |
1444 | } |
1445 | EXPORT_SYMBOL_GPL(iscsi_scan_finished); |
1446 | |
1447 | struct iscsi_scan_data { |
1448 | unsigned int channel; |
1449 | unsigned int id; |
1450 | unsigned int lun; |
1451 | }; |
1452 | |
1453 | static int iscsi_user_scan_session(struct device *dev, void *data) |
1454 | { |
1455 | struct iscsi_scan_data *scan_data = data; |
1456 | struct iscsi_cls_session *session; |
1457 | struct Scsi_Host *shost; |
1458 | struct iscsi_cls_host *ihost; |
1459 | unsigned long flags; |
1460 | unsigned int id; |
1461 | |
1462 | if (!iscsi_is_session_dev(dev)) |
1463 | return 0; |
1464 | |
1465 | session = iscsi_dev_to_session(dev); |
1466 | |
1467 | ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n"); |
1468 | |
1469 | shost = iscsi_session_to_shost(session); |
1470 | ihost = shost->shost_data; |
1471 | |
1472 | mutex_lock(&ihost->mutex); |
1473 | spin_lock_irqsave(&session->lock, flags); |
1474 | if (session->state != ISCSI_SESSION_LOGGED_IN) { |
1475 | spin_unlock_irqrestore(&session->lock, flags); |
1476 | goto user_scan_exit; |
1477 | } |
1478 | id = session->target_id; |
1479 | spin_unlock_irqrestore(&session->lock, flags); |
1480 | |
1481 | if (id != ISCSI_MAX_TARGET) { |
1482 | if ((scan_data->channel == SCAN_WILD_CARD || |
1483 | scan_data->channel == 0) && |
1484 | (scan_data->id == SCAN_WILD_CARD || |
1485 | scan_data->id == id)) |
1486 | scsi_scan_target(&session->dev, 0, id, |
1487 | scan_data->lun, 1); |
1488 | } |
1489 | |
1490 | user_scan_exit: |
1491 | mutex_unlock(&ihost->mutex); |
1492 | ISCSI_DBG_TRANS_SESSION(session, "Completed session scan\n"); |
1493 | return 0; |
1494 | } |
1495 | |
1496 | static int iscsi_user_scan(struct Scsi_Host *shost, uint channel, |
1497 | uint id, uint lun) |
1498 | { |
1499 | struct iscsi_scan_data scan_data; |
1500 | |
1501 | scan_data.channel = channel; |
1502 | scan_data.id = id; |
1503 | scan_data.lun = lun; |
1504 | |
1505 | return device_for_each_child(&shost->shost_gendev, &scan_data, |
1506 | iscsi_user_scan_session); |
1507 | } |
1508 | |
1509 | static void iscsi_scan_session(struct work_struct *work) |
1510 | { |
1511 | struct iscsi_cls_session *session = |
1512 | container_of(work, struct iscsi_cls_session, scan_work); |
1513 | struct Scsi_Host *shost = iscsi_session_to_shost(session); |
1514 | struct iscsi_cls_host *ihost = shost->shost_data; |
1515 | struct iscsi_scan_data scan_data; |
1516 | |
1517 | scan_data.channel = 0; |
1518 | scan_data.id = SCAN_WILD_CARD; |
1519 | scan_data.lun = SCAN_WILD_CARD; |
1520 | |
1521 | iscsi_user_scan_session(&session->dev, &scan_data); |
1522 | atomic_dec(&ihost->nr_scans); |
1523 | } |
1524 | |
1525 | /** |
1526 | * iscsi_block_scsi_eh - block scsi eh until session state has transistioned |
1527 | * @cmd: scsi cmd passed to scsi eh handler |
1528 | * |
1529 | * If the session is down this function will wait for the recovery |
1530 | * timer to fire or for the session to be logged back in. If the |
1531 | * recovery timer fires then FAST_IO_FAIL is returned. The caller |
1532 | * should pass this error value to the scsi eh. |
1533 | */ |
1534 | int iscsi_block_scsi_eh(struct scsi_cmnd *cmd) |
1535 | { |
1536 | struct iscsi_cls_session *session = |
1537 | starget_to_session(scsi_target(cmd->device)); |
1538 | unsigned long flags; |
1539 | int ret = 0; |
1540 | |
1541 | spin_lock_irqsave(&session->lock, flags); |
1542 | while (session->state != ISCSI_SESSION_LOGGED_IN) { |
1543 | if (session->state == ISCSI_SESSION_FREE) { |
1544 | ret = FAST_IO_FAIL; |
1545 | break; |
1546 | } |
1547 | spin_unlock_irqrestore(&session->lock, flags); |
1548 | msleep(1000); |
1549 | spin_lock_irqsave(&session->lock, flags); |
1550 | } |
1551 | spin_unlock_irqrestore(&session->lock, flags); |
1552 | return ret; |
1553 | } |
1554 | EXPORT_SYMBOL_GPL(iscsi_block_scsi_eh); |
1555 | |
1556 | static void session_recovery_timedout(struct work_struct *work) |
1557 | { |
1558 | struct iscsi_cls_session *session = |
1559 | container_of(work, struct iscsi_cls_session, |
1560 | recovery_work.work); |
1561 | unsigned long flags; |
1562 | |
1563 | iscsi_cls_session_printk(KERN_INFO, session, |
1564 | "session recovery timed out after %d secs\n", |
1565 | session->recovery_tmo); |
1566 | |
1567 | spin_lock_irqsave(&session->lock, flags); |
1568 | switch (session->state) { |
1569 | case ISCSI_SESSION_FAILED: |
1570 | session->state = ISCSI_SESSION_FREE; |
1571 | break; |
1572 | case ISCSI_SESSION_LOGGED_IN: |
1573 | case ISCSI_SESSION_FREE: |
1574 | /* we raced with the unblock's flush */ |
1575 | spin_unlock_irqrestore(&session->lock, flags); |
1576 | return; |
1577 | } |
1578 | spin_unlock_irqrestore(&session->lock, flags); |
1579 | |
1580 | if (session->transport->session_recovery_timedout) |
1581 | session->transport->session_recovery_timedout(session); |
1582 | |
1583 | ISCSI_DBG_TRANS_SESSION(session, "Unblocking SCSI target\n"); |
1584 | scsi_target_unblock(&session->dev, SDEV_TRANSPORT_OFFLINE); |
1585 | ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking SCSI target\n"); |
1586 | } |
1587 | |
1588 | static void __iscsi_unblock_session(struct work_struct *work) |
1589 | { |
1590 | struct iscsi_cls_session *session = |
1591 | container_of(work, struct iscsi_cls_session, |
1592 | unblock_work); |
1593 | struct Scsi_Host *shost = iscsi_session_to_shost(session); |
1594 | struct iscsi_cls_host *ihost = shost->shost_data; |
1595 | unsigned long flags; |
1596 | |
1597 | ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n"); |
1598 | /* |
1599 | * The recovery and unblock work get run from the same workqueue, |
1600 | * so try to cancel it if it was going to run after this unblock. |
1601 | */ |
1602 | cancel_delayed_work(&session->recovery_work); |
1603 | spin_lock_irqsave(&session->lock, flags); |
1604 | session->state = ISCSI_SESSION_LOGGED_IN; |
1605 | spin_unlock_irqrestore(&session->lock, flags); |
1606 | /* start IO */ |
1607 | scsi_target_unblock(&session->dev, SDEV_RUNNING); |
1608 | /* |
1609 | * Only do kernel scanning if the driver is properly hooked into |
1610 | * the async scanning code (drivers like iscsi_tcp do login and |
1611 | * scanning from userspace). |
1612 | */ |
1613 | if (shost->hostt->scan_finished) { |
1614 | if (scsi_queue_work(shost, &session->scan_work)) |
1615 | atomic_inc(&ihost->nr_scans); |
1616 | } |
1617 | ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n"); |
1618 | } |
1619 | |
1620 | /** |
1621 | * iscsi_unblock_session - set a session as logged in and start IO. |
1622 | * @session: iscsi session |
1623 | * |
1624 | * Mark a session as ready to accept IO. |
1625 | */ |
1626 | void iscsi_unblock_session(struct iscsi_cls_session *session) |
1627 | { |
1628 | queue_work(iscsi_eh_timer_workq, &session->unblock_work); |
1629 | /* |
1630 | * make sure all the events have completed before tell the driver |
1631 | * it is safe |
1632 | */ |
1633 | flush_workqueue(iscsi_eh_timer_workq); |
1634 | } |
1635 | EXPORT_SYMBOL_GPL(iscsi_unblock_session); |
1636 | |
1637 | static void __iscsi_block_session(struct work_struct *work) |
1638 | { |
1639 | struct iscsi_cls_session *session = |
1640 | container_of(work, struct iscsi_cls_session, |
1641 | block_work); |
1642 | unsigned long flags; |
1643 | |
1644 | ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n"); |
1645 | spin_lock_irqsave(&session->lock, flags); |
1646 | session->state = ISCSI_SESSION_FAILED; |
1647 | spin_unlock_irqrestore(&session->lock, flags); |
1648 | scsi_target_block(&session->dev); |
1649 | ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n"); |
1650 | if (session->recovery_tmo >= 0) |
1651 | queue_delayed_work(iscsi_eh_timer_workq, |
1652 | &session->recovery_work, |
1653 | session->recovery_tmo * HZ); |
1654 | } |
1655 | |
1656 | void iscsi_block_session(struct iscsi_cls_session *session) |
1657 | { |
1658 | queue_work(iscsi_eh_timer_workq, &session->block_work); |
1659 | } |
1660 | EXPORT_SYMBOL_GPL(iscsi_block_session); |
1661 | |
1662 | static void __iscsi_unbind_session(struct work_struct *work) |
1663 | { |
1664 | struct iscsi_cls_session *session = |
1665 | container_of(work, struct iscsi_cls_session, |
1666 | unbind_work); |
1667 | struct Scsi_Host *shost = iscsi_session_to_shost(session); |
1668 | struct iscsi_cls_host *ihost = shost->shost_data; |
1669 | unsigned long flags; |
1670 | unsigned int target_id; |
1671 | |
1672 | ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n"); |
1673 | |
1674 | /* Prevent new scans and make sure scanning is not in progress */ |
1675 | mutex_lock(&ihost->mutex); |
1676 | spin_lock_irqsave(&session->lock, flags); |
1677 | if (session->target_id == ISCSI_MAX_TARGET) { |
1678 | spin_unlock_irqrestore(&session->lock, flags); |
1679 | mutex_unlock(&ihost->mutex); |
1680 | return; |
1681 | } |
1682 | |
1683 | target_id = session->target_id; |
1684 | session->target_id = ISCSI_MAX_TARGET; |
1685 | spin_unlock_irqrestore(&session->lock, flags); |
1686 | mutex_unlock(&ihost->mutex); |
1687 | |
1688 | if (session->ida_used) |
1689 | ida_simple_remove(&iscsi_sess_ida, target_id); |
1690 | |
1691 | scsi_remove_target(&session->dev); |
1692 | iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION); |
1693 | ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n"); |
1694 | } |
1695 | |
1696 | struct iscsi_cls_session * |
1697 | iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport, |
1698 | int dd_size) |
1699 | { |
1700 | struct iscsi_cls_session *session; |
1701 | |
1702 | session = kzalloc(sizeof(*session) + dd_size, |
1703 | GFP_KERNEL); |
1704 | if (!session) |
1705 | return NULL; |
1706 | |
1707 | session->transport = transport; |
1708 | session->creator = -1; |
1709 | session->recovery_tmo = 120; |
1710 | session->state = ISCSI_SESSION_FREE; |
1711 | INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); |
1712 | INIT_LIST_HEAD(&session->sess_list); |
1713 | INIT_WORK(&session->unblock_work, __iscsi_unblock_session); |
1714 | INIT_WORK(&session->block_work, __iscsi_block_session); |
1715 | INIT_WORK(&session->unbind_work, __iscsi_unbind_session); |
1716 | INIT_WORK(&session->scan_work, iscsi_scan_session); |
1717 | spin_lock_init(&session->lock); |
1718 | |
1719 | /* this is released in the dev's release function */ |
1720 | scsi_host_get(shost); |
1721 | session->dev.parent = &shost->shost_gendev; |
1722 | session->dev.release = iscsi_session_release; |
1723 | device_initialize(&session->dev); |
1724 | if (dd_size) |
1725 | session->dd_data = &session[1]; |
1726 | |
1727 | ISCSI_DBG_TRANS_SESSION(session, "Completed session allocation\n"); |
1728 | return session; |
1729 | } |
1730 | EXPORT_SYMBOL_GPL(iscsi_alloc_session); |
1731 | |
1732 | int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) |
1733 | { |
1734 | struct Scsi_Host *shost = iscsi_session_to_shost(session); |
1735 | struct iscsi_cls_host *ihost; |
1736 | unsigned long flags; |
1737 | int id = 0; |
1738 | int err; |
1739 | |
1740 | ihost = shost->shost_data; |
1741 | session->sid = atomic_add_return(1, &iscsi_session_nr); |
1742 | |
1743 | if (target_id == ISCSI_MAX_TARGET) { |
1744 | id = ida_simple_get(&iscsi_sess_ida, 0, 0, GFP_KERNEL); |
1745 | |
1746 | if (id < 0) { |
1747 | iscsi_cls_session_printk(KERN_ERR, session, |
1748 | "Failure in Target ID Allocation\n"); |
1749 | return id; |
1750 | } |
1751 | session->target_id = (unsigned int)id; |
1752 | session->ida_used = true; |
1753 | } else |
1754 | session->target_id = target_id; |
1755 | |
1756 | dev_set_name(&session->dev, "session%u", session->sid); |
1757 | err = device_add(&session->dev); |
1758 | if (err) { |
1759 | iscsi_cls_session_printk(KERN_ERR, session, |
1760 | "could not register session's dev\n"); |
1761 | goto release_ida; |
1762 | } |
1763 | transport_register_device(&session->dev); |
1764 | |
1765 | spin_lock_irqsave(&sesslock, flags); |
1766 | list_add(&session->sess_list, &sesslist); |
1767 | spin_unlock_irqrestore(&sesslock, flags); |
1768 | |
1769 | iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION); |
1770 | ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n"); |
1771 | return 0; |
1772 | |
1773 | release_ida: |
1774 | if (session->ida_used) |
1775 | ida_simple_remove(&iscsi_sess_ida, session->target_id); |
1776 | |
1777 | return err; |
1778 | } |
1779 | EXPORT_SYMBOL_GPL(iscsi_add_session); |
1780 | |
1781 | /** |
1782 | * iscsi_create_session - create iscsi class session |
1783 | * @shost: scsi host |
1784 | * @transport: iscsi transport |
1785 | * @dd_size: private driver data size |
1786 | * @target_id: which target |
1787 | * |
1788 | * This can be called from a LLD or iscsi_transport. |
1789 | */ |
1790 | struct iscsi_cls_session * |
1791 | iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport, |
1792 | int dd_size, unsigned int target_id) |
1793 | { |
1794 | struct iscsi_cls_session *session; |
1795 | |
1796 | session = iscsi_alloc_session(shost, transport, dd_size); |
1797 | if (!session) |
1798 | return NULL; |
1799 | |
1800 | if (iscsi_add_session(session, target_id)) { |
1801 | iscsi_free_session(session); |
1802 | return NULL; |
1803 | } |
1804 | return session; |
1805 | } |
1806 | EXPORT_SYMBOL_GPL(iscsi_create_session); |
1807 | |
1808 | static void iscsi_conn_release(struct device *dev) |
1809 | { |
1810 | struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev); |
1811 | struct device *parent = conn->dev.parent; |
1812 | |
1813 | ISCSI_DBG_TRANS_CONN(conn, "Releasing conn\n"); |
1814 | kfree(conn); |
1815 | put_device(parent); |
1816 | } |
1817 | |
1818 | static int iscsi_is_conn_dev(const struct device *dev) |
1819 | { |
1820 | return dev->release == iscsi_conn_release; |
1821 | } |
1822 | |
1823 | static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data) |
1824 | { |
1825 | if (!iscsi_is_conn_dev(dev)) |
1826 | return 0; |
1827 | return iscsi_destroy_conn(iscsi_dev_to_conn(dev)); |
1828 | } |
1829 | |
1830 | void iscsi_remove_session(struct iscsi_cls_session *session) |
1831 | { |
1832 | struct Scsi_Host *shost = iscsi_session_to_shost(session); |
1833 | unsigned long flags; |
1834 | int err; |
1835 | |
1836 | ISCSI_DBG_TRANS_SESSION(session, "Removing session\n"); |
1837 | |
1838 | spin_lock_irqsave(&sesslock, flags); |
1839 | list_del(&session->sess_list); |
1840 | spin_unlock_irqrestore(&sesslock, flags); |
1841 | |
1842 | /* make sure there are no blocks/unblocks queued */ |
1843 | flush_workqueue(iscsi_eh_timer_workq); |
1844 | /* make sure the timedout callout is not running */ |
1845 | if (!cancel_delayed_work(&session->recovery_work)) |
1846 | flush_workqueue(iscsi_eh_timer_workq); |
1847 | /* |
1848 | * If we are blocked let commands flow again. The lld or iscsi |
1849 | * layer should set up the queuecommand to fail commands. |
1850 | * We assume that LLD will not be calling block/unblock while |
1851 | * removing the session. |
1852 | */ |
1853 | spin_lock_irqsave(&session->lock, flags); |
1854 | session->state = ISCSI_SESSION_FREE; |
1855 | spin_unlock_irqrestore(&session->lock, flags); |
1856 | |
1857 | scsi_target_unblock(&session->dev, SDEV_TRANSPORT_OFFLINE); |
1858 | /* flush running scans then delete devices */ |
1859 | scsi_flush_work(shost); |
1860 | __iscsi_unbind_session(&session->unbind_work); |
1861 | |
1862 | /* hw iscsi may not have removed all connections from session */ |
1863 | err = device_for_each_child(&session->dev, NULL, |
1864 | iscsi_iter_destroy_conn_fn); |
1865 | if (err) |
1866 | iscsi_cls_session_printk(KERN_ERR, session, |
1867 | "Could not delete all connections " |
1868 | "for session. Error %d.\n", err); |
1869 | |
1870 | transport_unregister_device(&session->dev); |
1871 | |
1872 | ISCSI_DBG_TRANS_SESSION(session, "Completing session removal\n"); |
1873 | device_del(&session->dev); |
1874 | } |
1875 | EXPORT_SYMBOL_GPL(iscsi_remove_session); |
1876 | |
1877 | void iscsi_free_session(struct iscsi_cls_session *session) |
1878 | { |
1879 | ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n"); |
1880 | iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION); |
1881 | put_device(&session->dev); |
1882 | } |
1883 | EXPORT_SYMBOL_GPL(iscsi_free_session); |
1884 | |
1885 | /** |
1886 | * iscsi_destroy_session - destroy iscsi session |
1887 | * @session: iscsi_session |
1888 | * |
1889 | * Can be called by a LLD or iscsi_transport. There must not be |
1890 | * any running connections. |
1891 | */ |
1892 | int iscsi_destroy_session(struct iscsi_cls_session *session) |
1893 | { |
1894 | iscsi_remove_session(session); |
1895 | ISCSI_DBG_TRANS_SESSION(session, "Completing session destruction\n"); |
1896 | iscsi_free_session(session); |
1897 | return 0; |
1898 | } |
1899 | EXPORT_SYMBOL_GPL(iscsi_destroy_session); |
1900 | |
1901 | /** |
1902 | * iscsi_create_conn - create iscsi class connection |
1903 | * @session: iscsi cls session |
1904 | * @dd_size: private driver data size |
1905 | * @cid: connection id |
1906 | * |
1907 | * This can be called from a LLD or iscsi_transport. The connection |
1908 | * is child of the session so cid must be unique for all connections |
1909 | * on the session. |
1910 | * |
1911 | * Since we do not support MCS, cid will normally be zero. In some cases |
1912 | * for software iscsi we could be trying to preallocate a connection struct |
1913 | * in which case there could be two connection structs and cid would be |
1914 | * non-zero. |
1915 | */ |
1916 | struct iscsi_cls_conn * |
1917 | iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) |
1918 | { |
1919 | struct iscsi_transport *transport = session->transport; |
1920 | struct iscsi_cls_conn *conn; |
1921 | unsigned long flags; |
1922 | int err; |
1923 | |
1924 | conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL); |
1925 | if (!conn) |
1926 | return NULL; |
1927 | if (dd_size) |
1928 | conn->dd_data = &conn[1]; |
1929 | |
1930 | mutex_init(&conn->ep_mutex); |
1931 | INIT_LIST_HEAD(&conn->conn_list); |
1932 | conn->transport = transport; |
1933 | conn->cid = cid; |
1934 | |
1935 | /* this is released in the dev's release function */ |
1936 | if (!get_device(&session->dev)) |
1937 | goto free_conn; |
1938 | |
1939 | dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid); |
1940 | conn->dev.parent = &session->dev; |
1941 | conn->dev.release = iscsi_conn_release; |
1942 | err = device_register(&conn->dev); |
1943 | if (err) { |
1944 | iscsi_cls_session_printk(KERN_ERR, session, "could not " |
1945 | "register connection's dev\n"); |
1946 | goto release_parent_ref; |
1947 | } |
1948 | transport_register_device(&conn->dev); |
1949 | |
1950 | spin_lock_irqsave(&connlock, flags); |
1951 | list_add(&conn->conn_list, &connlist); |
1952 | spin_unlock_irqrestore(&connlock, flags); |
1953 | |
1954 | ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n"); |
1955 | return conn; |
1956 | |
1957 | release_parent_ref: |
1958 | put_device(&session->dev); |
1959 | free_conn: |
1960 | kfree(conn); |
1961 | return NULL; |
1962 | } |
1963 | |
1964 | EXPORT_SYMBOL_GPL(iscsi_create_conn); |
1965 | |
1966 | /** |
1967 | * iscsi_destroy_conn - destroy iscsi class connection |
1968 | * @conn: iscsi cls session |
1969 | * |
1970 | * This can be called from a LLD or iscsi_transport. |
1971 | */ |
1972 | int iscsi_destroy_conn(struct iscsi_cls_conn *conn) |
1973 | { |
1974 | unsigned long flags; |
1975 | |
1976 | spin_lock_irqsave(&connlock, flags); |
1977 | list_del(&conn->conn_list); |
1978 | spin_unlock_irqrestore(&connlock, flags); |
1979 | |
1980 | transport_unregister_device(&conn->dev); |
1981 | ISCSI_DBG_TRANS_CONN(conn, "Completing conn destruction\n"); |
1982 | device_unregister(&conn->dev); |
1983 | return 0; |
1984 | } |
1985 | EXPORT_SYMBOL_GPL(iscsi_destroy_conn); |
1986 | |
1987 | /* |
1988 | * iscsi interface functions |
1989 | */ |
1990 | static struct iscsi_internal * |
1991 | iscsi_if_transport_lookup(struct iscsi_transport *tt) |
1992 | { |
1993 | struct iscsi_internal *priv; |
1994 | unsigned long flags; |
1995 | |
1996 | spin_lock_irqsave(&iscsi_transport_lock, flags); |
1997 | list_for_each_entry(priv, &iscsi_transports, list) { |
1998 | if (tt == priv->iscsi_transport) { |
1999 | spin_unlock_irqrestore(&iscsi_transport_lock, flags); |
2000 | return priv; |
2001 | } |
2002 | } |
2003 | spin_unlock_irqrestore(&iscsi_transport_lock, flags); |
2004 | return NULL; |
2005 | } |
2006 | |
2007 | static int |
2008 | iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp) |
2009 | { |
2010 | return nlmsg_multicast(nls, skb, 0, group, gfp); |
2011 | } |
2012 | |
2013 | int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, |
2014 | char *data, uint32_t data_size) |
2015 | { |
2016 | struct nlmsghdr *nlh; |
2017 | struct sk_buff *skb; |
2018 | struct iscsi_uevent *ev; |
2019 | char *pdu; |
2020 | struct iscsi_internal *priv; |
2021 | int len = nlmsg_total_size(sizeof(*ev) + sizeof(struct iscsi_hdr) + |
2022 | data_size); |
2023 | |
2024 | priv = iscsi_if_transport_lookup(conn->transport); |
2025 | if (!priv) |
2026 | return -EINVAL; |
2027 | |
2028 | skb = alloc_skb(len, GFP_ATOMIC); |
2029 | if (!skb) { |
2030 | iscsi_conn_error_event(conn, ISCSI_ERR_CONN_FAILED); |
2031 | iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver " |
2032 | "control PDU: OOM\n"); |
2033 | return -ENOMEM; |
2034 | } |
2035 | |
2036 | nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); |
2037 | ev = nlmsg_data(nlh); |
2038 | memset(ev, 0, sizeof(*ev)); |
2039 | ev->transport_handle = iscsi_handle(conn->transport); |
2040 | ev->type = ISCSI_KEVENT_RECV_PDU; |
2041 | ev->r.recv_req.cid = conn->cid; |
2042 | ev->r.recv_req.sid = iscsi_conn_get_sid(conn); |
2043 | pdu = (char*)ev + sizeof(*ev); |
2044 | memcpy(pdu, hdr, sizeof(struct iscsi_hdr)); |
2045 | memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size); |
2046 | |
2047 | return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC); |
2048 | } |
2049 | EXPORT_SYMBOL_GPL(iscsi_recv_pdu); |
2050 | |
2051 | int iscsi_offload_mesg(struct Scsi_Host *shost, |
2052 | struct iscsi_transport *transport, uint32_t type, |
2053 | char *data, uint16_t data_size) |
2054 | { |
2055 | struct nlmsghdr *nlh; |
2056 | struct sk_buff *skb; |
2057 | struct iscsi_uevent *ev; |
2058 | int len = nlmsg_total_size(sizeof(*ev) + data_size); |
2059 | |
2060 | skb = alloc_skb(len, GFP_ATOMIC); |
2061 | if (!skb) { |
2062 | printk(KERN_ERR "can not deliver iscsi offload message:OOM\n"); |
2063 | return -ENOMEM; |
2064 | } |
2065 | |
2066 | nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); |
2067 | ev = nlmsg_data(nlh); |
2068 | memset(ev, 0, sizeof(*ev)); |
2069 | ev->type = type; |
2070 | ev->transport_handle = iscsi_handle(transport); |
2071 | switch (type) { |
2072 | case ISCSI_KEVENT_PATH_REQ: |
2073 | ev->r.req_path.host_no = shost->host_no; |
2074 | break; |
2075 | case ISCSI_KEVENT_IF_DOWN: |
2076 | ev->r.notify_if_down.host_no = shost->host_no; |
2077 | break; |
2078 | } |
2079 | |
2080 | memcpy((char *)ev + sizeof(*ev), data, data_size); |
2081 | |
2082 | return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_ATOMIC); |
2083 | } |
2084 | EXPORT_SYMBOL_GPL(iscsi_offload_mesg); |
2085 | |
2086 | void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) |
2087 | { |
2088 | struct nlmsghdr *nlh; |
2089 | struct sk_buff *skb; |
2090 | struct iscsi_uevent *ev; |
2091 | struct iscsi_internal *priv; |
2092 | int len = nlmsg_total_size(sizeof(*ev)); |
2093 | |
2094 | priv = iscsi_if_transport_lookup(conn->transport); |
2095 | if (!priv) |
2096 | return; |
2097 | |
2098 | skb = alloc_skb(len, GFP_ATOMIC); |
2099 | if (!skb) { |
2100 | iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored " |
2101 | "conn error (%d)\n", error); |
2102 | return; |
2103 | } |
2104 | |
2105 | nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); |
2106 | ev = nlmsg_data(nlh); |
2107 | ev->transport_handle = iscsi_handle(conn->transport); |
2108 | ev->type = ISCSI_KEVENT_CONN_ERROR; |
2109 | ev->r.connerror.error = error; |
2110 | ev->r.connerror.cid = conn->cid; |
2111 | ev->r.connerror.sid = iscsi_conn_get_sid(conn); |
2112 | |
2113 | iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC); |
2114 | |
2115 | iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n", |
2116 | error); |
2117 | } |
2118 | EXPORT_SYMBOL_GPL(iscsi_conn_error_event); |
2119 | |
2120 | void iscsi_conn_login_event(struct iscsi_cls_conn *conn, |
2121 | enum iscsi_conn_state state) |
2122 | { |
2123 | struct nlmsghdr *nlh; |
2124 | struct sk_buff *skb; |
2125 | struct iscsi_uevent *ev; |
2126 | struct iscsi_internal *priv; |
2127 | int len = nlmsg_total_size(sizeof(*ev)); |
2128 | |
2129 | priv = iscsi_if_transport_lookup(conn->transport); |
2130 | if (!priv) |
2131 | return; |
2132 | |
2133 | skb = alloc_skb(len, GFP_ATOMIC); |
2134 | if (!skb) { |
2135 | iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored " |
2136 | "conn login (%d)\n", state); |
2137 | return; |
2138 | } |
2139 | |
2140 | nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); |
2141 | ev = nlmsg_data(nlh); |
2142 | ev->transport_handle = iscsi_handle(conn->transport); |
2143 | ev->type = ISCSI_KEVENT_CONN_LOGIN_STATE; |
2144 | ev->r.conn_login.state = state; |
2145 | ev->r.conn_login.cid = conn->cid; |
2146 | ev->r.conn_login.sid = iscsi_conn_get_sid(conn); |
2147 | iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC); |
2148 | |
2149 | iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn login (%d)\n", |
2150 | state); |
2151 | } |
2152 | EXPORT_SYMBOL_GPL(iscsi_conn_login_event); |
2153 | |
2154 | void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport, |
2155 | enum iscsi_host_event_code code, uint32_t data_size, |
2156 | uint8_t *data) |
2157 | { |
2158 | struct nlmsghdr *nlh; |
2159 | struct sk_buff *skb; |
2160 | struct iscsi_uevent *ev; |
2161 | int len = nlmsg_total_size(sizeof(*ev) + data_size); |
2162 | |
2163 | skb = alloc_skb(len, GFP_NOIO); |
2164 | if (!skb) { |
2165 | printk(KERN_ERR "gracefully ignored host event (%d):%d OOM\n", |
2166 | host_no, code); |
2167 | return; |
2168 | } |
2169 | |
2170 | nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); |
2171 | ev = nlmsg_data(nlh); |
2172 | ev->transport_handle = iscsi_handle(transport); |
2173 | ev->type = ISCSI_KEVENT_HOST_EVENT; |
2174 | ev->r.host_event.host_no = host_no; |
2175 | ev->r.host_event.code = code; |
2176 | ev->r.host_event.data_size = data_size; |
2177 | |
2178 | if (data_size) |
2179 | memcpy((char *)ev + sizeof(*ev), data, data_size); |
2180 | |
2181 | iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_NOIO); |
2182 | } |
2183 | EXPORT_SYMBOL_GPL(iscsi_post_host_event); |
2184 | |
2185 | void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport, |
2186 | uint32_t status, uint32_t pid, uint32_t data_size, |
2187 | uint8_t *data) |
2188 | { |
2189 | struct nlmsghdr *nlh; |
2190 | struct sk_buff *skb; |
2191 | struct iscsi_uevent *ev; |
2192 | int len = nlmsg_total_size(sizeof(*ev) + data_size); |
2193 | |
2194 | skb = alloc_skb(len, GFP_NOIO); |
2195 | if (!skb) { |
2196 | printk(KERN_ERR "gracefully ignored ping comp: OOM\n"); |
2197 | return; |
2198 | } |
2199 | |
2200 | nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); |
2201 | ev = nlmsg_data(nlh); |
2202 | ev->transport_handle = iscsi_handle(transport); |
2203 | ev->type = ISCSI_KEVENT_PING_COMP; |
2204 | ev->r.ping_comp.host_no = host_no; |
2205 | ev->r.ping_comp.status = status; |
2206 | ev->r.ping_comp.pid = pid; |
2207 | ev->r.ping_comp.data_size = data_size; |
2208 | memcpy((char *)ev + sizeof(*ev), data, data_size); |
2209 | |
2210 | iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_NOIO); |
2211 | } |
2212 | EXPORT_SYMBOL_GPL(iscsi_ping_comp_event); |
2213 | |
2214 | static int |
2215 | iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi, |
2216 | void *payload, int size) |
2217 | { |
2218 | struct sk_buff *skb; |
2219 | struct nlmsghdr *nlh; |
2220 | int len = nlmsg_total_size(size); |
2221 | int flags = multi ? NLM_F_MULTI : 0; |
2222 | int t = done ? NLMSG_DONE : type; |
2223 | |
2224 | skb = alloc_skb(len, GFP_ATOMIC); |
2225 | if (!skb) { |
2226 | printk(KERN_ERR "Could not allocate skb to send reply.\n"); |
2227 | return -ENOMEM; |
2228 | } |
2229 | |
2230 | nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0); |
2231 | nlh->nlmsg_flags = flags; |
2232 | memcpy(nlmsg_data(nlh), payload, size); |
2233 | return iscsi_multicast_skb(skb, group, GFP_ATOMIC); |
2234 | } |
2235 | |
2236 | static int |
2237 | iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh) |
2238 | { |
2239 | struct iscsi_uevent *ev = nlmsg_data(nlh); |
2240 | struct iscsi_stats *stats; |
2241 | struct sk_buff *skbstat; |
2242 | struct iscsi_cls_conn *conn; |
2243 | struct nlmsghdr *nlhstat; |
2244 | struct iscsi_uevent *evstat; |
2245 | struct iscsi_internal *priv; |
2246 | int len = nlmsg_total_size(sizeof(*ev) + |
2247 | sizeof(struct iscsi_stats) + |
2248 | sizeof(struct iscsi_stats_custom) * |
2249 | ISCSI_STATS_CUSTOM_MAX); |
2250 | int err = 0; |
2251 | |
2252 | priv = iscsi_if_transport_lookup(transport); |
2253 | if (!priv) |
2254 | return -EINVAL; |
2255 | |
2256 | conn = iscsi_conn_lookup(ev->u.get_stats.sid, ev->u.get_stats.cid); |
2257 | if (!conn) |
2258 | return -EEXIST; |
2259 | |
2260 | do { |
2261 | int actual_size; |
2262 | |
2263 | skbstat = alloc_skb(len, GFP_ATOMIC); |
2264 | if (!skbstat) { |
2265 | iscsi_cls_conn_printk(KERN_ERR, conn, "can not " |
2266 | "deliver stats: OOM\n"); |
2267 | return -ENOMEM; |
2268 | } |
2269 | |
2270 | nlhstat = __nlmsg_put(skbstat, 0, 0, 0, |
2271 | (len - sizeof(*nlhstat)), 0); |
2272 | evstat = nlmsg_data(nlhstat); |
2273 | memset(evstat, 0, sizeof(*evstat)); |
2274 | evstat->transport_handle = iscsi_handle(conn->transport); |
2275 | evstat->type = nlh->nlmsg_type; |
2276 | evstat->u.get_stats.cid = |
2277 | ev->u.get_stats.cid; |
2278 | evstat->u.get_stats.sid = |
2279 | ev->u.get_stats.sid; |
2280 | stats = (struct iscsi_stats *) |
2281 | ((char*)evstat + sizeof(*evstat)); |
2282 | memset(stats, 0, sizeof(*stats)); |
2283 | |
2284 | transport->get_stats(conn, stats); |
2285 | actual_size = nlmsg_total_size(sizeof(struct iscsi_uevent) + |
2286 | sizeof(struct iscsi_stats) + |
2287 | sizeof(struct iscsi_stats_custom) * |
2288 | stats->custom_length); |
2289 | actual_size -= sizeof(*nlhstat); |
2290 | actual_size = nlmsg_msg_size(actual_size); |
2291 | skb_trim(skbstat, NLMSG_ALIGN(actual_size)); |
2292 | nlhstat->nlmsg_len = actual_size; |
2293 | |
2294 | err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID, |
2295 | GFP_ATOMIC); |
2296 | } while (err < 0 && err != -ECONNREFUSED); |
2297 | |
2298 | return err; |
2299 | } |
2300 | |
2301 | /** |
2302 | * iscsi_session_event - send session destr. completion event |
2303 | * @session: iscsi class session |
2304 | * @event: type of event |
2305 | */ |
2306 | int iscsi_session_event(struct iscsi_cls_session *session, |
2307 | enum iscsi_uevent_e event) |
2308 | { |
2309 | struct iscsi_internal *priv; |
2310 | struct Scsi_Host *shost; |
2311 | struct iscsi_uevent *ev; |
2312 | struct sk_buff *skb; |
2313 | struct nlmsghdr *nlh; |
2314 | int rc, len = nlmsg_total_size(sizeof(*ev)); |
2315 | |
2316 | priv = iscsi_if_transport_lookup(session->transport); |
2317 | if (!priv) |
2318 | return -EINVAL; |
2319 | shost = iscsi_session_to_shost(session); |
2320 | |
2321 | skb = alloc_skb(len, GFP_KERNEL); |
2322 | if (!skb) { |
2323 | iscsi_cls_session_printk(KERN_ERR, session, |
2324 | "Cannot notify userspace of session " |
2325 | "event %u\n", event); |
2326 | return -ENOMEM; |
2327 | } |
2328 | |
2329 | nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); |
2330 | ev = nlmsg_data(nlh); |
2331 | ev->transport_handle = iscsi_handle(session->transport); |
2332 | |
2333 | ev->type = event; |
2334 | switch (event) { |
2335 | case ISCSI_KEVENT_DESTROY_SESSION: |
2336 | ev->r.d_session.host_no = shost->host_no; |
2337 | ev->r.d_session.sid = session->sid; |
2338 | break; |
2339 | case ISCSI_KEVENT_CREATE_SESSION: |
2340 | ev->r.c_session_ret.host_no = shost->host_no; |
2341 | ev->r.c_session_ret.sid = session->sid; |
2342 | break; |
2343 | case ISCSI_KEVENT_UNBIND_SESSION: |
2344 | ev->r.unbind_session.host_no = shost->host_no; |
2345 | ev->r.unbind_session.sid = session->sid; |
2346 | break; |
2347 | default: |
2348 | iscsi_cls_session_printk(KERN_ERR, session, "Invalid event " |
2349 | "%u.\n", event); |
2350 | kfree_skb(skb); |
2351 | return -EINVAL; |
2352 | } |
2353 | |
2354 | /* |
2355 | * this will occur if the daemon is not up, so we just warn |
2356 | * the user and when the daemon is restarted it will handle it |
2357 | */ |
2358 | rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL); |
2359 | if (rc == -ESRCH) |
2360 | iscsi_cls_session_printk(KERN_ERR, session, |
2361 | "Cannot notify userspace of session " |
2362 | "event %u. Check iscsi daemon\n", |
2363 | event); |
2364 | |
2365 | ISCSI_DBG_TRANS_SESSION(session, "Completed handling event %d rc %d\n", |
2366 | event, rc); |
2367 | return rc; |
2368 | } |
2369 | EXPORT_SYMBOL_GPL(iscsi_session_event); |
2370 | |
2371 | static int |
2372 | iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep, |
2373 | struct iscsi_uevent *ev, pid_t pid, |
2374 | uint32_t initial_cmdsn, uint16_t cmds_max, |
2375 | uint16_t queue_depth) |
2376 | { |
2377 | struct iscsi_transport *transport = priv->iscsi_transport; |
2378 | struct iscsi_cls_session *session; |
2379 | struct Scsi_Host *shost; |
2380 | |
2381 | session = transport->create_session(ep, cmds_max, queue_depth, |
2382 | initial_cmdsn); |
2383 | if (!session) |
2384 | return -ENOMEM; |
2385 | |
2386 | session->creator = pid; |
2387 | shost = iscsi_session_to_shost(session); |
2388 | ev->r.c_session_ret.host_no = shost->host_no; |
2389 | ev->r.c_session_ret.sid = session->sid; |
2390 | ISCSI_DBG_TRANS_SESSION(session, |
2391 | "Completed creating transport session\n"); |
2392 | return 0; |
2393 | } |
2394 | |
2395 | static int |
2396 | iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
2397 | { |
2398 | struct iscsi_cls_conn *conn; |
2399 | struct iscsi_cls_session *session; |
2400 | |
2401 | session = iscsi_session_lookup(ev->u.c_conn.sid); |
2402 | if (!session) { |
2403 | printk(KERN_ERR "iscsi: invalid session %d.\n", |
2404 | ev->u.c_conn.sid); |
2405 | return -EINVAL; |
2406 | } |
2407 | |
2408 | conn = transport->create_conn(session, ev->u.c_conn.cid); |
2409 | if (!conn) { |
2410 | iscsi_cls_session_printk(KERN_ERR, session, |
2411 | "couldn't create a new connection."); |
2412 | return -ENOMEM; |
2413 | } |
2414 | |
2415 | ev->r.c_conn_ret.sid = session->sid; |
2416 | ev->r.c_conn_ret.cid = conn->cid; |
2417 | |
2418 | ISCSI_DBG_TRANS_CONN(conn, "Completed creating transport conn\n"); |
2419 | return 0; |
2420 | } |
2421 | |
2422 | static int |
2423 | iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
2424 | { |
2425 | struct iscsi_cls_conn *conn; |
2426 | |
2427 | conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid); |
2428 | if (!conn) |
2429 | return -EINVAL; |
2430 | |
2431 | ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n"); |
2432 | if (transport->destroy_conn) |
2433 | transport->destroy_conn(conn); |
2434 | |
2435 | return 0; |
2436 | } |
2437 | |
2438 | static int |
2439 | iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
2440 | { |
2441 | char *data = (char*)ev + sizeof(*ev); |
2442 | struct iscsi_cls_conn *conn; |
2443 | struct iscsi_cls_session *session; |
2444 | int err = 0, value = 0; |
2445 | |
2446 | session = iscsi_session_lookup(ev->u.set_param.sid); |
2447 | conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid); |
2448 | if (!conn || !session) |
2449 | return -EINVAL; |
2450 | |
2451 | switch (ev->u.set_param.param) { |
2452 | case ISCSI_PARAM_SESS_RECOVERY_TMO: |
2453 | sscanf(data, "%d", &value); |
2454 | session->recovery_tmo = value; |
2455 | break; |
2456 | default: |
2457 | err = transport->set_param(conn, ev->u.set_param.param, |
2458 | data, ev->u.set_param.len); |
2459 | } |
2460 | |
2461 | return err; |
2462 | } |
2463 | |
2464 | static int iscsi_if_ep_connect(struct iscsi_transport *transport, |
2465 | struct iscsi_uevent *ev, int msg_type) |
2466 | { |
2467 | struct iscsi_endpoint *ep; |
2468 | struct sockaddr *dst_addr; |
2469 | struct Scsi_Host *shost = NULL; |
2470 | int non_blocking, err = 0; |
2471 | |
2472 | if (!transport->ep_connect) |
2473 | return -EINVAL; |
2474 | |
2475 | if (msg_type == ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST) { |
2476 | shost = scsi_host_lookup(ev->u.ep_connect_through_host.host_no); |
2477 | if (!shost) { |
2478 | printk(KERN_ERR "ep connect failed. Could not find " |
2479 | "host no %u\n", |
2480 | ev->u.ep_connect_through_host.host_no); |
2481 | return -ENODEV; |
2482 | } |
2483 | non_blocking = ev->u.ep_connect_through_host.non_blocking; |
2484 | } else |
2485 | non_blocking = ev->u.ep_connect.non_blocking; |
2486 | |
2487 | dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev)); |
2488 | ep = transport->ep_connect(shost, dst_addr, non_blocking); |
2489 | if (IS_ERR(ep)) { |
2490 | err = PTR_ERR(ep); |
2491 | goto release_host; |
2492 | } |
2493 | |
2494 | ev->r.ep_connect_ret.handle = ep->id; |
2495 | release_host: |
2496 | if (shost) |
2497 | scsi_host_put(shost); |
2498 | return err; |
2499 | } |
2500 | |
2501 | static int iscsi_if_ep_disconnect(struct iscsi_transport *transport, |
2502 | u64 ep_handle) |
2503 | { |
2504 | struct iscsi_cls_conn *conn; |
2505 | struct iscsi_endpoint *ep; |
2506 | |
2507 | if (!transport->ep_disconnect) |
2508 | return -EINVAL; |
2509 | |
2510 | ep = iscsi_lookup_endpoint(ep_handle); |
2511 | if (!ep) |
2512 | return -EINVAL; |
2513 | conn = ep->conn; |
2514 | if (conn) { |
2515 | mutex_lock(&conn->ep_mutex); |
2516 | conn->ep = NULL; |
2517 | mutex_unlock(&conn->ep_mutex); |
2518 | } |
2519 | |
2520 | transport->ep_disconnect(ep); |
2521 | return 0; |
2522 | } |
2523 | |
2524 | static int |
2525 | iscsi_if_transport_ep(struct iscsi_transport *transport, |
2526 | struct iscsi_uevent *ev, int msg_type) |
2527 | { |
2528 | struct iscsi_endpoint *ep; |
2529 | int rc = 0; |
2530 | |
2531 | switch (msg_type) { |
2532 | case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST: |
2533 | case ISCSI_UEVENT_TRANSPORT_EP_CONNECT: |
2534 | rc = iscsi_if_ep_connect(transport, ev, msg_type); |
2535 | break; |
2536 | case ISCSI_UEVENT_TRANSPORT_EP_POLL: |
2537 | if (!transport->ep_poll) |
2538 | return -EINVAL; |
2539 | |
2540 | ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle); |
2541 | if (!ep) |
2542 | return -EINVAL; |
2543 | |
2544 | ev->r.retcode = transport->ep_poll(ep, |
2545 | ev->u.ep_poll.timeout_ms); |
2546 | break; |
2547 | case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: |
2548 | rc = iscsi_if_ep_disconnect(transport, |
2549 | ev->u.ep_disconnect.ep_handle); |
2550 | break; |
2551 | } |
2552 | return rc; |
2553 | } |
2554 | |
2555 | static int |
2556 | iscsi_tgt_dscvr(struct iscsi_transport *transport, |
2557 | struct iscsi_uevent *ev) |
2558 | { |
2559 | struct Scsi_Host *shost; |
2560 | struct sockaddr *dst_addr; |
2561 | int err; |
2562 | |
2563 | if (!transport->tgt_dscvr) |
2564 | return -EINVAL; |
2565 | |
2566 | shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no); |
2567 | if (!shost) { |
2568 | printk(KERN_ERR "target discovery could not find host no %u\n", |
2569 | ev->u.tgt_dscvr.host_no); |
2570 | return -ENODEV; |
2571 | } |
2572 | |
2573 | |
2574 | dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev)); |
2575 | err = transport->tgt_dscvr(shost, ev->u.tgt_dscvr.type, |
2576 | ev->u.tgt_dscvr.enable, dst_addr); |
2577 | scsi_host_put(shost); |
2578 | return err; |
2579 | } |
2580 | |
2581 | static int |
2582 | iscsi_set_host_param(struct iscsi_transport *transport, |
2583 | struct iscsi_uevent *ev) |
2584 | { |
2585 | char *data = (char*)ev + sizeof(*ev); |
2586 | struct Scsi_Host *shost; |
2587 | int err; |
2588 | |
2589 | if (!transport->set_host_param) |
2590 | return -ENOSYS; |
2591 | |
2592 | shost = scsi_host_lookup(ev->u.set_host_param.host_no); |
2593 | if (!shost) { |
2594 | printk(KERN_ERR "set_host_param could not find host no %u\n", |
2595 | ev->u.set_host_param.host_no); |
2596 | return -ENODEV; |
2597 | } |
2598 | |
2599 | err = transport->set_host_param(shost, ev->u.set_host_param.param, |
2600 | data, ev->u.set_host_param.len); |
2601 | scsi_host_put(shost); |
2602 | return err; |
2603 | } |
2604 | |
2605 | static int |
2606 | iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
2607 | { |
2608 | struct Scsi_Host *shost; |
2609 | struct iscsi_path *params; |
2610 | int err; |
2611 | |
2612 | if (!transport->set_path) |
2613 | return -ENOSYS; |
2614 | |
2615 | shost = scsi_host_lookup(ev->u.set_path.host_no); |
2616 | if (!shost) { |
2617 | printk(KERN_ERR "set path could not find host no %u\n", |
2618 | ev->u.set_path.host_no); |
2619 | return -ENODEV; |
2620 | } |
2621 | |
2622 | params = (struct iscsi_path *)((char *)ev + sizeof(*ev)); |
2623 | err = transport->set_path(shost, params); |
2624 | |
2625 | scsi_host_put(shost); |
2626 | return err; |
2627 | } |
2628 | |
2629 | static int |
2630 | iscsi_set_iface_params(struct iscsi_transport *transport, |
2631 | struct iscsi_uevent *ev, uint32_t len) |
2632 | { |
2633 | char *data = (char *)ev + sizeof(*ev); |
2634 | struct Scsi_Host *shost; |
2635 | int err; |
2636 | |
2637 | if (!transport->set_iface_param) |
2638 | return -ENOSYS; |
2639 | |
2640 | shost = scsi_host_lookup(ev->u.set_iface_params.host_no); |
2641 | if (!shost) { |
2642 | printk(KERN_ERR "set_iface_params could not find host no %u\n", |
2643 | ev->u.set_iface_params.host_no); |
2644 | return -ENODEV; |
2645 | } |
2646 | |
2647 | err = transport->set_iface_param(shost, data, len); |
2648 | scsi_host_put(shost); |
2649 | return err; |
2650 | } |
2651 | |
2652 | static int |
2653 | iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
2654 | { |
2655 | struct Scsi_Host *shost; |
2656 | struct sockaddr *dst_addr; |
2657 | int err; |
2658 | |
2659 | if (!transport->send_ping) |
2660 | return -ENOSYS; |
2661 | |
2662 | shost = scsi_host_lookup(ev->u.iscsi_ping.host_no); |
2663 | if (!shost) { |
2664 | printk(KERN_ERR "iscsi_ping could not find host no %u\n", |
2665 | ev->u.iscsi_ping.host_no); |
2666 | return -ENODEV; |
2667 | } |
2668 | |
2669 | dst_addr = (struct sockaddr *)((char *)ev + sizeof(*ev)); |
2670 | err = transport->send_ping(shost, ev->u.iscsi_ping.iface_num, |
2671 | ev->u.iscsi_ping.iface_type, |
2672 | ev->u.iscsi_ping.payload_size, |
2673 | ev->u.iscsi_ping.pid, |
2674 | dst_addr); |
2675 | scsi_host_put(shost); |
2676 | return err; |
2677 | } |
2678 | |
2679 | static int |
2680 | iscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh) |
2681 | { |
2682 | struct iscsi_uevent *ev = nlmsg_data(nlh); |
2683 | struct Scsi_Host *shost = NULL; |
2684 | struct iscsi_chap_rec *chap_rec; |
2685 | struct iscsi_internal *priv; |
2686 | struct sk_buff *skbchap; |
2687 | struct nlmsghdr *nlhchap; |
2688 | struct iscsi_uevent *evchap; |
2689 | uint32_t chap_buf_size; |
2690 | int len, err = 0; |
2691 | char *buf; |
2692 | |
2693 | if (!transport->get_chap) |
2694 | return -EINVAL; |
2695 | |
2696 | priv = iscsi_if_transport_lookup(transport); |
2697 | if (!priv) |
2698 | return -EINVAL; |
2699 | |
2700 | chap_buf_size = (ev->u.get_chap.num_entries * sizeof(*chap_rec)); |
2701 | len = nlmsg_total_size(sizeof(*ev) + chap_buf_size); |
2702 | |
2703 | shost = scsi_host_lookup(ev->u.get_chap.host_no); |
2704 | if (!shost) { |
2705 | printk(KERN_ERR "%s: failed. Cound not find host no %u\n", |
2706 | __func__, ev->u.get_chap.host_no); |
2707 | return -ENODEV; |
2708 | } |
2709 | |
2710 | do { |
2711 | int actual_size; |
2712 | |
2713 | skbchap = alloc_skb(len, GFP_KERNEL); |
2714 | if (!skbchap) { |
2715 | printk(KERN_ERR "can not deliver chap: OOM\n"); |
2716 | err = -ENOMEM; |
2717 | goto exit_get_chap; |
2718 | } |
2719 | |
2720 | nlhchap = __nlmsg_put(skbchap, 0, 0, 0, |
2721 | (len - sizeof(*nlhchap)), 0); |
2722 | evchap = nlmsg_data(nlhchap); |
2723 | memset(evchap, 0, sizeof(*evchap)); |
2724 | evchap->transport_handle = iscsi_handle(transport); |
2725 | evchap->type = nlh->nlmsg_type; |
2726 | evchap->u.get_chap.host_no = ev->u.get_chap.host_no; |
2727 | evchap->u.get_chap.chap_tbl_idx = ev->u.get_chap.chap_tbl_idx; |
2728 | evchap->u.get_chap.num_entries = ev->u.get_chap.num_entries; |
2729 | buf = (char *) ((char *)evchap + sizeof(*evchap)); |
2730 | memset(buf, 0, chap_buf_size); |
2731 | |
2732 | err = transport->get_chap(shost, ev->u.get_chap.chap_tbl_idx, |
2733 | &evchap->u.get_chap.num_entries, buf); |
2734 | |
2735 | actual_size = nlmsg_total_size(sizeof(*ev) + chap_buf_size); |
2736 | skb_trim(skbchap, NLMSG_ALIGN(actual_size)); |
2737 | nlhchap->nlmsg_len = actual_size; |
2738 | |
2739 | err = iscsi_multicast_skb(skbchap, ISCSI_NL_GRP_ISCSID, |
2740 | GFP_KERNEL); |
2741 | } while (err < 0 && err != -ECONNREFUSED); |
2742 | |
2743 | exit_get_chap: |
2744 | scsi_host_put(shost); |
2745 | return err; |
2746 | } |
2747 | |
2748 | static int iscsi_delete_chap(struct iscsi_transport *transport, |
2749 | struct iscsi_uevent *ev) |
2750 | { |
2751 | struct Scsi_Host *shost; |
2752 | int err = 0; |
2753 | |
2754 | if (!transport->delete_chap) |
2755 | return -ENOSYS; |
2756 | |
2757 | shost = scsi_host_lookup(ev->u.delete_chap.host_no); |
2758 | if (!shost) { |
2759 | printk(KERN_ERR "%s could not find host no %u\n", |
2760 | __func__, ev->u.delete_chap.host_no); |
2761 | return -ENODEV; |
2762 | } |
2763 | |
2764 | err = transport->delete_chap(shost, ev->u.delete_chap.chap_tbl_idx); |
2765 | scsi_host_put(shost); |
2766 | return err; |
2767 | } |
2768 | |
2769 | static const struct { |
2770 | enum iscsi_discovery_parent_type value; |
2771 | char *name; |
2772 | } iscsi_discovery_parent_names[] = { |
2773 | {ISCSI_DISC_PARENT_UNKNOWN, "Unknown" }, |
2774 | {ISCSI_DISC_PARENT_SENDTGT, "Sendtarget" }, |
2775 | {ISCSI_DISC_PARENT_ISNS, "isns" }, |
2776 | }; |
2777 | |
2778 | char *iscsi_get_discovery_parent_name(int parent_type) |
2779 | { |
2780 | int i; |
2781 | char *state = "Unknown!"; |
2782 | |
2783 | for (i = 0; i < ARRAY_SIZE(iscsi_discovery_parent_names); i++) { |
2784 | if (iscsi_discovery_parent_names[i].value & parent_type) { |
2785 | state = iscsi_discovery_parent_names[i].name; |
2786 | break; |
2787 | } |
2788 | } |
2789 | return state; |
2790 | } |
2791 | EXPORT_SYMBOL_GPL(iscsi_get_discovery_parent_name); |
2792 | |
2793 | static int iscsi_set_flashnode_param(struct iscsi_transport *transport, |
2794 | struct iscsi_uevent *ev, uint32_t len) |
2795 | { |
2796 | char *data = (char *)ev + sizeof(*ev); |
2797 | struct Scsi_Host *shost; |
2798 | struct iscsi_bus_flash_session *fnode_sess; |
2799 | struct iscsi_bus_flash_conn *fnode_conn; |
2800 | struct device *dev; |
2801 | uint32_t idx; |
2802 | int err = 0; |
2803 | |
2804 | if (!transport->set_flashnode_param) { |
2805 | err = -ENOSYS; |
2806 | goto exit_set_fnode; |
2807 | } |
2808 | |
2809 | shost = scsi_host_lookup(ev->u.set_flashnode.host_no); |
2810 | if (!shost) { |
2811 | pr_err("%s could not find host no %u\n", |
2812 | __func__, ev->u.set_flashnode.host_no); |
2813 | err = -ENODEV; |
2814 | goto put_host; |
2815 | } |
2816 | |
2817 | idx = ev->u.set_flashnode.flashnode_idx; |
2818 | fnode_sess = iscsi_get_flashnode_by_index(shost, idx); |
2819 | if (!fnode_sess) { |
2820 | pr_err("%s could not find flashnode %u for host no %u\n", |
2821 | __func__, idx, ev->u.set_flashnode.host_no); |
2822 | err = -ENODEV; |
2823 | goto put_host; |
2824 | } |
2825 | |
2826 | dev = iscsi_find_flashnode_conn(fnode_sess); |
2827 | if (!dev) { |
2828 | err = -ENODEV; |
2829 | goto put_sess; |
2830 | } |
2831 | |
2832 | fnode_conn = iscsi_dev_to_flash_conn(dev); |
2833 | err = transport->set_flashnode_param(fnode_sess, fnode_conn, data, len); |
2834 | put_device(dev); |
2835 | |
2836 | put_sess: |
2837 | put_device(&fnode_sess->dev); |
2838 | |
2839 | put_host: |
2840 | scsi_host_put(shost); |
2841 | |
2842 | exit_set_fnode: |
2843 | return err; |
2844 | } |
2845 | |
2846 | static int iscsi_new_flashnode(struct iscsi_transport *transport, |
2847 | struct iscsi_uevent *ev, uint32_t len) |
2848 | { |
2849 | char *data = (char *)ev + sizeof(*ev); |
2850 | struct Scsi_Host *shost; |
2851 | int index; |
2852 | int err = 0; |
2853 | |
2854 | if (!transport->new_flashnode) { |
2855 | err = -ENOSYS; |
2856 | goto exit_new_fnode; |
2857 | } |
2858 | |
2859 | shost = scsi_host_lookup(ev->u.new_flashnode.host_no); |
2860 | if (!shost) { |
2861 | pr_err("%s could not find host no %u\n", |
2862 | __func__, ev->u.new_flashnode.host_no); |
2863 | err = -ENODEV; |
2864 | goto put_host; |
2865 | } |
2866 | |
2867 | index = transport->new_flashnode(shost, data, len); |
2868 | |
2869 | if (index >= 0) |
2870 | ev->r.new_flashnode_ret.flashnode_idx = index; |
2871 | else |
2872 | err = -EIO; |
2873 | |
2874 | put_host: |
2875 | scsi_host_put(shost); |
2876 | |
2877 | exit_new_fnode: |
2878 | return err; |
2879 | } |
2880 | |
2881 | static int iscsi_del_flashnode(struct iscsi_transport *transport, |
2882 | struct iscsi_uevent *ev) |
2883 | { |
2884 | struct Scsi_Host *shost; |
2885 | struct iscsi_bus_flash_session *fnode_sess; |
2886 | uint32_t idx; |
2887 | int err = 0; |
2888 | |
2889 | if (!transport->del_flashnode) { |
2890 | err = -ENOSYS; |
2891 | goto exit_del_fnode; |
2892 | } |
2893 | |
2894 | shost = scsi_host_lookup(ev->u.del_flashnode.host_no); |
2895 | if (!shost) { |
2896 | pr_err("%s could not find host no %u\n", |
2897 | __func__, ev->u.del_flashnode.host_no); |
2898 | err = -ENODEV; |
2899 | goto put_host; |
2900 | } |
2901 | |
2902 | idx = ev->u.del_flashnode.flashnode_idx; |
2903 | fnode_sess = iscsi_get_flashnode_by_index(shost, idx); |
2904 | if (!fnode_sess) { |
2905 | pr_err("%s could not find flashnode %u for host no %u\n", |
2906 | __func__, idx, ev->u.del_flashnode.host_no); |
2907 | err = -ENODEV; |
2908 | goto put_host; |
2909 | } |
2910 | |
2911 | err = transport->del_flashnode(fnode_sess); |
2912 | put_device(&fnode_sess->dev); |
2913 | |
2914 | put_host: |
2915 | scsi_host_put(shost); |
2916 | |
2917 | exit_del_fnode: |
2918 | return err; |
2919 | } |
2920 | |
2921 | static int iscsi_login_flashnode(struct iscsi_transport *transport, |
2922 | struct iscsi_uevent *ev) |
2923 | { |
2924 | struct Scsi_Host *shost; |
2925 | struct iscsi_bus_flash_session *fnode_sess; |
2926 | struct iscsi_bus_flash_conn *fnode_conn; |
2927 | struct device *dev; |
2928 | uint32_t idx; |
2929 | int err = 0; |
2930 | |
2931 | if (!transport->login_flashnode) { |
2932 | err = -ENOSYS; |
2933 | goto exit_login_fnode; |
2934 | } |
2935 | |
2936 | shost = scsi_host_lookup(ev->u.login_flashnode.host_no); |
2937 | if (!shost) { |
2938 | pr_err("%s could not find host no %u\n", |
2939 | __func__, ev->u.login_flashnode.host_no); |
2940 | err = -ENODEV; |
2941 | goto put_host; |
2942 | } |
2943 | |
2944 | idx = ev->u.login_flashnode.flashnode_idx; |
2945 | fnode_sess = iscsi_get_flashnode_by_index(shost, idx); |
2946 | if (!fnode_sess) { |
2947 | pr_err("%s could not find flashnode %u for host no %u\n", |
2948 | __func__, idx, ev->u.login_flashnode.host_no); |
2949 | err = -ENODEV; |
2950 | goto put_host; |
2951 | } |
2952 | |
2953 | dev = iscsi_find_flashnode_conn(fnode_sess); |
2954 | if (!dev) { |
2955 | err = -ENODEV; |
2956 | goto put_sess; |
2957 | } |
2958 | |
2959 | fnode_conn = iscsi_dev_to_flash_conn(dev); |
2960 | err = transport->login_flashnode(fnode_sess, fnode_conn); |
2961 | put_device(dev); |
2962 | |
2963 | put_sess: |
2964 | put_device(&fnode_sess->dev); |
2965 | |
2966 | put_host: |
2967 | scsi_host_put(shost); |
2968 | |
2969 | exit_login_fnode: |
2970 | return err; |
2971 | } |
2972 | |
2973 | static int iscsi_logout_flashnode(struct iscsi_transport *transport, |
2974 | struct iscsi_uevent *ev) |
2975 | { |
2976 | struct Scsi_Host *shost; |
2977 | struct iscsi_bus_flash_session *fnode_sess; |
2978 | struct iscsi_bus_flash_conn *fnode_conn; |
2979 | struct device *dev; |
2980 | uint32_t idx; |
2981 | int err = 0; |
2982 | |
2983 | if (!transport->logout_flashnode) { |
2984 | err = -ENOSYS; |
2985 | goto exit_logout_fnode; |
2986 | } |
2987 | |
2988 | shost = scsi_host_lookup(ev->u.logout_flashnode.host_no); |
2989 | if (!shost) { |
2990 | pr_err("%s could not find host no %u\n", |
2991 | __func__, ev->u.logout_flashnode.host_no); |
2992 | err = -ENODEV; |
2993 | goto put_host; |
2994 | } |
2995 | |
2996 | idx = ev->u.logout_flashnode.flashnode_idx; |
2997 | fnode_sess = iscsi_get_flashnode_by_index(shost, idx); |
2998 | if (!fnode_sess) { |
2999 | pr_err("%s could not find flashnode %u for host no %u\n", |
3000 | __func__, idx, ev->u.logout_flashnode.host_no); |
3001 | err = -ENODEV; |
3002 | goto put_host; |
3003 | } |
3004 | |
3005 | dev = iscsi_find_flashnode_conn(fnode_sess); |
3006 | if (!dev) { |
3007 | err = -ENODEV; |
3008 | goto put_sess; |
3009 | } |
3010 | |
3011 | fnode_conn = iscsi_dev_to_flash_conn(dev); |
3012 | |
3013 | err = transport->logout_flashnode(fnode_sess, fnode_conn); |
3014 | put_device(dev); |
3015 | |
3016 | put_sess: |
3017 | put_device(&fnode_sess->dev); |
3018 | |
3019 | put_host: |
3020 | scsi_host_put(shost); |
3021 | |
3022 | exit_logout_fnode: |
3023 | return err; |
3024 | } |
3025 | |
3026 | static int iscsi_logout_flashnode_sid(struct iscsi_transport *transport, |
3027 | struct iscsi_uevent *ev) |
3028 | { |
3029 | struct Scsi_Host *shost; |
3030 | struct iscsi_cls_session *session; |
3031 | int err = 0; |
3032 | |
3033 | if (!transport->logout_flashnode_sid) { |
3034 | err = -ENOSYS; |
3035 | goto exit_logout_sid; |
3036 | } |
3037 | |
3038 | shost = scsi_host_lookup(ev->u.logout_flashnode_sid.host_no); |
3039 | if (!shost) { |
3040 | pr_err("%s could not find host no %u\n", |
3041 | __func__, ev->u.logout_flashnode.host_no); |
3042 | err = -ENODEV; |
3043 | goto put_host; |
3044 | } |
3045 | |
3046 | session = iscsi_session_lookup(ev->u.logout_flashnode_sid.sid); |
3047 | if (!session) { |
3048 | pr_err("%s could not find session id %u\n", |
3049 | __func__, ev->u.logout_flashnode_sid.sid); |
3050 | err = -EINVAL; |
3051 | goto put_host; |
3052 | } |
3053 | |
3054 | err = transport->logout_flashnode_sid(session); |
3055 | |
3056 | put_host: |
3057 | scsi_host_put(shost); |
3058 | |
3059 | exit_logout_sid: |
3060 | return err; |
3061 | } |
3062 | |
3063 | static int |
3064 | iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) |
3065 | { |
3066 | int err = 0; |
3067 | struct iscsi_uevent *ev = nlmsg_data(nlh); |
3068 | struct iscsi_transport *transport = NULL; |
3069 | struct iscsi_internal *priv; |
3070 | struct iscsi_cls_session *session; |
3071 | struct iscsi_cls_conn *conn; |
3072 | struct iscsi_endpoint *ep = NULL; |
3073 | |
3074 | if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE) |
3075 | *group = ISCSI_NL_GRP_UIP; |
3076 | else |
3077 | *group = ISCSI_NL_GRP_ISCSID; |
3078 | |
3079 | priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle)); |
3080 | if (!priv) |
3081 | return -EINVAL; |
3082 | transport = priv->iscsi_transport; |
3083 | |
3084 | if (!try_module_get(transport->owner)) |
3085 | return -EINVAL; |
3086 | |
3087 | switch (nlh->nlmsg_type) { |
3088 | case ISCSI_UEVENT_CREATE_SESSION: |
3089 | err = iscsi_if_create_session(priv, ep, ev, |
3090 | NETLINK_CB(skb).portid, |
3091 | ev->u.c_session.initial_cmdsn, |
3092 | ev->u.c_session.cmds_max, |
3093 | ev->u.c_session.queue_depth); |
3094 | break; |
3095 | case ISCSI_UEVENT_CREATE_BOUND_SESSION: |
3096 | ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle); |
3097 | if (!ep) { |
3098 | err = -EINVAL; |
3099 | break; |
3100 | } |
3101 | |
3102 | err = iscsi_if_create_session(priv, ep, ev, |
3103 | NETLINK_CB(skb).portid, |
3104 | ev->u.c_bound_session.initial_cmdsn, |
3105 | ev->u.c_bound_session.cmds_max, |
3106 | ev->u.c_bound_session.queue_depth); |
3107 | break; |
3108 | case ISCSI_UEVENT_DESTROY_SESSION: |
3109 | session = iscsi_session_lookup(ev->u.d_session.sid); |
3110 | if (session) |
3111 | transport->destroy_session(session); |
3112 | else |
3113 | err = -EINVAL; |
3114 | break; |
3115 | case ISCSI_UEVENT_UNBIND_SESSION: |
3116 | session = iscsi_session_lookup(ev->u.d_session.sid); |
3117 | if (session) |
3118 | scsi_queue_work(iscsi_session_to_shost(session), |
3119 | &session->unbind_work); |
3120 | else |
3121 | err = -EINVAL; |
3122 | break; |
3123 | case ISCSI_UEVENT_CREATE_CONN: |
3124 | err = iscsi_if_create_conn(transport, ev); |
3125 | break; |
3126 | case ISCSI_UEVENT_DESTROY_CONN: |
3127 | err = iscsi_if_destroy_conn(transport, ev); |
3128 | break; |
3129 | case ISCSI_UEVENT_BIND_CONN: |
3130 | session = iscsi_session_lookup(ev->u.b_conn.sid); |
3131 | conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid); |
3132 | |
3133 | if (conn && conn->ep) |
3134 | iscsi_if_ep_disconnect(transport, conn->ep->id); |
3135 | |
3136 | if (!session || !conn) { |
3137 | err = -EINVAL; |
3138 | break; |
3139 | } |
3140 | |
3141 | ev->r.retcode = transport->bind_conn(session, conn, |
3142 | ev->u.b_conn.transport_eph, |
3143 | ev->u.b_conn.is_leading); |
3144 | if (ev->r.retcode || !transport->ep_connect) |
3145 | break; |
3146 | |
3147 | ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph); |
3148 | if (ep) { |
3149 | ep->conn = conn; |
3150 | |
3151 | mutex_lock(&conn->ep_mutex); |
3152 | conn->ep = ep; |
3153 | mutex_unlock(&conn->ep_mutex); |
3154 | } else |
3155 | iscsi_cls_conn_printk(KERN_ERR, conn, |
3156 | "Could not set ep conn " |
3157 | "binding\n"); |
3158 | break; |
3159 | case ISCSI_UEVENT_SET_PARAM: |
3160 | err = iscsi_set_param(transport, ev); |
3161 | break; |
3162 | case ISCSI_UEVENT_START_CONN: |
3163 | conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid); |
3164 | if (conn) |
3165 | ev->r.retcode = transport->start_conn(conn); |
3166 | else |
3167 | err = -EINVAL; |
3168 | break; |
3169 | case ISCSI_UEVENT_STOP_CONN: |
3170 | conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid); |
3171 | if (conn) |
3172 | transport->stop_conn(conn, ev->u.stop_conn.flag); |
3173 | else |
3174 | err = -EINVAL; |
3175 | break; |
3176 | case ISCSI_UEVENT_SEND_PDU: |
3177 | conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid); |
3178 | if (conn) |
3179 | ev->r.retcode = transport->send_pdu(conn, |
3180 | (struct iscsi_hdr*)((char*)ev + sizeof(*ev)), |
3181 | (char*)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size, |
3182 | ev->u.send_pdu.data_size); |
3183 | else |
3184 | err = -EINVAL; |
3185 | break; |
3186 | case ISCSI_UEVENT_GET_STATS: |
3187 | err = iscsi_if_get_stats(transport, nlh); |
3188 | break; |
3189 | case ISCSI_UEVENT_TRANSPORT_EP_CONNECT: |
3190 | case ISCSI_UEVENT_TRANSPORT_EP_POLL: |
3191 | case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: |
3192 | case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST: |
3193 | err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type); |
3194 | break; |
3195 | case ISCSI_UEVENT_TGT_DSCVR: |
3196 | err = iscsi_tgt_dscvr(transport, ev); |
3197 | break; |
3198 | case ISCSI_UEVENT_SET_HOST_PARAM: |
3199 | err = iscsi_set_host_param(transport, ev); |
3200 | break; |
3201 | case ISCSI_UEVENT_PATH_UPDATE: |
3202 | err = iscsi_set_path(transport, ev); |
3203 | break; |
3204 | case ISCSI_UEVENT_SET_IFACE_PARAMS: |
3205 | err = iscsi_set_iface_params(transport, ev, |
3206 | nlmsg_attrlen(nlh, sizeof(*ev))); |
3207 | break; |
3208 | case ISCSI_UEVENT_PING: |
3209 | err = iscsi_send_ping(transport, ev); |
3210 | break; |
3211 | case ISCSI_UEVENT_GET_CHAP: |
3212 | err = iscsi_get_chap(transport, nlh); |
3213 | break; |
3214 | case ISCSI_UEVENT_DELETE_CHAP: |
3215 | err = iscsi_delete_chap(transport, ev); |
3216 | break; |
3217 | case ISCSI_UEVENT_SET_FLASHNODE_PARAMS: |
3218 | err = iscsi_set_flashnode_param(transport, ev, |
3219 | nlmsg_attrlen(nlh, |
3220 | sizeof(*ev))); |
3221 | break; |
3222 | case ISCSI_UEVENT_NEW_FLASHNODE: |
3223 | err = iscsi_new_flashnode(transport, ev, |
3224 | nlmsg_attrlen(nlh, sizeof(*ev))); |
3225 | break; |
3226 | case ISCSI_UEVENT_DEL_FLASHNODE: |
3227 | err = iscsi_del_flashnode(transport, ev); |
3228 | break; |
3229 | case ISCSI_UEVENT_LOGIN_FLASHNODE: |
3230 | err = iscsi_login_flashnode(transport, ev); |
3231 | break; |
3232 | case ISCSI_UEVENT_LOGOUT_FLASHNODE: |
3233 | err = iscsi_logout_flashnode(transport, ev); |
3234 | break; |
3235 | case ISCSI_UEVENT_LOGOUT_FLASHNODE_SID: |
3236 | err = iscsi_logout_flashnode_sid(transport, ev); |
3237 | break; |
3238 | default: |
3239 | err = -ENOSYS; |
3240 | break; |
3241 | } |
3242 | |
3243 | module_put(transport->owner); |
3244 | return err; |
3245 | } |
3246 | |
3247 | /* |
3248 | * Get message from skb. Each message is processed by iscsi_if_recv_msg. |
3249 | * Malformed skbs with wrong lengths or invalid creds are not processed. |
3250 | */ |
3251 | static void |
3252 | iscsi_if_rx(struct sk_buff *skb) |
3253 | { |
3254 | mutex_lock(&rx_queue_mutex); |
3255 | while (skb->len >= NLMSG_HDRLEN) { |
3256 | int err; |
3257 | uint32_t rlen; |
3258 | struct nlmsghdr *nlh; |
3259 | struct iscsi_uevent *ev; |
3260 | uint32_t group; |
3261 | |
3262 | nlh = nlmsg_hdr(skb); |
3263 | if (nlh->nlmsg_len < sizeof(*nlh) || |
3264 | skb->len < nlh->nlmsg_len) { |
3265 | break; |
3266 | } |
3267 | |
3268 | ev = nlmsg_data(nlh); |
3269 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); |
3270 | if (rlen > skb->len) |
3271 | rlen = skb->len; |
3272 | |
3273 | err = iscsi_if_recv_msg(skb, nlh, &group); |
3274 | if (err) { |
3275 | ev->type = ISCSI_KEVENT_IF_ERROR; |
3276 | ev->iferror = err; |
3277 | } |
3278 | do { |
3279 | /* |
3280 | * special case for GET_STATS: |
3281 | * on success - sending reply and stats from |
3282 | * inside of if_recv_msg(), |
3283 | * on error - fall through. |
3284 | */ |
3285 | if (ev->type == ISCSI_UEVENT_GET_STATS && !err) |
3286 | break; |
3287 | if (ev->type == ISCSI_UEVENT_GET_CHAP && !err) |
3288 | break; |
3289 | err = iscsi_if_send_reply(group, nlh->nlmsg_seq, |
3290 | nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); |
3291 | } while (err < 0 && err != -ECONNREFUSED && err != -ESRCH); |
3292 | skb_pull(skb, rlen); |
3293 | } |
3294 | mutex_unlock(&rx_queue_mutex); |
3295 | } |
3296 | |
3297 | #define ISCSI_CLASS_ATTR(_prefix,_name,_mode,_show,_store) \ |
3298 | struct device_attribute dev_attr_##_prefix##_##_name = \ |
3299 | __ATTR(_name,_mode,_show,_store) |
3300 | |
3301 | /* |
3302 | * iSCSI connection attrs |
3303 | */ |
3304 | #define iscsi_conn_attr_show(param) \ |
3305 | static ssize_t \ |
3306 | show_conn_param_##param(struct device *dev, \ |
3307 | struct device_attribute *attr, char *buf) \ |
3308 | { \ |
3309 | struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); \ |
3310 | struct iscsi_transport *t = conn->transport; \ |
3311 | return t->get_conn_param(conn, param, buf); \ |
3312 | } |
3313 | |
3314 | #define iscsi_conn_attr(field, param) \ |
3315 | iscsi_conn_attr_show(param) \ |
3316 | static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_param_##param, \ |
3317 | NULL); |
3318 | |
3319 | iscsi_conn_attr(max_recv_dlength, ISCSI_PARAM_MAX_RECV_DLENGTH); |
3320 | iscsi_conn_attr(max_xmit_dlength, ISCSI_PARAM_MAX_XMIT_DLENGTH); |
3321 | iscsi_conn_attr(header_digest, ISCSI_PARAM_HDRDGST_EN); |
3322 | iscsi_conn_attr(data_digest, ISCSI_PARAM_DATADGST_EN); |
3323 | iscsi_conn_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN); |
3324 | iscsi_conn_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN); |
3325 | iscsi_conn_attr(persistent_port, ISCSI_PARAM_PERSISTENT_PORT); |
3326 | iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN); |
3327 | iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS); |
3328 | iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO); |
3329 | iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO); |
3330 | |
3331 | #define iscsi_conn_ep_attr_show(param) \ |
3332 | static ssize_t show_conn_ep_param_##param(struct device *dev, \ |
3333 | struct device_attribute *attr,\ |
3334 | char *buf) \ |
3335 | { \ |
3336 | struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); \ |
3337 | struct iscsi_transport *t = conn->transport; \ |
3338 | struct iscsi_endpoint *ep; \ |
3339 | ssize_t rc; \ |
3340 | \ |
3341 | /* \ |
3342 | * Need to make sure ep_disconnect does not free the LLD's \ |
3343 | * interconnect resources while we are trying to read them. \ |
3344 | */ \ |
3345 | mutex_lock(&conn->ep_mutex); \ |
3346 | ep = conn->ep; \ |
3347 | if (!ep && t->ep_connect) { \ |
3348 | mutex_unlock(&conn->ep_mutex); \ |
3349 | return -ENOTCONN; \ |
3350 | } \ |
3351 | \ |
3352 | if (ep) \ |
3353 | rc = t->get_ep_param(ep, param, buf); \ |
3354 | else \ |
3355 | rc = t->get_conn_param(conn, param, buf); \ |
3356 | mutex_unlock(&conn->ep_mutex); \ |
3357 | return rc; \ |
3358 | } |
3359 | |
3360 | #define iscsi_conn_ep_attr(field, param) \ |
3361 | iscsi_conn_ep_attr_show(param) \ |
3362 | static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, \ |
3363 | show_conn_ep_param_##param, NULL); |
3364 | |
3365 | iscsi_conn_ep_attr(address, ISCSI_PARAM_CONN_ADDRESS); |
3366 | iscsi_conn_ep_attr(port, ISCSI_PARAM_CONN_PORT); |
3367 | |
3368 | static struct attribute *iscsi_conn_attrs[] = { |
3369 | &dev_attr_conn_max_recv_dlength.attr, |
3370 | &dev_attr_conn_max_xmit_dlength.attr, |
3371 | &dev_attr_conn_header_digest.attr, |
3372 | &dev_attr_conn_data_digest.attr, |
3373 | &dev_attr_conn_ifmarker.attr, |
3374 | &dev_attr_conn_ofmarker.attr, |
3375 | &dev_attr_conn_address.attr, |
3376 | &dev_attr_conn_port.attr, |
3377 | &dev_attr_conn_exp_statsn.attr, |
3378 | &dev_attr_conn_persistent_address.attr, |
3379 | &dev_attr_conn_persistent_port.attr, |
3380 | &dev_attr_conn_ping_tmo.attr, |
3381 | &dev_attr_conn_recv_tmo.attr, |
3382 | NULL, |
3383 | }; |
3384 | |
3385 | static umode_t iscsi_conn_attr_is_visible(struct kobject *kobj, |
3386 | struct attribute *attr, int i) |
3387 | { |
3388 | struct device *cdev = container_of(kobj, struct device, kobj); |
3389 | struct iscsi_cls_conn *conn = transport_class_to_conn(cdev); |
3390 | struct iscsi_transport *t = conn->transport; |
3391 | int param; |
3392 | |
3393 | if (attr == &dev_attr_conn_max_recv_dlength.attr) |
3394 | param = ISCSI_PARAM_MAX_RECV_DLENGTH; |
3395 | else if (attr == &dev_attr_conn_max_xmit_dlength.attr) |
3396 | param = ISCSI_PARAM_MAX_XMIT_DLENGTH; |
3397 | else if (attr == &dev_attr_conn_header_digest.attr) |
3398 | param = ISCSI_PARAM_HDRDGST_EN; |
3399 | else if (attr == &dev_attr_conn_data_digest.attr) |
3400 | param = ISCSI_PARAM_DATADGST_EN; |
3401 | else if (attr == &dev_attr_conn_ifmarker.attr) |
3402 | param = ISCSI_PARAM_IFMARKER_EN; |
3403 | else if (attr == &dev_attr_conn_ofmarker.attr) |
3404 | param = ISCSI_PARAM_OFMARKER_EN; |
3405 | else if (attr == &dev_attr_conn_address.attr) |
3406 | param = ISCSI_PARAM_CONN_ADDRESS; |
3407 | else if (attr == &dev_attr_conn_port.attr) |
3408 | param = ISCSI_PARAM_CONN_PORT; |
3409 | else if (attr == &dev_attr_conn_exp_statsn.attr) |
3410 | param = ISCSI_PARAM_EXP_STATSN; |
3411 | else if (attr == &dev_attr_conn_persistent_address.attr) |
3412 | param = ISCSI_PARAM_PERSISTENT_ADDRESS; |
3413 | else if (attr == &dev_attr_conn_persistent_port.attr) |
3414 | param = ISCSI_PARAM_PERSISTENT_PORT; |
3415 | else if (attr == &dev_attr_conn_ping_tmo.attr) |
3416 | param = ISCSI_PARAM_PING_TMO; |
3417 | else if (attr == &dev_attr_conn_recv_tmo.attr) |
3418 | param = ISCSI_PARAM_RECV_TMO; |
3419 | else { |
3420 | WARN_ONCE(1, "Invalid conn attr"); |
3421 | return 0; |
3422 | } |
3423 | |
3424 | return t->attr_is_visible(ISCSI_PARAM, param); |
3425 | } |
3426 | |
3427 | static struct attribute_group iscsi_conn_group = { |
3428 | .attrs = iscsi_conn_attrs, |
3429 | .is_visible = iscsi_conn_attr_is_visible, |
3430 | }; |
3431 | |
3432 | /* |
3433 | * iSCSI session attrs |
3434 | */ |
3435 | #define iscsi_session_attr_show(param, perm) \ |
3436 | static ssize_t \ |
3437 | show_session_param_##param(struct device *dev, \ |
3438 | struct device_attribute *attr, char *buf) \ |
3439 | { \ |
3440 | struct iscsi_cls_session *session = \ |
3441 | iscsi_dev_to_session(dev->parent); \ |
3442 | struct iscsi_transport *t = session->transport; \ |
3443 | \ |
3444 | if (perm && !capable(CAP_SYS_ADMIN)) \ |
3445 | return -EACCES; \ |
3446 | return t->get_session_param(session, param, buf); \ |
3447 | } |
3448 | |
3449 | #define iscsi_session_attr(field, param, perm) \ |
3450 | iscsi_session_attr_show(param, perm) \ |
3451 | static ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_param_##param, \ |
3452 | NULL); |
3453 | iscsi_session_attr(targetname, ISCSI_PARAM_TARGET_NAME, 0); |
3454 | iscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN, 0); |
3455 | iscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T, 0); |
3456 | iscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN, 0); |
3457 | iscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST, 0); |
3458 | iscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST, 0); |
3459 | iscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN, 0); |
3460 | iscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN, 0); |
3461 | iscsi_session_attr(erl, ISCSI_PARAM_ERL, 0); |
3462 | iscsi_session_attr(tpgt, ISCSI_PARAM_TPGT, 0); |
3463 | iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1); |
3464 | iscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1); |
3465 | iscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1); |
3466 | iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1); |
3467 | iscsi_session_attr(chap_out_idx, ISCSI_PARAM_CHAP_OUT_IDX, 1); |
3468 | iscsi_session_attr(chap_in_idx, ISCSI_PARAM_CHAP_IN_IDX, 1); |
3469 | iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0); |
3470 | iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0); |
3471 | iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0); |
3472 | iscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0); |
3473 | iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0); |
3474 | iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0); |
3475 | iscsi_session_attr(targetalias, ISCSI_PARAM_TARGET_ALIAS, 0); |
3476 | iscsi_session_attr(boot_root, ISCSI_PARAM_BOOT_ROOT, 0); |
3477 | iscsi_session_attr(boot_nic, ISCSI_PARAM_BOOT_NIC, 0); |
3478 | iscsi_session_attr(boot_target, ISCSI_PARAM_BOOT_TARGET, 0); |
3479 | |
3480 | static ssize_t |
3481 | show_priv_session_state(struct device *dev, struct device_attribute *attr, |
3482 | char *buf) |
3483 | { |
3484 | struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent); |
3485 | return sprintf(buf, "%s\n", iscsi_session_state_name(session->state)); |
3486 | } |
3487 | static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state, |
3488 | NULL); |
3489 | static ssize_t |
3490 | show_priv_session_creator(struct device *dev, struct device_attribute *attr, |
3491 | char *buf) |
3492 | { |
3493 | struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent); |
3494 | return sprintf(buf, "%d\n", session->creator); |
3495 | } |
3496 | static ISCSI_CLASS_ATTR(priv_sess, creator, S_IRUGO, show_priv_session_creator, |
3497 | NULL); |
3498 | static ssize_t |
3499 | show_priv_session_target_id(struct device *dev, struct device_attribute *attr, |
3500 | char *buf) |
3501 | { |
3502 | struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent); |
3503 | return sprintf(buf, "%d\n", session->target_id); |
3504 | } |
3505 | static ISCSI_CLASS_ATTR(priv_sess, target_id, S_IRUGO, |
3506 | show_priv_session_target_id, NULL); |
3507 | |
3508 | #define iscsi_priv_session_attr_show(field, format) \ |
3509 | static ssize_t \ |
3510 | show_priv_session_##field(struct device *dev, \ |
3511 | struct device_attribute *attr, char *buf) \ |
3512 | { \ |
3513 | struct iscsi_cls_session *session = \ |
3514 | iscsi_dev_to_session(dev->parent); \ |
3515 | if (session->field == -1) \ |
3516 | return sprintf(buf, "off\n"); \ |
3517 | return sprintf(buf, format"\n", session->field); \ |
3518 | } |
3519 | |
3520 | #define iscsi_priv_session_attr_store(field) \ |
3521 | static ssize_t \ |
3522 | store_priv_session_##field(struct device *dev, \ |
3523 | struct device_attribute *attr, \ |
3524 | const char *buf, size_t count) \ |
3525 | { \ |
3526 | int val; \ |
3527 | char *cp; \ |
3528 | struct iscsi_cls_session *session = \ |
3529 | iscsi_dev_to_session(dev->parent); \ |
3530 | if ((session->state == ISCSI_SESSION_FREE) || \ |
3531 | (session->state == ISCSI_SESSION_FAILED)) \ |
3532 | return -EBUSY; \ |
3533 | if (strncmp(buf, "off", 3) == 0) \ |
3534 | session->field = -1; \ |
3535 | else { \ |
3536 | val = simple_strtoul(buf, &cp, 0); \ |
3537 | if (*cp != '\0' && *cp != '\n') \ |
3538 | return -EINVAL; \ |
3539 | session->field = val; \ |
3540 | } \ |
3541 | return count; \ |
3542 | } |
3543 | |
3544 | #define iscsi_priv_session_rw_attr(field, format) \ |
3545 | iscsi_priv_session_attr_show(field, format) \ |
3546 | iscsi_priv_session_attr_store(field) \ |
3547 | static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR, \ |
3548 | show_priv_session_##field, \ |
3549 | store_priv_session_##field) |
3550 | iscsi_priv_session_rw_attr(recovery_tmo, "%d"); |
3551 | |
3552 | static struct attribute *iscsi_session_attrs[] = { |
3553 | &dev_attr_sess_initial_r2t.attr, |
3554 | &dev_attr_sess_max_outstanding_r2t.attr, |
3555 | &dev_attr_sess_immediate_data.attr, |
3556 | &dev_attr_sess_first_burst_len.attr, |
3557 | &dev_attr_sess_max_burst_len.attr, |
3558 | &dev_attr_sess_data_pdu_in_order.attr, |
3559 | &dev_attr_sess_data_seq_in_order.attr, |
3560 | &dev_attr_sess_erl.attr, |
3561 | &dev_attr_sess_targetname.attr, |
3562 | &dev_attr_sess_tpgt.attr, |
3563 | &dev_attr_sess_password.attr, |
3564 | &dev_attr_sess_password_in.attr, |
3565 | &dev_attr_sess_username.attr, |
3566 | &dev_attr_sess_username_in.attr, |
3567 | &dev_attr_sess_fast_abort.attr, |
3568 | &dev_attr_sess_abort_tmo.attr, |
3569 | &dev_attr_sess_lu_reset_tmo.attr, |
3570 | &dev_attr_sess_tgt_reset_tmo.attr, |
3571 | &dev_attr_sess_ifacename.attr, |
3572 | &dev_attr_sess_initiatorname.attr, |
3573 | &dev_attr_sess_targetalias.attr, |
3574 | &dev_attr_sess_boot_root.attr, |
3575 | &dev_attr_sess_boot_nic.attr, |
3576 | &dev_attr_sess_boot_target.attr, |
3577 | &dev_attr_priv_sess_recovery_tmo.attr, |
3578 | &dev_attr_priv_sess_state.attr, |
3579 | &dev_attr_priv_sess_creator.attr, |
3580 | &dev_attr_sess_chap_out_idx.attr, |
3581 | &dev_attr_sess_chap_in_idx.attr, |
3582 | &dev_attr_priv_sess_target_id.attr, |
3583 | NULL, |
3584 | }; |
3585 | |
3586 | static umode_t iscsi_session_attr_is_visible(struct kobject *kobj, |
3587 | struct attribute *attr, int i) |
3588 | { |
3589 | struct device *cdev = container_of(kobj, struct device, kobj); |
3590 | struct iscsi_cls_session *session = transport_class_to_session(cdev); |
3591 | struct iscsi_transport *t = session->transport; |
3592 | int param; |
3593 | |
3594 | if (attr == &dev_attr_sess_initial_r2t.attr) |
3595 | param = ISCSI_PARAM_INITIAL_R2T_EN; |
3596 | else if (attr == &dev_attr_sess_max_outstanding_r2t.attr) |
3597 | param = ISCSI_PARAM_MAX_R2T; |
3598 | else if (attr == &dev_attr_sess_immediate_data.attr) |
3599 | param = ISCSI_PARAM_IMM_DATA_EN; |
3600 | else if (attr == &dev_attr_sess_first_burst_len.attr) |
3601 | param = ISCSI_PARAM_FIRST_BURST; |
3602 | else if (attr == &dev_attr_sess_max_burst_len.attr) |
3603 | param = ISCSI_PARAM_MAX_BURST; |
3604 | else if (attr == &dev_attr_sess_data_pdu_in_order.attr) |
3605 | param = ISCSI_PARAM_PDU_INORDER_EN; |
3606 | else if (attr == &dev_attr_sess_data_seq_in_order.attr) |
3607 | param = ISCSI_PARAM_DATASEQ_INORDER_EN; |
3608 | else if (attr == &dev_attr_sess_erl.attr) |
3609 | param = ISCSI_PARAM_ERL; |
3610 | else if (attr == &dev_attr_sess_targetname.attr) |
3611 | param = ISCSI_PARAM_TARGET_NAME; |
3612 | else if (attr == &dev_attr_sess_tpgt.attr) |
3613 | param = ISCSI_PARAM_TPGT; |
3614 | else if (attr == &dev_attr_sess_chap_in_idx.attr) |
3615 | param = ISCSI_PARAM_CHAP_IN_IDX; |
3616 | else if (attr == &dev_attr_sess_chap_out_idx.attr) |
3617 | param = ISCSI_PARAM_CHAP_OUT_IDX; |
3618 | else if (attr == &dev_attr_sess_password.attr) |
3619 | param = ISCSI_PARAM_USERNAME; |
3620 | else if (attr == &dev_attr_sess_password_in.attr) |
3621 | param = ISCSI_PARAM_USERNAME_IN; |
3622 | else if (attr == &dev_attr_sess_username.attr) |
3623 | param = ISCSI_PARAM_PASSWORD; |
3624 | else if (attr == &dev_attr_sess_username_in.attr) |
3625 | param = ISCSI_PARAM_PASSWORD_IN; |
3626 | else if (attr == &dev_attr_sess_fast_abort.attr) |
3627 | param = ISCSI_PARAM_FAST_ABORT; |
3628 | else if (attr == &dev_attr_sess_abort_tmo.attr) |
3629 | param = ISCSI_PARAM_ABORT_TMO; |
3630 | else if (attr == &dev_attr_sess_lu_reset_tmo.attr) |
3631 | param = ISCSI_PARAM_LU_RESET_TMO; |
3632 | else if (attr == &dev_attr_sess_tgt_reset_tmo.attr) |
3633 | param = ISCSI_PARAM_TGT_RESET_TMO; |
3634 | else if (attr == &dev_attr_sess_ifacename.attr) |
3635 | param = ISCSI_PARAM_IFACE_NAME; |
3636 | else if (attr == &dev_attr_sess_initiatorname.attr) |
3637 | param = ISCSI_PARAM_INITIATOR_NAME; |
3638 | else if (attr == &dev_attr_sess_targetalias.attr) |
3639 | param = ISCSI_PARAM_TARGET_ALIAS; |
3640 | else if (attr == &dev_attr_sess_boot_root.attr) |
3641 | param = ISCSI_PARAM_BOOT_ROOT; |
3642 | else if (attr == &dev_attr_sess_boot_nic.attr) |
3643 | param = ISCSI_PARAM_BOOT_NIC; |
3644 | else if (attr == &dev_attr_sess_boot_target.attr) |
3645 | param = ISCSI_PARAM_BOOT_TARGET; |
3646 | else if (attr == &dev_attr_priv_sess_recovery_tmo.attr) |
3647 | return S_IRUGO | S_IWUSR; |
3648 | else if (attr == &dev_attr_priv_sess_state.attr) |
3649 | return S_IRUGO; |
3650 | else if (attr == &dev_attr_priv_sess_creator.attr) |
3651 | return S_IRUGO; |
3652 | else if (attr == &dev_attr_priv_sess_target_id.attr) |
3653 | return S_IRUGO; |
3654 | else { |
3655 | WARN_ONCE(1, "Invalid session attr"); |
3656 | return 0; |
3657 | } |
3658 | |
3659 | return t->attr_is_visible(ISCSI_PARAM, param); |
3660 | } |
3661 | |
3662 | static struct attribute_group iscsi_session_group = { |
3663 | .attrs = iscsi_session_attrs, |
3664 | .is_visible = iscsi_session_attr_is_visible, |
3665 | }; |
3666 | |
3667 | /* |
3668 | * iSCSI host attrs |
3669 | */ |
3670 | #define iscsi_host_attr_show(param) \ |
3671 | static ssize_t \ |
3672 | show_host_param_##param(struct device *dev, \ |
3673 | struct device_attribute *attr, char *buf) \ |
3674 | { \ |
3675 | struct Scsi_Host *shost = transport_class_to_shost(dev); \ |
3676 | struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \ |
3677 | return priv->iscsi_transport->get_host_param(shost, param, buf); \ |
3678 | } |
3679 | |
3680 | #define iscsi_host_attr(field, param) \ |
3681 | iscsi_host_attr_show(param) \ |
3682 | static ISCSI_CLASS_ATTR(host, field, S_IRUGO, show_host_param_##param, \ |
3683 | NULL); |
3684 | |
3685 | iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME); |
3686 | iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS); |
3687 | iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS); |
3688 | iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME); |
3689 | iscsi_host_attr(port_state, ISCSI_HOST_PARAM_PORT_STATE); |
3690 | iscsi_host_attr(port_speed, ISCSI_HOST_PARAM_PORT_SPEED); |
3691 | |
3692 | static struct attribute *iscsi_host_attrs[] = { |
3693 | &dev_attr_host_netdev.attr, |
3694 | &dev_attr_host_hwaddress.attr, |
3695 | &dev_attr_host_ipaddress.attr, |
3696 | &dev_attr_host_initiatorname.attr, |
3697 | &dev_attr_host_port_state.attr, |
3698 | &dev_attr_host_port_speed.attr, |
3699 | NULL, |
3700 | }; |
3701 | |
3702 | static umode_t iscsi_host_attr_is_visible(struct kobject *kobj, |
3703 | struct attribute *attr, int i) |
3704 | { |
3705 | struct device *cdev = container_of(kobj, struct device, kobj); |
3706 | struct Scsi_Host *shost = transport_class_to_shost(cdev); |
3707 | struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); |
3708 | int param; |
3709 | |
3710 | if (attr == &dev_attr_host_netdev.attr) |
3711 | param = ISCSI_HOST_PARAM_NETDEV_NAME; |
3712 | else if (attr == &dev_attr_host_hwaddress.attr) |
3713 | param = ISCSI_HOST_PARAM_HWADDRESS; |
3714 | else if (attr == &dev_attr_host_ipaddress.attr) |
3715 | param = ISCSI_HOST_PARAM_IPADDRESS; |
3716 | else if (attr == &dev_attr_host_initiatorname.attr) |
3717 | param = ISCSI_HOST_PARAM_INITIATOR_NAME; |
3718 | else if (attr == &dev_attr_host_port_state.attr) |
3719 | param = ISCSI_HOST_PARAM_PORT_STATE; |
3720 | else if (attr == &dev_attr_host_port_speed.attr) |
3721 | param = ISCSI_HOST_PARAM_PORT_SPEED; |
3722 | else { |
3723 | WARN_ONCE(1, "Invalid host attr"); |
3724 | return 0; |
3725 | } |
3726 | |
3727 | return priv->iscsi_transport->attr_is_visible(ISCSI_HOST_PARAM, param); |
3728 | } |
3729 | |
3730 | static struct attribute_group iscsi_host_group = { |
3731 | .attrs = iscsi_host_attrs, |
3732 | .is_visible = iscsi_host_attr_is_visible, |
3733 | }; |
3734 | |
3735 | /* convert iscsi_port_speed values to ascii string name */ |
3736 | static const struct { |
3737 | enum iscsi_port_speed value; |
3738 | char *name; |
3739 | } iscsi_port_speed_names[] = { |
3740 | {ISCSI_PORT_SPEED_UNKNOWN, "Unknown" }, |
3741 | {ISCSI_PORT_SPEED_10MBPS, "10 Mbps" }, |
3742 | {ISCSI_PORT_SPEED_100MBPS, "100 Mbps" }, |
3743 | {ISCSI_PORT_SPEED_1GBPS, "1 Gbps" }, |
3744 | {ISCSI_PORT_SPEED_10GBPS, "10 Gbps" }, |
3745 | }; |
3746 | |
3747 | char *iscsi_get_port_speed_name(struct Scsi_Host *shost) |
3748 | { |
3749 | int i; |
3750 | char *speed = "Unknown!"; |
3751 | struct iscsi_cls_host *ihost = shost->shost_data; |
3752 | uint32_t port_speed = ihost->port_speed; |
3753 | |
3754 | for (i = 0; i < ARRAY_SIZE(iscsi_port_speed_names); i++) { |
3755 | if (iscsi_port_speed_names[i].value & port_speed) { |
3756 | speed = iscsi_port_speed_names[i].name; |
3757 | break; |
3758 | } |
3759 | } |
3760 | return speed; |
3761 | } |
3762 | EXPORT_SYMBOL_GPL(iscsi_get_port_speed_name); |
3763 | |
3764 | /* convert iscsi_port_state values to ascii string name */ |
3765 | static const struct { |
3766 | enum iscsi_port_state value; |
3767 | char *name; |
3768 | } iscsi_port_state_names[] = { |
3769 | {ISCSI_PORT_STATE_DOWN, "LINK DOWN" }, |
3770 | {ISCSI_PORT_STATE_UP, "LINK UP" }, |
3771 | }; |
3772 | |
3773 | char *iscsi_get_port_state_name(struct Scsi_Host *shost) |
3774 | { |
3775 | int i; |
3776 | char *state = "Unknown!"; |
3777 | struct iscsi_cls_host *ihost = shost->shost_data; |
3778 | uint32_t port_state = ihost->port_state; |
3779 | |
3780 | for (i = 0; i < ARRAY_SIZE(iscsi_port_state_names); i++) { |
3781 | if (iscsi_port_state_names[i].value & port_state) { |
3782 | state = iscsi_port_state_names[i].name; |
3783 | break; |
3784 | } |
3785 | } |
3786 | return state; |
3787 | } |
3788 | EXPORT_SYMBOL_GPL(iscsi_get_port_state_name); |
3789 | |
3790 | static int iscsi_session_match(struct attribute_container *cont, |
3791 | struct device *dev) |
3792 | { |
3793 | struct iscsi_cls_session *session; |
3794 | struct Scsi_Host *shost; |
3795 | struct iscsi_internal *priv; |
3796 | |
3797 | if (!iscsi_is_session_dev(dev)) |
3798 | return 0; |
3799 | |
3800 | session = iscsi_dev_to_session(dev); |
3801 | shost = iscsi_session_to_shost(session); |
3802 | if (!shost->transportt) |
3803 | return 0; |
3804 | |
3805 | priv = to_iscsi_internal(shost->transportt); |
3806 | if (priv->session_cont.ac.class != &iscsi_session_class.class) |
3807 | return 0; |
3808 | |
3809 | return &priv->session_cont.ac == cont; |
3810 | } |
3811 | |
3812 | static int iscsi_conn_match(struct attribute_container *cont, |
3813 | struct device *dev) |
3814 | { |
3815 | struct iscsi_cls_session *session; |
3816 | struct iscsi_cls_conn *conn; |
3817 | struct Scsi_Host *shost; |
3818 | struct iscsi_internal *priv; |
3819 | |
3820 | if (!iscsi_is_conn_dev(dev)) |
3821 | return 0; |
3822 | |
3823 | conn = iscsi_dev_to_conn(dev); |
3824 | session = iscsi_dev_to_session(conn->dev.parent); |
3825 | shost = iscsi_session_to_shost(session); |
3826 | |
3827 | if (!shost->transportt) |
3828 | return 0; |
3829 | |
3830 | priv = to_iscsi_internal(shost->transportt); |
3831 | if (priv->conn_cont.ac.class != &iscsi_connection_class.class) |
3832 | return 0; |
3833 | |
3834 | return &priv->conn_cont.ac == cont; |
3835 | } |
3836 | |
3837 | static int iscsi_host_match(struct attribute_container *cont, |
3838 | struct device *dev) |
3839 | { |
3840 | struct Scsi_Host *shost; |
3841 | struct iscsi_internal *priv; |
3842 | |
3843 | if (!scsi_is_host_device(dev)) |
3844 | return 0; |
3845 | |
3846 | shost = dev_to_shost(dev); |
3847 | if (!shost->transportt || |
3848 | shost->transportt->host_attrs.ac.class != &iscsi_host_class.class) |
3849 | return 0; |
3850 | |
3851 | priv = to_iscsi_internal(shost->transportt); |
3852 | return &priv->t.host_attrs.ac == cont; |
3853 | } |
3854 | |
3855 | struct scsi_transport_template * |
3856 | iscsi_register_transport(struct iscsi_transport *tt) |
3857 | { |
3858 | struct iscsi_internal *priv; |
3859 | unsigned long flags; |
3860 | int err; |
3861 | |
3862 | BUG_ON(!tt); |
3863 | |
3864 | priv = iscsi_if_transport_lookup(tt); |
3865 | if (priv) |
3866 | return NULL; |
3867 | |
3868 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
3869 | if (!priv) |
3870 | return NULL; |
3871 | INIT_LIST_HEAD(&priv->list); |
3872 | priv->iscsi_transport = tt; |
3873 | priv->t.user_scan = iscsi_user_scan; |
3874 | priv->t.create_work_queue = 1; |
3875 | |
3876 | priv->dev.class = &iscsi_transport_class; |
3877 | dev_set_name(&priv->dev, "%s", tt->name); |
3878 | err = device_register(&priv->dev); |
3879 | if (err) |
3880 | goto free_priv; |
3881 | |
3882 | err = sysfs_create_group(&priv->dev.kobj, &iscsi_transport_group); |
3883 | if (err) |
3884 | goto unregister_dev; |
3885 | |
3886 | /* host parameters */ |
3887 | priv->t.host_attrs.ac.class = &iscsi_host_class.class; |
3888 | priv->t.host_attrs.ac.match = iscsi_host_match; |
3889 | priv->t.host_attrs.ac.grp = &iscsi_host_group; |
3890 | priv->t.host_size = sizeof(struct iscsi_cls_host); |
3891 | transport_container_register(&priv->t.host_attrs); |
3892 | |
3893 | /* connection parameters */ |
3894 | priv->conn_cont.ac.class = &iscsi_connection_class.class; |
3895 | priv->conn_cont.ac.match = iscsi_conn_match; |
3896 | priv->conn_cont.ac.grp = &iscsi_conn_group; |
3897 | transport_container_register(&priv->conn_cont); |
3898 | |
3899 | /* session parameters */ |
3900 | priv->session_cont.ac.class = &iscsi_session_class.class; |
3901 | priv->session_cont.ac.match = iscsi_session_match; |
3902 | priv->session_cont.ac.grp = &iscsi_session_group; |
3903 | transport_container_register(&priv->session_cont); |
3904 | |
3905 | spin_lock_irqsave(&iscsi_transport_lock, flags); |
3906 | list_add(&priv->list, &iscsi_transports); |
3907 | spin_unlock_irqrestore(&iscsi_transport_lock, flags); |
3908 | |
3909 | printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name); |
3910 | return &priv->t; |
3911 | |
3912 | unregister_dev: |
3913 | device_unregister(&priv->dev); |
3914 | return NULL; |
3915 | free_priv: |
3916 | kfree(priv); |
3917 | return NULL; |
3918 | } |
3919 | EXPORT_SYMBOL_GPL(iscsi_register_transport); |
3920 | |
3921 | int iscsi_unregister_transport(struct iscsi_transport *tt) |
3922 | { |
3923 | struct iscsi_internal *priv; |
3924 | unsigned long flags; |
3925 | |
3926 | BUG_ON(!tt); |
3927 | |
3928 | mutex_lock(&rx_queue_mutex); |
3929 | |
3930 | priv = iscsi_if_transport_lookup(tt); |
3931 | BUG_ON (!priv); |
3932 | |
3933 | spin_lock_irqsave(&iscsi_transport_lock, flags); |
3934 | list_del(&priv->list); |
3935 | spin_unlock_irqrestore(&iscsi_transport_lock, flags); |
3936 | |
3937 | transport_container_unregister(&priv->conn_cont); |
3938 | transport_container_unregister(&priv->session_cont); |
3939 | transport_container_unregister(&priv->t.host_attrs); |
3940 | |
3941 | sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group); |
3942 | device_unregister(&priv->dev); |
3943 | mutex_unlock(&rx_queue_mutex); |
3944 | |
3945 | return 0; |
3946 | } |
3947 | EXPORT_SYMBOL_GPL(iscsi_unregister_transport); |
3948 | |
3949 | static __init int iscsi_transport_init(void) |
3950 | { |
3951 | int err; |
3952 | struct netlink_kernel_cfg cfg = { |
3953 | .groups = 1, |
3954 | .input = iscsi_if_rx, |
3955 | }; |
3956 | printk(KERN_INFO "Loading iSCSI transport class v%s.\n", |
3957 | ISCSI_TRANSPORT_VERSION); |
3958 | |
3959 | atomic_set(&iscsi_session_nr, 0); |
3960 | |
3961 | err = class_register(&iscsi_transport_class); |
3962 | if (err) |
3963 | return err; |
3964 | |
3965 | err = class_register(&iscsi_endpoint_class); |
3966 | if (err) |
3967 | goto unregister_transport_class; |
3968 | |
3969 | err = class_register(&iscsi_iface_class); |
3970 | if (err) |
3971 | goto unregister_endpoint_class; |
3972 | |
3973 | err = transport_class_register(&iscsi_host_class); |
3974 | if (err) |
3975 | goto unregister_iface_class; |
3976 | |
3977 | err = transport_class_register(&iscsi_connection_class); |
3978 | if (err) |
3979 | goto unregister_host_class; |
3980 | |
3981 | err = transport_class_register(&iscsi_session_class); |
3982 | if (err) |
3983 | goto unregister_conn_class; |
3984 | |
3985 | err = bus_register(&iscsi_flashnode_bus); |
3986 | if (err) |
3987 | goto unregister_session_class; |
3988 | |
3989 | nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, &cfg); |
3990 | if (!nls) { |
3991 | err = -ENOBUFS; |
3992 | goto unregister_flashnode_bus; |
3993 | } |
3994 | |
3995 | iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh"); |
3996 | if (!iscsi_eh_timer_workq) { |
3997 | err = -ENOMEM; |
3998 | goto release_nls; |
3999 | } |
4000 | |
4001 | return 0; |
4002 | |
4003 | release_nls: |
4004 | netlink_kernel_release(nls); |
4005 | unregister_flashnode_bus: |
4006 | bus_unregister(&iscsi_flashnode_bus); |
4007 | unregister_session_class: |
4008 | transport_class_unregister(&iscsi_session_class); |
4009 | unregister_conn_class: |
4010 | transport_class_unregister(&iscsi_connection_class); |
4011 | unregister_host_class: |
4012 | transport_class_unregister(&iscsi_host_class); |
4013 | unregister_iface_class: |
4014 | class_unregister(&iscsi_iface_class); |
4015 | unregister_endpoint_class: |
4016 | class_unregister(&iscsi_endpoint_class); |
4017 | unregister_transport_class: |
4018 | class_unregister(&iscsi_transport_class); |
4019 | return err; |
4020 | } |
4021 | |
4022 | static void __exit iscsi_transport_exit(void) |
4023 | { |
4024 | destroy_workqueue(iscsi_eh_timer_workq); |
4025 | netlink_kernel_release(nls); |
4026 | bus_unregister(&iscsi_flashnode_bus); |
4027 | transport_class_unregister(&iscsi_connection_class); |
4028 | transport_class_unregister(&iscsi_session_class); |
4029 | transport_class_unregister(&iscsi_host_class); |
4030 | class_unregister(&iscsi_endpoint_class); |
4031 | class_unregister(&iscsi_iface_class); |
4032 | class_unregister(&iscsi_transport_class); |
4033 | } |
4034 | |
4035 | module_init(iscsi_transport_init); |
4036 | module_exit(iscsi_transport_exit); |
4037 | |
4038 | MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, " |
4039 | "Dmitry Yusupov <dmitry_yus@yahoo.com>, " |
4040 | "Alex Aizman <itn780@yahoo.com>"); |
4041 | MODULE_DESCRIPTION("iSCSI Transport Interface"); |
4042 | MODULE_LICENSE("GPL"); |
4043 | MODULE_VERSION(ISCSI_TRANSPORT_VERSION); |
4044 | MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_ISCSI); |
4045 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9