diff options
-rw-r--r-- | drivers/scsi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/scsi/Makefile | 1 | ||||
-rw-r--r-- | drivers/scsi/scsi.c | 3 | ||||
-rw-r--r-- | drivers/scsi/scsi_netlink.c | 199 | ||||
-rw-r--r-- | drivers/scsi/scsi_priv.h | 11 | ||||
-rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 200 | ||||
-rw-r--r-- | include/linux/netlink.h | 2 | ||||
-rw-r--r-- | include/scsi/scsi_netlink.h | 86 | ||||
-rw-r--r-- | include/scsi/scsi_netlink_fc.h | 71 | ||||
-rw-r--r-- | include/scsi/scsi_transport_fc.h | 34 |
10 files changed, 612 insertions, 1 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index c8c606589ea6..4d1998d23f0f 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig | |||
@@ -27,6 +27,11 @@ config SCSI | |||
27 | However, do not compile this as a module if your root file system | 27 | However, do not compile this as a module if your root file system |
28 | (the one containing the directory /) is located on a SCSI device. | 28 | (the one containing the directory /) is located on a SCSI device. |
29 | 29 | ||
30 | config SCSI_NETLINK | ||
31 | tristate | ||
32 | default n | ||
33 | select NET | ||
34 | |||
30 | config SCSI_PROC_FS | 35 | config SCSI_PROC_FS |
31 | bool "legacy /proc/scsi/ support" | 36 | bool "legacy /proc/scsi/ support" |
32 | depends on SCSI && PROC_FS | 37 | depends on SCSI && PROC_FS |
@@ -222,6 +227,7 @@ config SCSI_SPI_ATTRS | |||
222 | config SCSI_FC_ATTRS | 227 | config SCSI_FC_ATTRS |
223 | tristate "FiberChannel Transport Attributes" | 228 | tristate "FiberChannel Transport Attributes" |
224 | depends on SCSI | 229 | depends on SCSI |
230 | select SCSI_NETLINK | ||
225 | help | 231 | help |
226 | If you wish to export transport-specific information about | 232 | If you wish to export transport-specific information about |
227 | each attached FiberChannel device to sysfs, say Y. | 233 | each attached FiberChannel device to sysfs, say Y. |
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index fd9aeb1ba07f..8fc2c594b537 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile | |||
@@ -159,6 +159,7 @@ scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \ | |||
159 | scsicam.o scsi_error.o scsi_lib.o \ | 159 | scsicam.o scsi_error.o scsi_lib.o \ |
160 | scsi_scan.o scsi_sysfs.o \ | 160 | scsi_scan.o scsi_sysfs.o \ |
161 | scsi_devinfo.o | 161 | scsi_devinfo.o |
162 | scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o | ||
162 | scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o | 163 | scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o |
163 | scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o | 164 | scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o |
164 | 165 | ||
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 37843927e47f..eedfd059b82b 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c | |||
@@ -1118,6 +1118,8 @@ static int __init init_scsi(void) | |||
1118 | for_each_possible_cpu(i) | 1118 | for_each_possible_cpu(i) |
1119 | INIT_LIST_HEAD(&per_cpu(scsi_done_q, i)); | 1119 | INIT_LIST_HEAD(&per_cpu(scsi_done_q, i)); |
1120 | 1120 | ||
1121 | scsi_netlink_init(); | ||
1122 | |||
1121 | printk(KERN_NOTICE "SCSI subsystem initialized\n"); | 1123 | printk(KERN_NOTICE "SCSI subsystem initialized\n"); |
1122 | return 0; | 1124 | return 0; |
1123 | 1125 | ||
@@ -1138,6 +1140,7 @@ cleanup_queue: | |||
1138 | 1140 | ||
1139 | static void __exit exit_scsi(void) | 1141 | static void __exit exit_scsi(void) |
1140 | { | 1142 | { |
1143 | scsi_netlink_exit(); | ||
1141 | scsi_sysfs_unregister(); | 1144 | scsi_sysfs_unregister(); |
1142 | scsi_exit_sysctl(); | 1145 | scsi_exit_sysctl(); |
1143 | scsi_exit_hosts(); | 1146 | scsi_exit_hosts(); |
diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c new file mode 100644 index 000000000000..1b59b27e887f --- /dev/null +++ b/drivers/scsi/scsi_netlink.c | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | * scsi_netlink.c - SCSI Transport Netlink Interface | ||
3 | * | ||
4 | * Copyright (C) 2006 James Smart, Emulex Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | #include <linux/time.h> | ||
22 | #include <linux/jiffies.h> | ||
23 | #include <linux/security.h> | ||
24 | #include <net/sock.h> | ||
25 | #include <net/netlink.h> | ||
26 | |||
27 | #include <scsi/scsi_netlink.h> | ||
28 | #include "scsi_priv.h" | ||
29 | |||
30 | struct sock *scsi_nl_sock = NULL; | ||
31 | EXPORT_SYMBOL_GPL(scsi_nl_sock); | ||
32 | |||
33 | |||
34 | /** | ||
35 | * scsi_nl_rcv_msg - | ||
36 | * Receive message handler. Extracts message from a receive buffer. | ||
37 | * Validates message header and calls appropriate transport message handler | ||
38 | * | ||
39 | * @skb: socket receive buffer | ||
40 | * | ||
41 | **/ | ||
42 | static void | ||
43 | scsi_nl_rcv_msg(struct sk_buff *skb) | ||
44 | { | ||
45 | struct nlmsghdr *nlh; | ||
46 | struct scsi_nl_hdr *hdr; | ||
47 | uint32_t rlen; | ||
48 | int err; | ||
49 | |||
50 | while (skb->len >= NLMSG_SPACE(0)) { | ||
51 | err = 0; | ||
52 | |||
53 | nlh = (struct nlmsghdr *) skb->data; | ||
54 | if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) || | ||
55 | (skb->len < nlh->nlmsg_len)) { | ||
56 | printk(KERN_WARNING "%s: discarding partial skb\n", | ||
57 | __FUNCTION__); | ||
58 | return; | ||
59 | } | ||
60 | |||
61 | rlen = NLMSG_ALIGN(nlh->nlmsg_len); | ||
62 | if (rlen > skb->len) | ||
63 | rlen = skb->len; | ||
64 | |||
65 | if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) { | ||
66 | err = -EBADMSG; | ||
67 | goto next_msg; | ||
68 | } | ||
69 | |||
70 | hdr = NLMSG_DATA(nlh); | ||
71 | if ((hdr->version != SCSI_NL_VERSION) || | ||
72 | (hdr->magic != SCSI_NL_MAGIC)) { | ||
73 | err = -EPROTOTYPE; | ||
74 | goto next_msg; | ||
75 | } | ||
76 | |||
77 | if (security_netlink_recv(skb, CAP_SYS_ADMIN)) { | ||
78 | err = -EPERM; | ||
79 | goto next_msg; | ||
80 | } | ||
81 | |||
82 | if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) { | ||
83 | printk(KERN_WARNING "%s: discarding partial message\n", | ||
84 | __FUNCTION__); | ||
85 | return; | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * We currently don't support anyone sending us a message | ||
90 | */ | ||
91 | |||
92 | next_msg: | ||
93 | if ((err) || (nlh->nlmsg_flags & NLM_F_ACK)) | ||
94 | netlink_ack(skb, nlh, err); | ||
95 | |||
96 | skb_pull(skb, rlen); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | |||
101 | /** | ||
102 | * scsi_nl_rcv_msg - | ||
103 | * Receive handler for a socket. Extracts a received message buffer from | ||
104 | * the socket, and starts message processing. | ||
105 | * | ||
106 | * @sk: socket | ||
107 | * @len: unused | ||
108 | * | ||
109 | **/ | ||
110 | static void | ||
111 | scsi_nl_rcv(struct sock *sk, int len) | ||
112 | { | ||
113 | struct sk_buff *skb; | ||
114 | |||
115 | while ((skb = skb_dequeue(&sk->sk_receive_queue))) { | ||
116 | scsi_nl_rcv_msg(skb); | ||
117 | kfree_skb(skb); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | |||
122 | /** | ||
123 | * scsi_nl_rcv_event - | ||
124 | * Event handler for a netlink socket. | ||
125 | * | ||
126 | * @this: event notifier block | ||
127 | * @event: event type | ||
128 | * @ptr: event payload | ||
129 | * | ||
130 | **/ | ||
131 | static int | ||
132 | scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr) | ||
133 | { | ||
134 | struct netlink_notify *n = ptr; | ||
135 | |||
136 | if (n->protocol != NETLINK_SCSITRANSPORT) | ||
137 | return NOTIFY_DONE; | ||
138 | |||
139 | /* | ||
140 | * Currently, we are not tracking PID's, etc. There is nothing | ||
141 | * to handle. | ||
142 | */ | ||
143 | |||
144 | return NOTIFY_DONE; | ||
145 | } | ||
146 | |||
147 | static struct notifier_block scsi_netlink_notifier = { | ||
148 | .notifier_call = scsi_nl_rcv_event, | ||
149 | }; | ||
150 | |||
151 | |||
152 | /** | ||
153 | * scsi_netlink_init - | ||
154 | * Called by SCSI subsystem to intialize the SCSI transport netlink | ||
155 | * interface | ||
156 | * | ||
157 | **/ | ||
158 | void | ||
159 | scsi_netlink_init(void) | ||
160 | { | ||
161 | int error; | ||
162 | |||
163 | error = netlink_register_notifier(&scsi_netlink_notifier); | ||
164 | if (error) { | ||
165 | printk(KERN_ERR "%s: register of event handler failed - %d\n", | ||
166 | __FUNCTION__, error); | ||
167 | return; | ||
168 | } | ||
169 | |||
170 | scsi_nl_sock = netlink_kernel_create(NETLINK_SCSITRANSPORT, | ||
171 | SCSI_NL_GRP_CNT, scsi_nl_rcv, THIS_MODULE); | ||
172 | if (!scsi_nl_sock) { | ||
173 | printk(KERN_ERR "%s: register of recieve handler failed\n", | ||
174 | __FUNCTION__); | ||
175 | netlink_unregister_notifier(&scsi_netlink_notifier); | ||
176 | } | ||
177 | |||
178 | return; | ||
179 | } | ||
180 | |||
181 | |||
182 | /** | ||
183 | * scsi_netlink_exit - | ||
184 | * Called by SCSI subsystem to disable the SCSI transport netlink | ||
185 | * interface | ||
186 | * | ||
187 | **/ | ||
188 | void | ||
189 | scsi_netlink_exit(void) | ||
190 | { | ||
191 | if (scsi_nl_sock) { | ||
192 | sock_release(scsi_nl_sock->sk_socket); | ||
193 | netlink_unregister_notifier(&scsi_netlink_notifier); | ||
194 | } | ||
195 | |||
196 | return; | ||
197 | } | ||
198 | |||
199 | |||
diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index ae24c85aaeea..5d023d44e5e7 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h | |||
@@ -8,6 +8,7 @@ struct scsi_cmnd; | |||
8 | struct scsi_device; | 8 | struct scsi_device; |
9 | struct scsi_host_template; | 9 | struct scsi_host_template; |
10 | struct Scsi_Host; | 10 | struct Scsi_Host; |
11 | struct scsi_nl_hdr; | ||
11 | 12 | ||
12 | 13 | ||
13 | /* | 14 | /* |
@@ -110,6 +111,16 @@ extern void __scsi_remove_device(struct scsi_device *); | |||
110 | 111 | ||
111 | extern struct bus_type scsi_bus_type; | 112 | extern struct bus_type scsi_bus_type; |
112 | 113 | ||
114 | /* scsi_netlink.c */ | ||
115 | #ifdef CONFIG_SCSI_NETLINK | ||
116 | extern void scsi_netlink_init(void); | ||
117 | extern void scsi_netlink_exit(void); | ||
118 | extern struct sock *scsi_nl_sock; | ||
119 | #else | ||
120 | static inline void scsi_netlink_init(void) {} | ||
121 | static inline void scsi_netlink_exit(void) {} | ||
122 | #endif | ||
123 | |||
113 | /* | 124 | /* |
114 | * internal scsi timeout functions: for use by mid-layer and transport | 125 | * internal scsi timeout functions: for use by mid-layer and transport |
115 | * classes. | 126 | * classes. |
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 79d31ca2b741..05989f130554 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c | |||
@@ -32,6 +32,9 @@ | |||
32 | #include <scsi/scsi_transport.h> | 32 | #include <scsi/scsi_transport.h> |
33 | #include <scsi/scsi_transport_fc.h> | 33 | #include <scsi/scsi_transport_fc.h> |
34 | #include <scsi/scsi_cmnd.h> | 34 | #include <scsi/scsi_cmnd.h> |
35 | #include <linux/netlink.h> | ||
36 | #include <net/netlink.h> | ||
37 | #include <scsi/scsi_netlink_fc.h> | ||
35 | #include "scsi_priv.h" | 38 | #include "scsi_priv.h" |
36 | 39 | ||
37 | static int fc_queue_work(struct Scsi_Host *, struct work_struct *); | 40 | static int fc_queue_work(struct Scsi_Host *, struct work_struct *); |
@@ -93,6 +96,29 @@ fc_enum_name_search(port_type, fc_port_type, fc_port_type_names) | |||
93 | #define FC_PORTTYPE_MAX_NAMELEN 50 | 96 | #define FC_PORTTYPE_MAX_NAMELEN 50 |
94 | 97 | ||
95 | 98 | ||
99 | /* Convert fc_host_event_code values to ascii string name */ | ||
100 | static const struct { | ||
101 | enum fc_host_event_code value; | ||
102 | char *name; | ||
103 | } fc_host_event_code_names[] = { | ||
104 | { FCH_EVT_LIP, "lip" }, | ||
105 | { FCH_EVT_LINKUP, "link_up" }, | ||
106 | { FCH_EVT_LINKDOWN, "link_down" }, | ||
107 | { FCH_EVT_LIPRESET, "lip_reset" }, | ||
108 | { FCH_EVT_RSCN, "rscn" }, | ||
109 | { FCH_EVT_ADAPTER_CHANGE, "adapter_chg" }, | ||
110 | { FCH_EVT_PORT_UNKNOWN, "port_unknown" }, | ||
111 | { FCH_EVT_PORT_ONLINE, "port_online" }, | ||
112 | { FCH_EVT_PORT_OFFLINE, "port_offline" }, | ||
113 | { FCH_EVT_PORT_FABRIC, "port_fabric" }, | ||
114 | { FCH_EVT_LINK_UNKNOWN, "link_unknown" }, | ||
115 | { FCH_EVT_VENDOR_UNIQUE, "vendor_unique" }, | ||
116 | }; | ||
117 | fc_enum_name_search(host_event_code, fc_host_event_code, | ||
118 | fc_host_event_code_names) | ||
119 | #define FC_HOST_EVENT_CODE_MAX_NAMELEN 30 | ||
120 | |||
121 | |||
96 | /* Convert fc_port_state values to ascii string name */ | 122 | /* Convert fc_port_state values to ascii string name */ |
97 | static struct { | 123 | static struct { |
98 | enum fc_port_state value; | 124 | enum fc_port_state value; |
@@ -377,10 +403,182 @@ MODULE_PARM_DESC(dev_loss_tmo, | |||
377 | " exceeded, the scsi target is removed. Value should be" | 403 | " exceeded, the scsi target is removed. Value should be" |
378 | " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT."); | 404 | " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT."); |
379 | 405 | ||
406 | /** | ||
407 | * Netlink Infrastructure | ||
408 | **/ | ||
409 | |||
410 | static atomic_t fc_event_seq; | ||
411 | |||
412 | /** | ||
413 | * fc_get_event_number - Obtain the next sequential FC event number | ||
414 | * | ||
415 | * Notes: | ||
416 | * We could have inline'd this, but it would have required fc_event_seq to | ||
417 | * be exposed. For now, live with the subroutine call. | ||
418 | * Atomic used to avoid lock/unlock... | ||
419 | **/ | ||
420 | u32 | ||
421 | fc_get_event_number(void) | ||
422 | { | ||
423 | return atomic_add_return(1, &fc_event_seq); | ||
424 | } | ||
425 | EXPORT_SYMBOL(fc_get_event_number); | ||
426 | |||
427 | |||
428 | /** | ||
429 | * fc_host_post_event - called to post an even on an fc_host. | ||
430 | * | ||
431 | * @shost: host the event occurred on | ||
432 | * @event_number: fc event number obtained from get_fc_event_number() | ||
433 | * @event_code: fc_host event being posted | ||
434 | * @event_data: 32bits of data for the event being posted | ||
435 | * | ||
436 | * Notes: | ||
437 | * This routine assumes no locks are held on entry. | ||
438 | **/ | ||
439 | void | ||
440 | fc_host_post_event(struct Scsi_Host *shost, u32 event_number, | ||
441 | enum fc_host_event_code event_code, u32 event_data) | ||
442 | { | ||
443 | struct sk_buff *skb; | ||
444 | struct nlmsghdr *nlh; | ||
445 | struct fc_nl_event *event; | ||
446 | const char *name; | ||
447 | u32 len, skblen; | ||
448 | int err; | ||
449 | |||
450 | if (!scsi_nl_sock) { | ||
451 | err = -ENOENT; | ||
452 | goto send_fail; | ||
453 | } | ||
454 | |||
455 | len = FC_NL_MSGALIGN(sizeof(*event)); | ||
456 | skblen = NLMSG_SPACE(len); | ||
457 | |||
458 | skb = alloc_skb(skblen, GFP_KERNEL); | ||
459 | if (!skb) { | ||
460 | err = -ENOBUFS; | ||
461 | goto send_fail; | ||
462 | } | ||
463 | |||
464 | nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, | ||
465 | skblen - sizeof(*nlh), 0); | ||
466 | if (!nlh) { | ||
467 | err = -ENOBUFS; | ||
468 | goto send_fail_skb; | ||
469 | } | ||
470 | event = NLMSG_DATA(nlh); | ||
471 | |||
472 | INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC, | ||
473 | FC_NL_ASYNC_EVENT, len); | ||
474 | event->seconds = get_seconds(); | ||
475 | event->vendor_id = 0; | ||
476 | event->host_no = shost->host_no; | ||
477 | event->event_datalen = sizeof(u32); /* bytes */ | ||
478 | event->event_num = event_number; | ||
479 | event->event_code = event_code; | ||
480 | event->event_data = event_data; | ||
481 | |||
482 | err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS); | ||
483 | if (err && (err != -ESRCH)) /* filter no recipient errors */ | ||
484 | /* nlmsg_multicast already kfree_skb'd */ | ||
485 | goto send_fail; | ||
486 | |||
487 | return; | ||
488 | |||
489 | send_fail_skb: | ||
490 | kfree_skb(skb); | ||
491 | send_fail: | ||
492 | name = get_fc_host_event_code_name(event_code); | ||
493 | printk(KERN_WARNING | ||
494 | "%s: Dropped Event : host %d %s data 0x%08x - err %d\n", | ||
495 | __FUNCTION__, shost->host_no, | ||
496 | (name) ? name : "<unknown>", event_data, err); | ||
497 | return; | ||
498 | } | ||
499 | EXPORT_SYMBOL(fc_host_post_event); | ||
500 | |||
501 | |||
502 | /** | ||
503 | * fc_host_post_vendor_event - called to post a vendor unique event on | ||
504 | * a fc_host | ||
505 | * | ||
506 | * @shost: host the event occurred on | ||
507 | * @event_number: fc event number obtained from get_fc_event_number() | ||
508 | * @data_len: amount, in bytes, of vendor unique data | ||
509 | * @data_buf: pointer to vendor unique data | ||
510 | * | ||
511 | * Notes: | ||
512 | * This routine assumes no locks are held on entry. | ||
513 | **/ | ||
514 | void | ||
515 | fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, | ||
516 | u32 data_len, char * data_buf, u32 vendor_id) | ||
517 | { | ||
518 | struct sk_buff *skb; | ||
519 | struct nlmsghdr *nlh; | ||
520 | struct fc_nl_event *event; | ||
521 | u32 len, skblen; | ||
522 | int err; | ||
523 | |||
524 | if (!scsi_nl_sock) { | ||
525 | err = -ENOENT; | ||
526 | goto send_vendor_fail; | ||
527 | } | ||
528 | |||
529 | len = FC_NL_MSGALIGN(sizeof(*event) + data_len); | ||
530 | skblen = NLMSG_SPACE(len); | ||
531 | |||
532 | skb = alloc_skb(skblen, GFP_KERNEL); | ||
533 | if (!skb) { | ||
534 | err = -ENOBUFS; | ||
535 | goto send_vendor_fail; | ||
536 | } | ||
537 | |||
538 | nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, | ||
539 | skblen - sizeof(*nlh), 0); | ||
540 | if (!nlh) { | ||
541 | err = -ENOBUFS; | ||
542 | goto send_vendor_fail_skb; | ||
543 | } | ||
544 | event = NLMSG_DATA(nlh); | ||
545 | |||
546 | INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC, | ||
547 | FC_NL_ASYNC_EVENT, len); | ||
548 | event->seconds = get_seconds(); | ||
549 | event->vendor_id = vendor_id; | ||
550 | event->host_no = shost->host_no; | ||
551 | event->event_datalen = data_len; /* bytes */ | ||
552 | event->event_num = event_number; | ||
553 | event->event_code = FCH_EVT_VENDOR_UNIQUE; | ||
554 | memcpy(&event->event_data, data_buf, data_len); | ||
555 | |||
556 | err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS); | ||
557 | if (err && (err != -ESRCH)) /* filter no recipient errors */ | ||
558 | /* nlmsg_multicast already kfree_skb'd */ | ||
559 | goto send_vendor_fail; | ||
560 | |||
561 | return; | ||
562 | |||
563 | send_vendor_fail_skb: | ||
564 | kfree_skb(skb); | ||
565 | send_vendor_fail: | ||
566 | printk(KERN_WARNING | ||
567 | "%s: Dropped Event : host %d vendor_unique - err %d\n", | ||
568 | __FUNCTION__, shost->host_no, err); | ||
569 | return; | ||
570 | } | ||
571 | EXPORT_SYMBOL(fc_host_post_vendor_event); | ||
572 | |||
573 | |||
380 | 574 | ||
381 | static __init int fc_transport_init(void) | 575 | static __init int fc_transport_init(void) |
382 | { | 576 | { |
383 | int error = transport_class_register(&fc_host_class); | 577 | int error; |
578 | |||
579 | atomic_set(&fc_event_seq, 0); | ||
580 | |||
581 | error = transport_class_register(&fc_host_class); | ||
384 | if (error) | 582 | if (error) |
385 | return error; | 583 | return error; |
386 | error = transport_class_register(&fc_rport_class); | 584 | error = transport_class_register(&fc_rport_class); |
diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 855b44668caa..66411622e06e 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h | |||
@@ -21,6 +21,8 @@ | |||
21 | #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ | 21 | #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ |
22 | #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ | 22 | #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ |
23 | #define NETLINK_GENERIC 16 | 23 | #define NETLINK_GENERIC 16 |
24 | /* leave room for NETLINK_DM (DM Events) */ | ||
25 | #define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ | ||
24 | 26 | ||
25 | #define MAX_LINKS 32 | 27 | #define MAX_LINKS 32 |
26 | 28 | ||
diff --git a/include/scsi/scsi_netlink.h b/include/scsi/scsi_netlink.h new file mode 100644 index 000000000000..7a3a20e640c0 --- /dev/null +++ b/include/scsi/scsi_netlink.h | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * SCSI Transport Netlink Interface | ||
3 | * Used for the posting of outbound SCSI transport events | ||
4 | * | ||
5 | * Copyright (C) 2006 James Smart, Emulex Corporation | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | * | ||
21 | */ | ||
22 | #ifndef SCSI_NETLINK_H | ||
23 | #define SCSI_NETLINK_H | ||
24 | |||
25 | /* | ||
26 | * This file intended to be included by both kernel and user space | ||
27 | */ | ||
28 | |||
29 | /* Single Netlink Message type to send all SCSI Transport messages */ | ||
30 | #define SCSI_TRANSPORT_MSG NLMSG_MIN_TYPE + 1 | ||
31 | |||
32 | /* SCSI Transport Broadcast Groups */ | ||
33 | /* leaving groups 0 and 1 unassigned */ | ||
34 | #define SCSI_NL_GRP_FC_EVENTS (1<<2) /* Group 2 */ | ||
35 | #define SCSI_NL_GRP_CNT 3 | ||
36 | |||
37 | |||
38 | /* SCSI_TRANSPORT_MSG event message header */ | ||
39 | struct scsi_nl_hdr { | ||
40 | uint8_t version; | ||
41 | uint8_t transport; | ||
42 | uint16_t magic; | ||
43 | uint16_t msgtype; | ||
44 | uint16_t msglen; | ||
45 | } __attribute__((aligned(sizeof(uint64_t)))); | ||
46 | |||
47 | /* scsi_nl_hdr->version value */ | ||
48 | #define SCSI_NL_VERSION 1 | ||
49 | |||
50 | /* scsi_nl_hdr->magic value */ | ||
51 | #define SCSI_NL_MAGIC 0xA1B2 | ||
52 | |||
53 | /* scsi_nl_hdr->transport value */ | ||
54 | #define SCSI_NL_TRANSPORT 0 | ||
55 | #define SCSI_NL_TRANSPORT_FC 1 | ||
56 | #define SCSI_NL_MAX_TRANSPORTS 2 | ||
57 | |||
58 | /* scsi_nl_hdr->msgtype values are defined in each transport */ | ||
59 | |||
60 | |||
61 | /* | ||
62 | * Vendor ID: | ||
63 | * If transports post vendor-unique events, they must pass a well-known | ||
64 | * 32-bit vendor identifier. This identifier consists of 8 bits indicating | ||
65 | * the "type" of identifier contained, and 24 bits of id data. | ||
66 | * | ||
67 | * Identifiers for each type: | ||
68 | * PCI : ID data is the 16 bit PCI Registered Vendor ID | ||
69 | */ | ||
70 | #define SCSI_NL_VID_ID_MASK 0x00FFFFFF | ||
71 | #define SCSI_NL_VID_TYPE_MASK 0xFF000000 | ||
72 | #define SCSI_NL_VID_TYPE_PCI 0x01000000 | ||
73 | |||
74 | |||
75 | #define INIT_SCSI_NL_HDR(hdr, t, mtype, mlen) \ | ||
76 | { \ | ||
77 | (hdr)->version = SCSI_NL_VERSION; \ | ||
78 | (hdr)->transport = t; \ | ||
79 | (hdr)->magic = SCSI_NL_MAGIC; \ | ||
80 | (hdr)->msgtype = mtype; \ | ||
81 | (hdr)->msglen = mlen; \ | ||
82 | } | ||
83 | |||
84 | |||
85 | #endif /* SCSI_NETLINK_H */ | ||
86 | |||
diff --git a/include/scsi/scsi_netlink_fc.h b/include/scsi/scsi_netlink_fc.h new file mode 100644 index 000000000000..b213d2909fed --- /dev/null +++ b/include/scsi/scsi_netlink_fc.h | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * FC Transport Netlink Interface | ||
3 | * | ||
4 | * Copyright (C) 2006 James Smart, Emulex Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | #ifndef SCSI_NETLINK_FC_H | ||
22 | #define SCSI_NETLINK_FC_H | ||
23 | |||
24 | #include <scsi/scsi_netlink.h> | ||
25 | |||
26 | /* | ||
27 | * This file intended to be included by both kernel and user space | ||
28 | */ | ||
29 | |||
30 | /* | ||
31 | * FC Transport Message Types | ||
32 | */ | ||
33 | /* kernel -> user */ | ||
34 | #define FC_NL_ASYNC_EVENT 0x0100 | ||
35 | /* user -> kernel */ | ||
36 | /* none */ | ||
37 | |||
38 | |||
39 | /* | ||
40 | * Message Structures : | ||
41 | */ | ||
42 | |||
43 | /* macro to round up message lengths to 8byte boundary */ | ||
44 | #define FC_NL_MSGALIGN(len) (((len) + 7) & ~7) | ||
45 | |||
46 | |||
47 | /* | ||
48 | * FC Transport Broadcast Event Message : | ||
49 | * FC_NL_ASYNC_EVENT | ||
50 | * | ||
51 | * Note: if Vendor Unique message, &event_data will be start of | ||
52 | * vendor unique payload, and the length of the payload is | ||
53 | * per event_datalen | ||
54 | * | ||
55 | * Note: When specifying vendor_id, be sure to read the Vendor Type and ID | ||
56 | * formatting requirements specified in scsi_netlink.h | ||
57 | */ | ||
58 | struct fc_nl_event { | ||
59 | struct scsi_nl_hdr snlh; /* must be 1st element ! */ | ||
60 | uint64_t seconds; | ||
61 | uint32_t vendor_id; | ||
62 | uint16_t host_no; | ||
63 | uint16_t event_datalen; | ||
64 | uint32_t event_num; | ||
65 | uint32_t event_code; | ||
66 | uint32_t event_data; | ||
67 | } __attribute__((aligned(sizeof(uint64_t)))); | ||
68 | |||
69 | |||
70 | #endif /* SCSI_NETLINK_FC_H */ | ||
71 | |||
diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index c74be5dabfeb..f91c5358af3a 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h | |||
@@ -29,6 +29,7 @@ | |||
29 | 29 | ||
30 | #include <linux/sched.h> | 30 | #include <linux/sched.h> |
31 | #include <scsi/scsi.h> | 31 | #include <scsi/scsi.h> |
32 | #include <scsi/scsi_netlink.h> | ||
32 | 33 | ||
33 | struct scsi_transport_template; | 34 | struct scsi_transport_template; |
34 | 35 | ||
@@ -284,6 +285,30 @@ struct fc_host_statistics { | |||
284 | 285 | ||
285 | 286 | ||
286 | /* | 287 | /* |
288 | * FC Event Codes - Polled and Async, following FC HBAAPI v2.0 guidelines | ||
289 | */ | ||
290 | |||
291 | /* | ||
292 | * fc_host_event_code: If you alter this, you also need to alter | ||
293 | * scsi_transport_fc.c (for the ascii descriptions). | ||
294 | */ | ||
295 | enum fc_host_event_code { | ||
296 | FCH_EVT_LIP = 0x1, | ||
297 | FCH_EVT_LINKUP = 0x2, | ||
298 | FCH_EVT_LINKDOWN = 0x3, | ||
299 | FCH_EVT_LIPRESET = 0x4, | ||
300 | FCH_EVT_RSCN = 0x5, | ||
301 | FCH_EVT_ADAPTER_CHANGE = 0x103, | ||
302 | FCH_EVT_PORT_UNKNOWN = 0x200, | ||
303 | FCH_EVT_PORT_OFFLINE = 0x201, | ||
304 | FCH_EVT_PORT_ONLINE = 0x202, | ||
305 | FCH_EVT_PORT_FABRIC = 0x204, | ||
306 | FCH_EVT_LINK_UNKNOWN = 0x500, | ||
307 | FCH_EVT_VENDOR_UNIQUE = 0xffff, | ||
308 | }; | ||
309 | |||
310 | |||
311 | /* | ||
287 | * FC Local Port (Host) Attributes | 312 | * FC Local Port (Host) Attributes |
288 | * | 313 | * |
289 | * Attributes are based on HBAAPI V2.0 definitions. | 314 | * Attributes are based on HBAAPI V2.0 definitions. |
@@ -526,5 +551,14 @@ struct fc_rport *fc_remote_port_add(struct Scsi_Host *shost, | |||
526 | void fc_remote_port_delete(struct fc_rport *rport); | 551 | void fc_remote_port_delete(struct fc_rport *rport); |
527 | void fc_remote_port_rolechg(struct fc_rport *rport, u32 roles); | 552 | void fc_remote_port_rolechg(struct fc_rport *rport, u32 roles); |
528 | int scsi_is_fc_rport(const struct device *); | 553 | int scsi_is_fc_rport(const struct device *); |
554 | u32 fc_get_event_number(void); | ||
555 | void fc_host_post_event(struct Scsi_Host *shost, u32 event_number, | ||
556 | enum fc_host_event_code event_code, u32 event_data); | ||
557 | void fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, | ||
558 | u32 data_len, char * data_buf, u32 vendor_id); | ||
559 | /* Note: when specifying vendor_id to fc_host_post_vendor_event() | ||
560 | * be sure to read the Vendor Type and ID formatting requirements | ||
561 | * specified in scsi_netlink.h | ||
562 | */ | ||
529 | 563 | ||
530 | #endif /* SCSI_TRANSPORT_FC_H */ | 564 | #endif /* SCSI_TRANSPORT_FC_H */ |