diff options
Diffstat (limited to 'drivers/scsi/scsi_transport_fc.c')
-rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 200 |
1 files changed, 199 insertions, 1 deletions
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); |