diff options
-rw-r--r-- | drivers/scsi/scsi_netlink.c | 523 | ||||
-rw-r--r-- | include/scsi/scsi_netlink.h | 62 |
2 files changed, 576 insertions, 9 deletions
diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index ae7ed9a22662..b37e133de805 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/time.h> | 21 | #include <linux/time.h> |
22 | #include <linux/jiffies.h> | 22 | #include <linux/jiffies.h> |
23 | #include <linux/security.h> | 23 | #include <linux/security.h> |
24 | #include <linux/delay.h> | ||
24 | #include <net/sock.h> | 25 | #include <net/sock.h> |
25 | #include <net/netlink.h> | 26 | #include <net/netlink.h> |
26 | 27 | ||
@@ -30,6 +31,39 @@ | |||
30 | struct sock *scsi_nl_sock = NULL; | 31 | struct sock *scsi_nl_sock = NULL; |
31 | EXPORT_SYMBOL_GPL(scsi_nl_sock); | 32 | EXPORT_SYMBOL_GPL(scsi_nl_sock); |
32 | 33 | ||
34 | static DEFINE_SPINLOCK(scsi_nl_lock); | ||
35 | static struct list_head scsi_nl_drivers; | ||
36 | |||
37 | static u32 scsi_nl_state; | ||
38 | #define STATE_EHANDLER_BSY 0x00000001 | ||
39 | |||
40 | struct scsi_nl_transport { | ||
41 | int (*msg_handler)(struct sk_buff *); | ||
42 | void (*event_handler)(struct notifier_block *, unsigned long, void *); | ||
43 | unsigned int refcnt; | ||
44 | int flags; | ||
45 | }; | ||
46 | |||
47 | /* flags values (bit flags) */ | ||
48 | #define HANDLER_DELETING 0x1 | ||
49 | |||
50 | static struct scsi_nl_transport transports[SCSI_NL_MAX_TRANSPORTS] = | ||
51 | { {NULL, }, }; | ||
52 | |||
53 | |||
54 | struct scsi_nl_drvr { | ||
55 | struct list_head next; | ||
56 | int (*dmsg_handler)(struct Scsi_Host *shost, void *payload, | ||
57 | u32 len, u32 pid); | ||
58 | void (*devt_handler)(struct notifier_block *nb, | ||
59 | unsigned long event, void *notify_ptr); | ||
60 | struct scsi_host_template *hostt; | ||
61 | u64 vendor_id; | ||
62 | unsigned int refcnt; | ||
63 | int flags; | ||
64 | }; | ||
65 | |||
66 | |||
33 | 67 | ||
34 | /** | 68 | /** |
35 | * scsi_nl_rcv_msg - Receive message handler. | 69 | * scsi_nl_rcv_msg - Receive message handler. |
@@ -45,8 +79,9 @@ scsi_nl_rcv_msg(struct sk_buff *skb) | |||
45 | { | 79 | { |
46 | struct nlmsghdr *nlh; | 80 | struct nlmsghdr *nlh; |
47 | struct scsi_nl_hdr *hdr; | 81 | struct scsi_nl_hdr *hdr; |
48 | uint32_t rlen; | 82 | unsigned long flags; |
49 | int err; | 83 | u32 rlen; |
84 | int err, tport; | ||
50 | 85 | ||
51 | while (skb->len >= NLMSG_SPACE(0)) { | 86 | while (skb->len >= NLMSG_SPACE(0)) { |
52 | err = 0; | 87 | err = 0; |
@@ -65,7 +100,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb) | |||
65 | 100 | ||
66 | if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) { | 101 | if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) { |
67 | err = -EBADMSG; | 102 | err = -EBADMSG; |
68 | return; | 103 | goto next_msg; |
69 | } | 104 | } |
70 | 105 | ||
71 | hdr = NLMSG_DATA(nlh); | 106 | hdr = NLMSG_DATA(nlh); |
@@ -83,12 +118,27 @@ scsi_nl_rcv_msg(struct sk_buff *skb) | |||
83 | if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) { | 118 | if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) { |
84 | printk(KERN_WARNING "%s: discarding partial message\n", | 119 | printk(KERN_WARNING "%s: discarding partial message\n", |
85 | __func__); | 120 | __func__); |
86 | return; | 121 | goto next_msg; |
87 | } | 122 | } |
88 | 123 | ||
89 | /* | 124 | /* |
90 | * We currently don't support anyone sending us a message | 125 | * Deliver message to the appropriate transport |
91 | */ | 126 | */ |
127 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
128 | |||
129 | tport = hdr->transport; | ||
130 | if ((tport < SCSI_NL_MAX_TRANSPORTS) && | ||
131 | !(transports[tport].flags & HANDLER_DELETING) && | ||
132 | (transports[tport].msg_handler)) { | ||
133 | transports[tport].refcnt++; | ||
134 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
135 | err = transports[tport].msg_handler(skb); | ||
136 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
137 | transports[tport].refcnt--; | ||
138 | } else | ||
139 | err = -ENOENT; | ||
140 | |||
141 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
92 | 142 | ||
93 | next_msg: | 143 | next_msg: |
94 | if ((err) || (nlh->nlmsg_flags & NLM_F_ACK)) | 144 | if ((err) || (nlh->nlmsg_flags & NLM_F_ACK)) |
@@ -110,14 +160,42 @@ static int | |||
110 | scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr) | 160 | scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr) |
111 | { | 161 | { |
112 | struct netlink_notify *n = ptr; | 162 | struct netlink_notify *n = ptr; |
163 | struct scsi_nl_drvr *driver; | ||
164 | unsigned long flags; | ||
165 | int tport; | ||
113 | 166 | ||
114 | if (n->protocol != NETLINK_SCSITRANSPORT) | 167 | if (n->protocol != NETLINK_SCSITRANSPORT) |
115 | return NOTIFY_DONE; | 168 | return NOTIFY_DONE; |
116 | 169 | ||
170 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
171 | scsi_nl_state |= STATE_EHANDLER_BSY; | ||
172 | |||
117 | /* | 173 | /* |
118 | * Currently, we are not tracking PID's, etc. There is nothing | 174 | * Pass event on to any transports that may be listening |
119 | * to handle. | ||
120 | */ | 175 | */ |
176 | for (tport = 0; tport < SCSI_NL_MAX_TRANSPORTS; tport++) { | ||
177 | if (!(transports[tport].flags & HANDLER_DELETING) && | ||
178 | (transports[tport].event_handler)) { | ||
179 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
180 | transports[tport].event_handler(this, event, ptr); | ||
181 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * Pass event on to any drivers that may be listening | ||
187 | */ | ||
188 | list_for_each_entry(driver, &scsi_nl_drivers, next) { | ||
189 | if (!(driver->flags & HANDLER_DELETING) && | ||
190 | (driver->devt_handler)) { | ||
191 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
192 | driver->devt_handler(this, event, ptr); | ||
193 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | scsi_nl_state &= ~STATE_EHANDLER_BSY; | ||
198 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
121 | 199 | ||
122 | return NOTIFY_DONE; | 200 | return NOTIFY_DONE; |
123 | } | 201 | } |
@@ -128,7 +206,281 @@ static struct notifier_block scsi_netlink_notifier = { | |||
128 | 206 | ||
129 | 207 | ||
130 | /** | 208 | /** |
131 | * scsi_netlink_init - Called by SCSI subsystem to intialize the SCSI transport netlink interface | 209 | * GENERIC SCSI transport receive and event handlers |
210 | **/ | ||
211 | |||
212 | /** | ||
213 | * scsi_generic_msg_handler - receive message handler for GENERIC transport | ||
214 | * messages | ||
215 | * | ||
216 | * @skb: socket receive buffer | ||
217 | * | ||
218 | **/ | ||
219 | static int | ||
220 | scsi_generic_msg_handler(struct sk_buff *skb) | ||
221 | { | ||
222 | struct nlmsghdr *nlh = nlmsg_hdr(skb); | ||
223 | struct scsi_nl_hdr *snlh = NLMSG_DATA(nlh); | ||
224 | struct scsi_nl_drvr *driver; | ||
225 | struct Scsi_Host *shost; | ||
226 | unsigned long flags; | ||
227 | int err = 0, match, pid; | ||
228 | |||
229 | pid = NETLINK_CREDS(skb)->pid; | ||
230 | |||
231 | switch (snlh->msgtype) { | ||
232 | case SCSI_NL_SHOST_VENDOR: | ||
233 | { | ||
234 | struct scsi_nl_host_vendor_msg *msg = NLMSG_DATA(nlh); | ||
235 | |||
236 | /* Locate the driver that corresponds to the message */ | ||
237 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
238 | match = 0; | ||
239 | list_for_each_entry(driver, &scsi_nl_drivers, next) { | ||
240 | if (driver->vendor_id == msg->vendor_id) { | ||
241 | match = 1; | ||
242 | break; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | if ((!match) || (!driver->dmsg_handler)) { | ||
247 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
248 | err = -ESRCH; | ||
249 | goto rcv_exit; | ||
250 | } | ||
251 | |||
252 | if (driver->flags & HANDLER_DELETING) { | ||
253 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
254 | err = -ESHUTDOWN; | ||
255 | goto rcv_exit; | ||
256 | } | ||
257 | |||
258 | driver->refcnt++; | ||
259 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
260 | |||
261 | |||
262 | /* if successful, scsi_host_lookup takes a shost reference */ | ||
263 | shost = scsi_host_lookup(msg->host_no); | ||
264 | if (!shost) { | ||
265 | err = -ENODEV; | ||
266 | goto driver_exit; | ||
267 | } | ||
268 | |||
269 | /* is this host owned by the vendor ? */ | ||
270 | if (shost->hostt != driver->hostt) { | ||
271 | err = -EINVAL; | ||
272 | goto vendormsg_put; | ||
273 | } | ||
274 | |||
275 | /* pass message on to the driver */ | ||
276 | err = driver->dmsg_handler(shost, (void *)&msg[1], | ||
277 | msg->vmsg_datalen, pid); | ||
278 | |||
279 | vendormsg_put: | ||
280 | /* release reference by scsi_host_lookup */ | ||
281 | scsi_host_put(shost); | ||
282 | |||
283 | driver_exit: | ||
284 | /* release our own reference on the registration object */ | ||
285 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
286 | driver->refcnt--; | ||
287 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
288 | break; | ||
289 | } | ||
290 | |||
291 | default: | ||
292 | err = -EBADR; | ||
293 | break; | ||
294 | } | ||
295 | |||
296 | rcv_exit: | ||
297 | if (err) | ||
298 | printk(KERN_WARNING "%s: Msgtype %d failed - err %d\n", | ||
299 | __func__, snlh->msgtype, err); | ||
300 | return err; | ||
301 | } | ||
302 | |||
303 | |||
304 | /** | ||
305 | * scsi_nl_add_transport - | ||
306 | * Registers message and event handlers for a transport. Enables | ||
307 | * receipt of netlink messages and events to a transport. | ||
308 | * | ||
309 | * @tport: transport registering handlers | ||
310 | * @msg_handler: receive message handler callback | ||
311 | * @event_handler: receive event handler callback | ||
312 | **/ | ||
313 | int | ||
314 | scsi_nl_add_transport(u8 tport, | ||
315 | int (*msg_handler)(struct sk_buff *), | ||
316 | void (*event_handler)(struct notifier_block *, unsigned long, void *)) | ||
317 | { | ||
318 | unsigned long flags; | ||
319 | int err = 0; | ||
320 | |||
321 | if (tport >= SCSI_NL_MAX_TRANSPORTS) | ||
322 | return -EINVAL; | ||
323 | |||
324 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
325 | |||
326 | if (scsi_nl_state & STATE_EHANDLER_BSY) { | ||
327 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
328 | msleep(1); | ||
329 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
330 | } | ||
331 | |||
332 | if (transports[tport].msg_handler || transports[tport].event_handler) { | ||
333 | err = -EALREADY; | ||
334 | goto register_out; | ||
335 | } | ||
336 | |||
337 | transports[tport].msg_handler = msg_handler; | ||
338 | transports[tport].event_handler = event_handler; | ||
339 | transports[tport].flags = 0; | ||
340 | transports[tport].refcnt = 0; | ||
341 | |||
342 | register_out: | ||
343 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
344 | |||
345 | return err; | ||
346 | } | ||
347 | EXPORT_SYMBOL_GPL(scsi_nl_add_transport); | ||
348 | |||
349 | |||
350 | /** | ||
351 | * scsi_nl_remove_transport - | ||
352 | * Disable transport receiption of messages and events | ||
353 | * | ||
354 | * @tport: transport deregistering handlers | ||
355 | * | ||
356 | **/ | ||
357 | void | ||
358 | scsi_nl_remove_transport(u8 tport) | ||
359 | { | ||
360 | unsigned long flags; | ||
361 | |||
362 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
363 | if (scsi_nl_state & STATE_EHANDLER_BSY) { | ||
364 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
365 | msleep(1); | ||
366 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
367 | } | ||
368 | |||
369 | if (tport < SCSI_NL_MAX_TRANSPORTS) { | ||
370 | transports[tport].flags |= HANDLER_DELETING; | ||
371 | |||
372 | while (transports[tport].refcnt != 0) { | ||
373 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
374 | schedule_timeout_uninterruptible(HZ/4); | ||
375 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
376 | } | ||
377 | transports[tport].msg_handler = NULL; | ||
378 | transports[tport].event_handler = NULL; | ||
379 | transports[tport].flags = 0; | ||
380 | } | ||
381 | |||
382 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
383 | |||
384 | return; | ||
385 | } | ||
386 | EXPORT_SYMBOL_GPL(scsi_nl_remove_transport); | ||
387 | |||
388 | |||
389 | /** | ||
390 | * scsi_nl_add_driver - | ||
391 | * A driver is registering its interfaces for SCSI netlink messages | ||
392 | * | ||
393 | * @vendor_id: A unique identification value for the driver. | ||
394 | * @hostt: address of the driver's host template. Used | ||
395 | * to verify an shost is bound to the driver | ||
396 | * @nlmsg_handler: receive message handler callback | ||
397 | * @nlevt_handler: receive event handler callback | ||
398 | * | ||
399 | * Returns: | ||
400 | * 0 on Success | ||
401 | * error result otherwise | ||
402 | **/ | ||
403 | int | ||
404 | scsi_nl_add_driver(u64 vendor_id, struct scsi_host_template *hostt, | ||
405 | int (*nlmsg_handler)(struct Scsi_Host *shost, void *payload, | ||
406 | u32 len, u32 pid), | ||
407 | void (*nlevt_handler)(struct notifier_block *nb, | ||
408 | unsigned long event, void *notify_ptr)) | ||
409 | { | ||
410 | struct scsi_nl_drvr *driver; | ||
411 | unsigned long flags; | ||
412 | |||
413 | driver = kzalloc(sizeof(*driver), GFP_KERNEL); | ||
414 | if (unlikely(!driver)) { | ||
415 | printk(KERN_ERR "%s: allocation failure\n", __func__); | ||
416 | return -ENOMEM; | ||
417 | } | ||
418 | |||
419 | driver->dmsg_handler = nlmsg_handler; | ||
420 | driver->devt_handler = nlevt_handler; | ||
421 | driver->hostt = hostt; | ||
422 | driver->vendor_id = vendor_id; | ||
423 | |||
424 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
425 | if (scsi_nl_state & STATE_EHANDLER_BSY) { | ||
426 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
427 | msleep(1); | ||
428 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
429 | } | ||
430 | list_add_tail(&driver->next, &scsi_nl_drivers); | ||
431 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | EXPORT_SYMBOL_GPL(scsi_nl_add_driver); | ||
436 | |||
437 | |||
438 | /** | ||
439 | * scsi_nl_remove_driver - | ||
440 | * An driver is unregistering with the SCSI netlink messages | ||
441 | * | ||
442 | * @vendor_id: The unique identification value for the driver. | ||
443 | **/ | ||
444 | void | ||
445 | scsi_nl_remove_driver(u64 vendor_id) | ||
446 | { | ||
447 | struct scsi_nl_drvr *driver; | ||
448 | unsigned long flags; | ||
449 | |||
450 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
451 | if (scsi_nl_state & STATE_EHANDLER_BSY) { | ||
452 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
453 | msleep(1); | ||
454 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
455 | } | ||
456 | |||
457 | list_for_each_entry(driver, &scsi_nl_drivers, next) { | ||
458 | if (driver->vendor_id == vendor_id) { | ||
459 | driver->flags |= HANDLER_DELETING; | ||
460 | while (driver->refcnt != 0) { | ||
461 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
462 | schedule_timeout_uninterruptible(HZ/4); | ||
463 | spin_lock_irqsave(&scsi_nl_lock, flags); | ||
464 | } | ||
465 | list_del(&driver->next); | ||
466 | kfree(driver); | ||
467 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
468 | return; | ||
469 | } | ||
470 | } | ||
471 | |||
472 | spin_unlock_irqrestore(&scsi_nl_lock, flags); | ||
473 | |||
474 | printk(KERN_ERR "%s: removal of driver failed - vendor_id 0x%llx\n", | ||
475 | __func__, (unsigned long long)vendor_id); | ||
476 | return; | ||
477 | } | ||
478 | EXPORT_SYMBOL_GPL(scsi_nl_remove_driver); | ||
479 | |||
480 | |||
481 | /** | ||
482 | * scsi_netlink_init - Called by SCSI subsystem to intialize | ||
483 | * the SCSI transport netlink interface | ||
132 | * | 484 | * |
133 | **/ | 485 | **/ |
134 | void | 486 | void |
@@ -136,6 +488,8 @@ scsi_netlink_init(void) | |||
136 | { | 488 | { |
137 | int error; | 489 | int error; |
138 | 490 | ||
491 | INIT_LIST_HEAD(&scsi_nl_drivers); | ||
492 | |||
139 | error = netlink_register_notifier(&scsi_netlink_notifier); | 493 | error = netlink_register_notifier(&scsi_netlink_notifier); |
140 | if (error) { | 494 | if (error) { |
141 | printk(KERN_ERR "%s: register of event handler failed - %d\n", | 495 | printk(KERN_ERR "%s: register of event handler failed - %d\n", |
@@ -150,8 +504,15 @@ scsi_netlink_init(void) | |||
150 | printk(KERN_ERR "%s: register of recieve handler failed\n", | 504 | printk(KERN_ERR "%s: register of recieve handler failed\n", |
151 | __func__); | 505 | __func__); |
152 | netlink_unregister_notifier(&scsi_netlink_notifier); | 506 | netlink_unregister_notifier(&scsi_netlink_notifier); |
507 | return; | ||
153 | } | 508 | } |
154 | 509 | ||
510 | /* Register the entry points for the generic SCSI transport */ | ||
511 | error = scsi_nl_add_transport(SCSI_NL_TRANSPORT, | ||
512 | scsi_generic_msg_handler, NULL); | ||
513 | if (error) | ||
514 | printk(KERN_ERR "%s: register of GENERIC transport handler" | ||
515 | " failed - %d\n", __func__, error); | ||
155 | return; | 516 | return; |
156 | } | 517 | } |
157 | 518 | ||
@@ -163,6 +524,8 @@ scsi_netlink_init(void) | |||
163 | void | 524 | void |
164 | scsi_netlink_exit(void) | 525 | scsi_netlink_exit(void) |
165 | { | 526 | { |
527 | scsi_nl_remove_transport(SCSI_NL_TRANSPORT); | ||
528 | |||
166 | if (scsi_nl_sock) { | 529 | if (scsi_nl_sock) { |
167 | netlink_kernel_release(scsi_nl_sock); | 530 | netlink_kernel_release(scsi_nl_sock); |
168 | netlink_unregister_notifier(&scsi_netlink_notifier); | 531 | netlink_unregister_notifier(&scsi_netlink_notifier); |
@@ -172,3 +535,147 @@ scsi_netlink_exit(void) | |||
172 | } | 535 | } |
173 | 536 | ||
174 | 537 | ||
538 | /* | ||
539 | * Exported Interfaces | ||
540 | */ | ||
541 | |||
542 | /** | ||
543 | * scsi_nl_send_transport_msg - | ||
544 | * Generic function to send a single message from a SCSI transport to | ||
545 | * a single process | ||
546 | * | ||
547 | * @pid: receiving pid | ||
548 | * @hdr: message payload | ||
549 | * | ||
550 | **/ | ||
551 | void | ||
552 | scsi_nl_send_transport_msg(u32 pid, struct scsi_nl_hdr *hdr) | ||
553 | { | ||
554 | struct sk_buff *skb; | ||
555 | struct nlmsghdr *nlh; | ||
556 | const char *fn; | ||
557 | char *datab; | ||
558 | u32 len, skblen; | ||
559 | int err; | ||
560 | |||
561 | if (!scsi_nl_sock) { | ||
562 | err = -ENOENT; | ||
563 | fn = "netlink socket"; | ||
564 | goto msg_fail; | ||
565 | } | ||
566 | |||
567 | len = NLMSG_SPACE(hdr->msglen); | ||
568 | skblen = NLMSG_SPACE(len); | ||
569 | |||
570 | skb = alloc_skb(skblen, GFP_KERNEL); | ||
571 | if (!skb) { | ||
572 | err = -ENOBUFS; | ||
573 | fn = "alloc_skb"; | ||
574 | goto msg_fail; | ||
575 | } | ||
576 | |||
577 | nlh = nlmsg_put(skb, pid, 0, SCSI_TRANSPORT_MSG, len - sizeof(*nlh), 0); | ||
578 | if (!nlh) { | ||
579 | err = -ENOBUFS; | ||
580 | fn = "nlmsg_put"; | ||
581 | goto msg_fail_skb; | ||
582 | } | ||
583 | datab = NLMSG_DATA(nlh); | ||
584 | memcpy(datab, hdr, hdr->msglen); | ||
585 | |||
586 | err = nlmsg_unicast(scsi_nl_sock, skb, pid); | ||
587 | if (err < 0) { | ||
588 | fn = "nlmsg_unicast"; | ||
589 | /* nlmsg_unicast already kfree_skb'd */ | ||
590 | goto msg_fail; | ||
591 | } | ||
592 | |||
593 | return; | ||
594 | |||
595 | msg_fail_skb: | ||
596 | kfree_skb(skb); | ||
597 | msg_fail: | ||
598 | printk(KERN_WARNING | ||
599 | "%s: Dropped Message : pid %d Transport %d, msgtype x%x, " | ||
600 | "msglen %d: %s : err %d\n", | ||
601 | __func__, pid, hdr->transport, hdr->msgtype, hdr->msglen, | ||
602 | fn, err); | ||
603 | return; | ||
604 | } | ||
605 | EXPORT_SYMBOL_GPL(scsi_nl_send_transport_msg); | ||
606 | |||
607 | |||
608 | /** | ||
609 | * scsi_nl_send_vendor_msg - called to send a shost vendor unique message | ||
610 | * to a specific process id. | ||
611 | * | ||
612 | * @pid: process id of the receiver | ||
613 | * @host_no: host # sending the message | ||
614 | * @vendor_id: unique identifier for the driver's vendor | ||
615 | * @data_len: amount, in bytes, of vendor unique payload data | ||
616 | * @data_buf: pointer to vendor unique data buffer | ||
617 | * | ||
618 | * Returns: | ||
619 | * 0 on succesful return | ||
620 | * otherwise, failing error code | ||
621 | * | ||
622 | * Notes: | ||
623 | * This routine assumes no locks are held on entry. | ||
624 | */ | ||
625 | int | ||
626 | scsi_nl_send_vendor_msg(u32 pid, unsigned short host_no, u64 vendor_id, | ||
627 | char *data_buf, u32 data_len) | ||
628 | { | ||
629 | struct sk_buff *skb; | ||
630 | struct nlmsghdr *nlh; | ||
631 | struct scsi_nl_host_vendor_msg *msg; | ||
632 | u32 len, skblen; | ||
633 | int err; | ||
634 | |||
635 | if (!scsi_nl_sock) { | ||
636 | err = -ENOENT; | ||
637 | goto send_vendor_fail; | ||
638 | } | ||
639 | |||
640 | len = SCSI_NL_MSGALIGN(sizeof(*msg) + data_len); | ||
641 | skblen = NLMSG_SPACE(len); | ||
642 | |||
643 | skb = alloc_skb(skblen, GFP_KERNEL); | ||
644 | if (!skb) { | ||
645 | err = -ENOBUFS; | ||
646 | goto send_vendor_fail; | ||
647 | } | ||
648 | |||
649 | nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, | ||
650 | skblen - sizeof(*nlh), 0); | ||
651 | if (!nlh) { | ||
652 | err = -ENOBUFS; | ||
653 | goto send_vendor_fail_skb; | ||
654 | } | ||
655 | msg = NLMSG_DATA(nlh); | ||
656 | |||
657 | INIT_SCSI_NL_HDR(&msg->snlh, SCSI_NL_TRANSPORT, | ||
658 | SCSI_NL_SHOST_VENDOR, len); | ||
659 | msg->vendor_id = vendor_id; | ||
660 | msg->host_no = host_no; | ||
661 | msg->vmsg_datalen = data_len; /* bytes */ | ||
662 | memcpy(&msg[1], data_buf, data_len); | ||
663 | |||
664 | err = nlmsg_unicast(scsi_nl_sock, skb, pid); | ||
665 | if (err) | ||
666 | /* nlmsg_multicast already kfree_skb'd */ | ||
667 | goto send_vendor_fail; | ||
668 | |||
669 | return 0; | ||
670 | |||
671 | send_vendor_fail_skb: | ||
672 | kfree_skb(skb); | ||
673 | send_vendor_fail: | ||
674 | printk(KERN_WARNING | ||
675 | "%s: Dropped SCSI Msg : host %d vendor_unique - err %d\n", | ||
676 | __func__, host_no, err); | ||
677 | return err; | ||
678 | } | ||
679 | EXPORT_SYMBOL(scsi_nl_send_vendor_msg); | ||
680 | |||
681 | |||
diff --git a/include/scsi/scsi_netlink.h b/include/scsi/scsi_netlink.h index 8c1470cc8209..536752c40d41 100644 --- a/include/scsi/scsi_netlink.h +++ b/include/scsi/scsi_netlink.h | |||
@@ -22,6 +22,9 @@ | |||
22 | #ifndef SCSI_NETLINK_H | 22 | #ifndef SCSI_NETLINK_H |
23 | #define SCSI_NETLINK_H | 23 | #define SCSI_NETLINK_H |
24 | 24 | ||
25 | #include <linux/netlink.h> | ||
26 | |||
27 | |||
25 | /* | 28 | /* |
26 | * This file intended to be included by both kernel and user space | 29 | * This file intended to be included by both kernel and user space |
27 | */ | 30 | */ |
@@ -55,7 +58,41 @@ struct scsi_nl_hdr { | |||
55 | #define SCSI_NL_TRANSPORT_FC 1 | 58 | #define SCSI_NL_TRANSPORT_FC 1 |
56 | #define SCSI_NL_MAX_TRANSPORTS 2 | 59 | #define SCSI_NL_MAX_TRANSPORTS 2 |
57 | 60 | ||
58 | /* scsi_nl_hdr->msgtype values are defined in each transport */ | 61 | /* Transport-based scsi_nl_hdr->msgtype values are defined in each transport */ |
62 | |||
63 | /* | ||
64 | * GENERIC SCSI scsi_nl_hdr->msgtype Values | ||
65 | */ | ||
66 | /* kernel -> user */ | ||
67 | #define SCSI_NL_SHOST_VENDOR 0x0001 | ||
68 | /* user -> kernel */ | ||
69 | /* SCSI_NL_SHOST_VENDOR msgtype is kernel->user and user->kernel */ | ||
70 | |||
71 | |||
72 | /* | ||
73 | * Message Structures : | ||
74 | */ | ||
75 | |||
76 | /* macro to round up message lengths to 8byte boundary */ | ||
77 | #define SCSI_NL_MSGALIGN(len) (((len) + 7) & ~7) | ||
78 | |||
79 | |||
80 | /* | ||
81 | * SCSI HOST Vendor Unique messages : | ||
82 | * SCSI_NL_SHOST_VENDOR | ||
83 | * | ||
84 | * Note: The Vendor Unique message payload will begin directly after | ||
85 | * this structure, with the length of the payload per vmsg_datalen. | ||
86 | * | ||
87 | * Note: When specifying vendor_id, be sure to read the Vendor Type and ID | ||
88 | * formatting requirements specified below | ||
89 | */ | ||
90 | struct scsi_nl_host_vendor_msg { | ||
91 | struct scsi_nl_hdr snlh; /* must be 1st element ! */ | ||
92 | uint64_t vendor_id; | ||
93 | uint16_t host_no; | ||
94 | uint16_t vmsg_datalen; | ||
95 | } __attribute__((aligned(sizeof(uint64_t)))); | ||
59 | 96 | ||
60 | 97 | ||
61 | /* | 98 | /* |
@@ -83,5 +120,28 @@ struct scsi_nl_hdr { | |||
83 | } | 120 | } |
84 | 121 | ||
85 | 122 | ||
123 | #ifdef __KERNEL__ | ||
124 | |||
125 | #include <scsi/scsi_host.h> | ||
126 | |||
127 | /* Exported Kernel Interfaces */ | ||
128 | int scsi_nl_add_transport(u8 tport, | ||
129 | int (*msg_handler)(struct sk_buff *), | ||
130 | void (*event_handler)(struct notifier_block *, unsigned long, void *)); | ||
131 | void scsi_nl_remove_transport(u8 tport); | ||
132 | |||
133 | int scsi_nl_add_driver(u64 vendor_id, struct scsi_host_template *hostt, | ||
134 | int (*nlmsg_handler)(struct Scsi_Host *shost, void *payload, | ||
135 | u32 len, u32 pid), | ||
136 | void (*nlevt_handler)(struct notifier_block *nb, | ||
137 | unsigned long event, void *notify_ptr)); | ||
138 | void scsi_nl_remove_driver(u64 vendor_id); | ||
139 | |||
140 | void scsi_nl_send_transport_msg(u32 pid, struct scsi_nl_hdr *hdr); | ||
141 | int scsi_nl_send_vendor_msg(u32 pid, unsigned short host_no, u64 vendor_id, | ||
142 | char *data_buf, u32 data_len); | ||
143 | |||
144 | #endif /* __KERNEL__ */ | ||
145 | |||
86 | #endif /* SCSI_NETLINK_H */ | 146 | #endif /* SCSI_NETLINK_H */ |
87 | 147 | ||