Skip to content

Commit

Permalink
[SCSI] SCSI and FC Transport: add netlink support for posting of tran…
Browse files Browse the repository at this point in the history
…sport 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: James Smart <James.Smart@emulex.com>

Tidy up configuration not to make SCSI always select NET

Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
  • Loading branch information
James Smart authored and James Bottomley committed Sep 2, 2006
1 parent deb81d8 commit 84314fd
Show file tree
Hide file tree
Showing 10 changed files with 612 additions and 1 deletion.
6 changes: 6 additions & 0 deletions drivers/scsi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions drivers/scsi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions drivers/scsi/scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -1138,6 +1140,7 @@ static int __init init_scsi(void)

static void __exit exit_scsi(void)
{
scsi_netlink_exit();
scsi_sysfs_unregister();
scsi_exit_sysctl();
scsi_exit_hosts();
Expand Down
199 changes: 199 additions & 0 deletions drivers/scsi/scsi_netlink.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* 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 changes: 11 additions & 0 deletions drivers/scsi/scsi_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ struct scsi_cmnd;
struct scsi_device;
struct scsi_host_template;
struct Scsi_Host;
struct scsi_nl_hdr;


/*
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 84314fd

Please sign in to comment.