|
@@ -32,6 +32,9 @@
|
|
|
#include <scsi/scsi_transport.h>
|
|
|
#include <scsi/scsi_transport_fc.h>
|
|
|
#include <scsi/scsi_cmnd.h>
|
|
|
+#include <linux/netlink.h>
|
|
|
+#include <net/netlink.h>
|
|
|
+#include <scsi/scsi_netlink_fc.h>
|
|
|
#include "scsi_priv.h"
|
|
|
|
|
|
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)
|
|
|
#define FC_PORTTYPE_MAX_NAMELEN 50
|
|
|
|
|
|
|
|
|
+/* Convert fc_host_event_code values to ascii string name */
|
|
|
+static const struct {
|
|
|
+ enum fc_host_event_code value;
|
|
|
+ char *name;
|
|
|
+} fc_host_event_code_names[] = {
|
|
|
+ { FCH_EVT_LIP, "lip" },
|
|
|
+ { FCH_EVT_LINKUP, "link_up" },
|
|
|
+ { FCH_EVT_LINKDOWN, "link_down" },
|
|
|
+ { FCH_EVT_LIPRESET, "lip_reset" },
|
|
|
+ { FCH_EVT_RSCN, "rscn" },
|
|
|
+ { FCH_EVT_ADAPTER_CHANGE, "adapter_chg" },
|
|
|
+ { FCH_EVT_PORT_UNKNOWN, "port_unknown" },
|
|
|
+ { FCH_EVT_PORT_ONLINE, "port_online" },
|
|
|
+ { FCH_EVT_PORT_OFFLINE, "port_offline" },
|
|
|
+ { FCH_EVT_PORT_FABRIC, "port_fabric" },
|
|
|
+ { FCH_EVT_LINK_UNKNOWN, "link_unknown" },
|
|
|
+ { FCH_EVT_VENDOR_UNIQUE, "vendor_unique" },
|
|
|
+};
|
|
|
+fc_enum_name_search(host_event_code, fc_host_event_code,
|
|
|
+ fc_host_event_code_names)
|
|
|
+#define FC_HOST_EVENT_CODE_MAX_NAMELEN 30
|
|
|
+
|
|
|
+
|
|
|
/* Convert fc_port_state values to ascii string name */
|
|
|
static struct {
|
|
|
enum fc_port_state value;
|
|
@@ -377,10 +403,182 @@ MODULE_PARM_DESC(dev_loss_tmo,
|
|
|
" exceeded, the scsi target is removed. Value should be"
|
|
|
" between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT.");
|
|
|
|
|
|
+/**
|
|
|
+ * Netlink Infrastructure
|
|
|
+ **/
|
|
|
+
|
|
|
+static atomic_t fc_event_seq;
|
|
|
+
|
|
|
+/**
|
|
|
+ * fc_get_event_number - Obtain the next sequential FC event number
|
|
|
+ *
|
|
|
+ * Notes:
|
|
|
+ * We could have inline'd this, but it would have required fc_event_seq to
|
|
|
+ * be exposed. For now, live with the subroutine call.
|
|
|
+ * Atomic used to avoid lock/unlock...
|
|
|
+ **/
|
|
|
+u32
|
|
|
+fc_get_event_number(void)
|
|
|
+{
|
|
|
+ return atomic_add_return(1, &fc_event_seq);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(fc_get_event_number);
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * fc_host_post_event - called to post an even on an fc_host.
|
|
|
+ *
|
|
|
+ * @shost: host the event occurred on
|
|
|
+ * @event_number: fc event number obtained from get_fc_event_number()
|
|
|
+ * @event_code: fc_host event being posted
|
|
|
+ * @event_data: 32bits of data for the event being posted
|
|
|
+ *
|
|
|
+ * Notes:
|
|
|
+ * This routine assumes no locks are held on entry.
|
|
|
+ **/
|
|
|
+void
|
|
|
+fc_host_post_event(struct Scsi_Host *shost, u32 event_number,
|
|
|
+ enum fc_host_event_code event_code, u32 event_data)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ struct nlmsghdr *nlh;
|
|
|
+ struct fc_nl_event *event;
|
|
|
+ const char *name;
|
|
|
+ u32 len, skblen;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!scsi_nl_sock) {
|
|
|
+ err = -ENOENT;
|
|
|
+ goto send_fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = FC_NL_MSGALIGN(sizeof(*event));
|
|
|
+ skblen = NLMSG_SPACE(len);
|
|
|
+
|
|
|
+ skb = alloc_skb(skblen, GFP_KERNEL);
|
|
|
+ if (!skb) {
|
|
|
+ err = -ENOBUFS;
|
|
|
+ goto send_fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG,
|
|
|
+ skblen - sizeof(*nlh), 0);
|
|
|
+ if (!nlh) {
|
|
|
+ err = -ENOBUFS;
|
|
|
+ goto send_fail_skb;
|
|
|
+ }
|
|
|
+ event = NLMSG_DATA(nlh);
|
|
|
+
|
|
|
+ INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC,
|
|
|
+ FC_NL_ASYNC_EVENT, len);
|
|
|
+ event->seconds = get_seconds();
|
|
|
+ event->vendor_id = 0;
|
|
|
+ event->host_no = shost->host_no;
|
|
|
+ event->event_datalen = sizeof(u32); /* bytes */
|
|
|
+ event->event_num = event_number;
|
|
|
+ event->event_code = event_code;
|
|
|
+ event->event_data = event_data;
|
|
|
+
|
|
|
+ err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS);
|
|
|
+ if (err && (err != -ESRCH)) /* filter no recipient errors */
|
|
|
+ /* nlmsg_multicast already kfree_skb'd */
|
|
|
+ goto send_fail;
|
|
|
+
|
|
|
+ return;
|
|
|
+
|
|
|
+send_fail_skb:
|
|
|
+ kfree_skb(skb);
|
|
|
+send_fail:
|
|
|
+ name = get_fc_host_event_code_name(event_code);
|
|
|
+ printk(KERN_WARNING
|
|
|
+ "%s: Dropped Event : host %d %s data 0x%08x - err %d\n",
|
|
|
+ __FUNCTION__, shost->host_no,
|
|
|
+ (name) ? name : "<unknown>", event_data, err);
|
|
|
+ return;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(fc_host_post_event);
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * fc_host_post_vendor_event - called to post a vendor unique event on
|
|
|
+ * a fc_host
|
|
|
+ *
|
|
|
+ * @shost: host the event occurred on
|
|
|
+ * @event_number: fc event number obtained from get_fc_event_number()
|
|
|
+ * @data_len: amount, in bytes, of vendor unique data
|
|
|
+ * @data_buf: pointer to vendor unique data
|
|
|
+ *
|
|
|
+ * Notes:
|
|
|
+ * This routine assumes no locks are held on entry.
|
|
|
+ **/
|
|
|
+void
|
|
|
+fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
|
|
|
+ u32 data_len, char * data_buf, u32 vendor_id)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ struct nlmsghdr *nlh;
|
|
|
+ struct fc_nl_event *event;
|
|
|
+ u32 len, skblen;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!scsi_nl_sock) {
|
|
|
+ err = -ENOENT;
|
|
|
+ goto send_vendor_fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = FC_NL_MSGALIGN(sizeof(*event) + data_len);
|
|
|
+ skblen = NLMSG_SPACE(len);
|
|
|
+
|
|
|
+ skb = alloc_skb(skblen, GFP_KERNEL);
|
|
|
+ if (!skb) {
|
|
|
+ err = -ENOBUFS;
|
|
|
+ goto send_vendor_fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG,
|
|
|
+ skblen - sizeof(*nlh), 0);
|
|
|
+ if (!nlh) {
|
|
|
+ err = -ENOBUFS;
|
|
|
+ goto send_vendor_fail_skb;
|
|
|
+ }
|
|
|
+ event = NLMSG_DATA(nlh);
|
|
|
+
|
|
|
+ INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC,
|
|
|
+ FC_NL_ASYNC_EVENT, len);
|
|
|
+ event->seconds = get_seconds();
|
|
|
+ event->vendor_id = vendor_id;
|
|
|
+ event->host_no = shost->host_no;
|
|
|
+ event->event_datalen = data_len; /* bytes */
|
|
|
+ event->event_num = event_number;
|
|
|
+ event->event_code = FCH_EVT_VENDOR_UNIQUE;
|
|
|
+ memcpy(&event->event_data, data_buf, data_len);
|
|
|
+
|
|
|
+ err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS);
|
|
|
+ if (err && (err != -ESRCH)) /* filter no recipient errors */
|
|
|
+ /* nlmsg_multicast already kfree_skb'd */
|
|
|
+ goto send_vendor_fail;
|
|
|
+
|
|
|
+ return;
|
|
|
+
|
|
|
+send_vendor_fail_skb:
|
|
|
+ kfree_skb(skb);
|
|
|
+send_vendor_fail:
|
|
|
+ printk(KERN_WARNING
|
|
|
+ "%s: Dropped Event : host %d vendor_unique - err %d\n",
|
|
|
+ __FUNCTION__, shost->host_no, err);
|
|
|
+ return;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(fc_host_post_vendor_event);
|
|
|
+
|
|
|
+
|
|
|
|
|
|
static __init int fc_transport_init(void)
|
|
|
{
|
|
|
- int error = transport_class_register(&fc_host_class);
|
|
|
+ int error;
|
|
|
+
|
|
|
+ atomic_set(&fc_event_seq, 0);
|
|
|
+
|
|
|
+ error = transport_class_register(&fc_host_class);
|
|
|
if (error)
|
|
|
return error;
|
|
|
error = transport_class_register(&fc_rport_class);
|