Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 84314fd4 authored by James Smart's avatar James Smart Committed by James Bottomley
Browse files

[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: default avatarJames Smart <James.Smart@emulex.com>

Tidy up configuration not to make SCSI always select NET

Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent deb81d80
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -27,6 +27,11 @@ config SCSI
	  However, do not compile this as a module if your root file system
	  (the one containing the directory /) is located on a SCSI device.

config SCSI_NETLINK
	tristate
	default	n
	select NET

config SCSI_PROC_FS
	bool "legacy /proc/scsi/ support"
	depends on SCSI && PROC_FS
@@ -222,6 +227,7 @@ config SCSI_SPI_ATTRS
config SCSI_FC_ATTRS
	tristate "FiberChannel Transport Attributes"
	depends on SCSI
	select SCSI_NETLINK
	help
	  If you wish to export transport-specific information about
	  each attached FiberChannel device to sysfs, say Y.
+1 −0
Original line number Diff line number Diff line
@@ -159,6 +159,7 @@ scsi_mod-y += scsi.o hosts.o scsi_ioctl.o constants.o \
				   scsicam.o scsi_error.o scsi_lib.o \
				   scsi_scan.o scsi_sysfs.o \
				   scsi_devinfo.o
scsi_mod-$(CONFIG_SCSI_NETLINK)	+= scsi_netlink.o
scsi_mod-$(CONFIG_SYSCTL)	+= scsi_sysctl.o
scsi_mod-$(CONFIG_SCSI_PROC_FS)	+= scsi_proc.o

+3 −0
Original line number Diff line number Diff line
@@ -1118,6 +1118,8 @@ static int __init init_scsi(void)
	for_each_possible_cpu(i)
		INIT_LIST_HEAD(&per_cpu(scsi_done_q, i));

	scsi_netlink_init();

	printk(KERN_NOTICE "SCSI subsystem initialized\n");
	return 0;

@@ -1138,6 +1140,7 @@ cleanup_queue:

static void __exit exit_scsi(void)
{
	scsi_netlink_exit();
	scsi_sysfs_unregister();
	scsi_exit_sysctl();
	scsi_exit_hosts();
+199 −0
Original line number Diff line number Diff line
/*
 *  scsi_netlink.c  - SCSI Transport Netlink Interface
 *
 *  Copyright (C) 2006   James Smart, Emulex Corporation
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
#include <linux/time.h>
#include <linux/jiffies.h>
#include <linux/security.h>
#include <net/sock.h>
#include <net/netlink.h>

#include <scsi/scsi_netlink.h>
#include "scsi_priv.h"

struct sock *scsi_nl_sock = NULL;
EXPORT_SYMBOL_GPL(scsi_nl_sock);


/**
 * scsi_nl_rcv_msg -
 *    Receive message handler. Extracts message from a receive buffer.
 *    Validates message header and calls appropriate transport message handler
 *
 * @skb:		socket receive buffer
 *
 **/
static void
scsi_nl_rcv_msg(struct sk_buff *skb)
{
	struct nlmsghdr *nlh;
	struct scsi_nl_hdr *hdr;
	uint32_t rlen;
	int err;

	while (skb->len >= NLMSG_SPACE(0)) {
		err = 0;

		nlh = (struct nlmsghdr *) skb->data;
		if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
		    (skb->len < nlh->nlmsg_len)) {
			printk(KERN_WARNING "%s: discarding partial skb\n",
				 __FUNCTION__);
			return;
		}

		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
		if (rlen > skb->len)
			rlen = skb->len;

		if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
			err = -EBADMSG;
			goto next_msg;
		}

		hdr = NLMSG_DATA(nlh);
		if ((hdr->version != SCSI_NL_VERSION) ||
		    (hdr->magic != SCSI_NL_MAGIC)) {
			err = -EPROTOTYPE;
			goto next_msg;
		}

		if (security_netlink_recv(skb, CAP_SYS_ADMIN)) {
			err = -EPERM;
			goto next_msg;
		}

		if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
			printk(KERN_WARNING "%s: discarding partial message\n",
				 __FUNCTION__);
			return;
		}

		/*
		 * We currently don't support anyone sending us a message
		 */

next_msg:
		if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
			netlink_ack(skb, nlh, err);

		skb_pull(skb, rlen);
	}
}


/**
 * scsi_nl_rcv_msg -
 *    Receive handler for a socket. Extracts a received message buffer from
 *    the socket, and starts message processing.
 *
 * @sk:		socket
 * @len:	unused
 *
 **/
static void
scsi_nl_rcv(struct sock *sk, int len)
{
	struct sk_buff *skb;

	while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
		scsi_nl_rcv_msg(skb);
		kfree_skb(skb);
	}
}


/**
 * scsi_nl_rcv_event -
 *    Event handler for a netlink socket.
 *
 * @this:		event notifier block
 * @event:		event type
 * @ptr:		event payload
 *
 **/
static int
scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr)
{
	struct netlink_notify *n = ptr;

	if (n->protocol != NETLINK_SCSITRANSPORT)
		return NOTIFY_DONE;

	/*
	 * Currently, we are not tracking PID's, etc. There is nothing
	 * to handle.
	 */

	return NOTIFY_DONE;
}

static struct notifier_block scsi_netlink_notifier = {
	.notifier_call  = scsi_nl_rcv_event,
};


/**
 * scsi_netlink_init -
 *    Called by SCSI subsystem to intialize the SCSI transport netlink
 *    interface
 *
 **/
void
scsi_netlink_init(void)
{
	int error;

	error = netlink_register_notifier(&scsi_netlink_notifier);
	if (error) {
		printk(KERN_ERR "%s: register of event handler failed - %d\n",
				__FUNCTION__, error);
		return;
	}

	scsi_nl_sock = netlink_kernel_create(NETLINK_SCSITRANSPORT,
				SCSI_NL_GRP_CNT, scsi_nl_rcv, THIS_MODULE);
	if (!scsi_nl_sock) {
		printk(KERN_ERR "%s: register of recieve handler failed\n",
				__FUNCTION__);
		netlink_unregister_notifier(&scsi_netlink_notifier);
	}

	return;
}


/**
 * scsi_netlink_exit -
 *    Called by SCSI subsystem to disable the SCSI transport netlink
 *    interface
 *
 **/
void
scsi_netlink_exit(void)
{
	if (scsi_nl_sock) {
		sock_release(scsi_nl_sock->sk_socket);
		netlink_unregister_notifier(&scsi_netlink_notifier);
	}

	return;
}

+11 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ struct scsi_cmnd;
struct scsi_device;
struct scsi_host_template;
struct Scsi_Host;
struct scsi_nl_hdr;


/*
@@ -110,6 +111,16 @@ extern void __scsi_remove_device(struct scsi_device *);

extern struct bus_type scsi_bus_type;

/* scsi_netlink.c */
#ifdef CONFIG_SCSI_NETLINK
extern void scsi_netlink_init(void);
extern void scsi_netlink_exit(void);
extern struct sock *scsi_nl_sock;
#else
static inline void scsi_netlink_init(void) {}
static inline void scsi_netlink_exit(void) {}
#endif

/* 
 * internal scsi timeout functions: for use by mid-layer and transport
 * classes.
Loading