aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/scsi_transport_fc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/scsi_transport_fc.c')
-rw-r--r--drivers/scsi/scsi_transport_fc.c200
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
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);