aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJames Smart <James.Smart@Emulex.Com>2006-08-18 17:30:09 -0400
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2006-09-02 16:33:49 -0400
commit84314fd4740ad73550c76dee4a9578979d84af48 (patch)
treef8902dbe4134f9bab4f6886bb6c0b2a30797ceaa /drivers
parentdeb81d80ba27da8dfabc29ccb5977db8f4942a0a (diff)
[SCSI] SCSI and FC Transport: add netlink support for posting of transport events
This patch formally adds support for the posting of FC events via netlink. It is a followup to the original RFC at: http://marc.theaimsgroup.com/?l=linux-scsi&m=114530667923464&w=2 and the initial posting at: http://marc.theaimsgroup.com/?l=linux-scsi&m=115507374832500&w=2 The patch has been updated to optimize the send path, per the discussions in the initial posting. Per discussions at the Storage Summit and at OLS, we are to use netlink for async events from transports. Also per discussions, to avoid a netlink protocol per transport, I've create a single NETLINK_SCSITRANSPORT protocol, which can then be used by all transports. This patch: - Creates new files scsi_netlink.c and scsi_netlink.h, which contains the single and shared definitions for the SCSI Transport. It is tied into the base SCSI subsystem intialization. Contains a single interface routine, scsi_send_transport_event(), for a transport to send an event (via multicast to a protocol specific group). - Creates a new scsi_netlink_fc.h file, which contains the FC netlink event messages - Adds 3 new routines to the fc transport: fc_get_event_number() - to get a FC event # fc_host_post_event() - to send a simple FC event (32 bits of data) fc_host_post_vendor_event() - to send a Vendor unique event, with arbitrary amounts of data. Note: the separation of event number allows for a LLD to send a standard event, followed by vendor-specific data for the event. Note: This patch assumes 2 prior fc transport patches have been installed: http://marc.theaimsgroup.com/?l=linux-scsi&m=115555807316329&w=2 http://marc.theaimsgroup.com/?l=linux-scsi&m=115581614930261&w=2 Sorry - next time I'll do something like making these individual patches of the same posting when I know they'll be posted closely together. Signed-off-by: James Smart <James.Smart@emulex.com> Tidy up configuration not to make SCSI always select NET Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/Kconfig6
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/scsi.c3
-rw-r--r--drivers/scsi/scsi_netlink.c199
-rw-r--r--drivers/scsi/scsi_priv.h11
-rw-r--r--drivers/scsi/scsi_transport_fc.c200
6 files changed, 419 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
30config SCSI_NETLINK
31 tristate
32 default n
33 select NET
34
30config SCSI_PROC_FS 35config 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
222config SCSI_FC_ATTRS 227config 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
162scsi_mod-$(CONFIG_SCSI_NETLINK) += scsi_netlink.o
162scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o 163scsi_mod-$(CONFIG_SYSCTL) += scsi_sysctl.o
163scsi_mod-$(CONFIG_SCSI_PROC_FS) += scsi_proc.o 164scsi_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
1139static void __exit exit_scsi(void) 1141static 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
30struct sock *scsi_nl_sock = NULL;
31EXPORT_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 **/
42static void
43scsi_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
92next_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 **/
110static void
111scsi_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 **/
131static int
132scsi_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
147static 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 **/
158void
159scsi_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 **/
188void
189scsi_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;
8struct scsi_device; 8struct scsi_device;
9struct scsi_host_template; 9struct scsi_host_template;
10struct Scsi_Host; 10struct Scsi_Host;
11struct scsi_nl_hdr;
11 12
12 13
13/* 14/*
@@ -110,6 +111,16 @@ extern void __scsi_remove_device(struct scsi_device *);
110 111
111extern struct bus_type scsi_bus_type; 112extern struct bus_type scsi_bus_type;
112 113
114/* scsi_netlink.c */
115#ifdef CONFIG_SCSI_NETLINK
116extern void scsi_netlink_init(void);
117extern void scsi_netlink_exit(void);
118extern struct sock *scsi_nl_sock;
119#else
120static inline void scsi_netlink_init(void) {}
121static 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
37static int fc_queue_work(struct Scsi_Host *, struct work_struct *); 40static 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 */
100static 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};
117fc_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 */
97static struct { 123static 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
410static 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 **/
420u32
421fc_get_event_number(void)
422{
423 return atomic_add_return(1, &fc_event_seq);
424}
425EXPORT_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 **/
439void
440fc_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
489send_fail_skb:
490 kfree_skb(skb);
491send_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}
499EXPORT_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 **/
514void
515fc_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
563send_vendor_fail_skb:
564 kfree_skb(skb);
565send_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}
571EXPORT_SYMBOL(fc_host_post_vendor_event);
572
573
380 574
381static __init int fc_transport_init(void) 575static __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);