scsi_netlink.c revision 25985edcedea6396277003854657b5f3cb31a628
184314fd4740ad73550c76dee4a9578979d84af48James Smart/*
284314fd4740ad73550c76dee4a9578979d84af48James Smart *  scsi_netlink.c  - SCSI Transport Netlink Interface
384314fd4740ad73550c76dee4a9578979d84af48James Smart *
484314fd4740ad73550c76dee4a9578979d84af48James Smart *  Copyright (C) 2006   James Smart, Emulex Corporation
584314fd4740ad73550c76dee4a9578979d84af48James Smart *
684314fd4740ad73550c76dee4a9578979d84af48James Smart *  This program is free software; you can redistribute it and/or modify
784314fd4740ad73550c76dee4a9578979d84af48James Smart *  it under the terms of the GNU General Public License as published by
884314fd4740ad73550c76dee4a9578979d84af48James Smart *  the Free Software Foundation; either version 2 of the License, or
984314fd4740ad73550c76dee4a9578979d84af48James Smart *  (at your option) any later version.
1084314fd4740ad73550c76dee4a9578979d84af48James Smart *
1184314fd4740ad73550c76dee4a9578979d84af48James Smart *  This program is distributed in the hope that it will be useful,
1284314fd4740ad73550c76dee4a9578979d84af48James Smart *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1384314fd4740ad73550c76dee4a9578979d84af48James Smart *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1484314fd4740ad73550c76dee4a9578979d84af48James Smart *  GNU General Public License for more details.
1584314fd4740ad73550c76dee4a9578979d84af48James Smart *
1684314fd4740ad73550c76dee4a9578979d84af48James Smart *  You should have received a copy of the GNU General Public License
1784314fd4740ad73550c76dee4a9578979d84af48James Smart *  along with this program; if not, write to the Free Software
1884314fd4740ad73550c76dee4a9578979d84af48James Smart *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1984314fd4740ad73550c76dee4a9578979d84af48James Smart *
2084314fd4740ad73550c76dee4a9578979d84af48James Smart */
2184314fd4740ad73550c76dee4a9578979d84af48James Smart#include <linux/time.h>
2284314fd4740ad73550c76dee4a9578979d84af48James Smart#include <linux/jiffies.h>
2384314fd4740ad73550c76dee4a9578979d84af48James Smart#include <linux/security.h>
2422447be7d15aefcfab84e9bec4859a28198b0c62James Smart#include <linux/delay.h>
255a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
2684314fd4740ad73550c76dee4a9578979d84af48James Smart#include <net/sock.h>
2784314fd4740ad73550c76dee4a9578979d84af48James Smart#include <net/netlink.h>
2884314fd4740ad73550c76dee4a9578979d84af48James Smart
2984314fd4740ad73550c76dee4a9578979d84af48James Smart#include <scsi/scsi_netlink.h>
3084314fd4740ad73550c76dee4a9578979d84af48James Smart#include "scsi_priv.h"
3184314fd4740ad73550c76dee4a9578979d84af48James Smart
3284314fd4740ad73550c76dee4a9578979d84af48James Smartstruct sock *scsi_nl_sock = NULL;
3384314fd4740ad73550c76dee4a9578979d84af48James SmartEXPORT_SYMBOL_GPL(scsi_nl_sock);
3484314fd4740ad73550c76dee4a9578979d84af48James Smart
3522447be7d15aefcfab84e9bec4859a28198b0c62James Smartstatic DEFINE_SPINLOCK(scsi_nl_lock);
3622447be7d15aefcfab84e9bec4859a28198b0c62James Smartstatic struct list_head scsi_nl_drivers;
3722447be7d15aefcfab84e9bec4859a28198b0c62James Smart
3822447be7d15aefcfab84e9bec4859a28198b0c62James Smartstatic u32	scsi_nl_state;
3922447be7d15aefcfab84e9bec4859a28198b0c62James Smart#define STATE_EHANDLER_BSY		0x00000001
4022447be7d15aefcfab84e9bec4859a28198b0c62James Smart
4122447be7d15aefcfab84e9bec4859a28198b0c62James Smartstruct scsi_nl_transport {
4222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int (*msg_handler)(struct sk_buff *);
4322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	void (*event_handler)(struct notifier_block *, unsigned long, void *);
4422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	unsigned int refcnt;
4522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int flags;
4622447be7d15aefcfab84e9bec4859a28198b0c62James Smart};
4722447be7d15aefcfab84e9bec4859a28198b0c62James Smart
4822447be7d15aefcfab84e9bec4859a28198b0c62James Smart/* flags values (bit flags) */
4922447be7d15aefcfab84e9bec4859a28198b0c62James Smart#define HANDLER_DELETING		0x1
5022447be7d15aefcfab84e9bec4859a28198b0c62James Smart
5122447be7d15aefcfab84e9bec4859a28198b0c62James Smartstatic struct scsi_nl_transport transports[SCSI_NL_MAX_TRANSPORTS] =
5222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	{ {NULL, }, };
5322447be7d15aefcfab84e9bec4859a28198b0c62James Smart
5422447be7d15aefcfab84e9bec4859a28198b0c62James Smart
5522447be7d15aefcfab84e9bec4859a28198b0c62James Smartstruct scsi_nl_drvr {
5622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct list_head next;
5722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int (*dmsg_handler)(struct Scsi_Host *shost, void *payload,
5822447be7d15aefcfab84e9bec4859a28198b0c62James Smart				 u32 len, u32 pid);
5922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	void (*devt_handler)(struct notifier_block *nb,
6022447be7d15aefcfab84e9bec4859a28198b0c62James Smart				 unsigned long event, void *notify_ptr);
6122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct scsi_host_template *hostt;
6222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	u64 vendor_id;
6322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	unsigned int refcnt;
6422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int flags;
6522447be7d15aefcfab84e9bec4859a28198b0c62James Smart};
6622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
6722447be7d15aefcfab84e9bec4859a28198b0c62James Smart
6884314fd4740ad73550c76dee4a9578979d84af48James Smart
6984314fd4740ad73550c76dee4a9578979d84af48James Smart/**
70eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley * scsi_nl_rcv_msg - Receive message handler.
71eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley * @skb:		socket receive buffer
72eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley *
73eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley * Description: Extracts message from a receive buffer.
7484314fd4740ad73550c76dee4a9578979d84af48James Smart *    Validates message header and calls appropriate transport message handler
7584314fd4740ad73550c76dee4a9578979d84af48James Smart *
7684314fd4740ad73550c76dee4a9578979d84af48James Smart *
7784314fd4740ad73550c76dee4a9578979d84af48James Smart **/
7884314fd4740ad73550c76dee4a9578979d84af48James Smartstatic void
7984314fd4740ad73550c76dee4a9578979d84af48James Smartscsi_nl_rcv_msg(struct sk_buff *skb)
8084314fd4740ad73550c76dee4a9578979d84af48James Smart{
8184314fd4740ad73550c76dee4a9578979d84af48James Smart	struct nlmsghdr *nlh;
8284314fd4740ad73550c76dee4a9578979d84af48James Smart	struct scsi_nl_hdr *hdr;
8322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	unsigned long flags;
8422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	u32 rlen;
8522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int err, tport;
8684314fd4740ad73550c76dee4a9578979d84af48James Smart
8784314fd4740ad73550c76dee4a9578979d84af48James Smart	while (skb->len >= NLMSG_SPACE(0)) {
8884314fd4740ad73550c76dee4a9578979d84af48James Smart		err = 0;
8984314fd4740ad73550c76dee4a9578979d84af48James Smart
90b529ccf2799c14346d1518e9bdf1f88f03643e99Arnaldo Carvalho de Melo		nlh = nlmsg_hdr(skb);
9184314fd4740ad73550c76dee4a9578979d84af48James Smart		if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
9284314fd4740ad73550c76dee4a9578979d84af48James Smart		    (skb->len < nlh->nlmsg_len)) {
9384314fd4740ad73550c76dee4a9578979d84af48James Smart			printk(KERN_WARNING "%s: discarding partial skb\n",
94cadbd4a5e36dde7e6c49b587b2c419103c0b7218Harvey Harrison				 __func__);
9584314fd4740ad73550c76dee4a9578979d84af48James Smart			return;
9684314fd4740ad73550c76dee4a9578979d84af48James Smart		}
9784314fd4740ad73550c76dee4a9578979d84af48James Smart
9884314fd4740ad73550c76dee4a9578979d84af48James Smart		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
9984314fd4740ad73550c76dee4a9578979d84af48James Smart		if (rlen > skb->len)
10084314fd4740ad73550c76dee4a9578979d84af48James Smart			rlen = skb->len;
10184314fd4740ad73550c76dee4a9578979d84af48James Smart
10284314fd4740ad73550c76dee4a9578979d84af48James Smart		if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
10384314fd4740ad73550c76dee4a9578979d84af48James Smart			err = -EBADMSG;
10422447be7d15aefcfab84e9bec4859a28198b0c62James Smart			goto next_msg;
10584314fd4740ad73550c76dee4a9578979d84af48James Smart		}
10684314fd4740ad73550c76dee4a9578979d84af48James Smart
10784314fd4740ad73550c76dee4a9578979d84af48James Smart		hdr = NLMSG_DATA(nlh);
10884314fd4740ad73550c76dee4a9578979d84af48James Smart		if ((hdr->version != SCSI_NL_VERSION) ||
10984314fd4740ad73550c76dee4a9578979d84af48James Smart		    (hdr->magic != SCSI_NL_MAGIC)) {
11084314fd4740ad73550c76dee4a9578979d84af48James Smart			err = -EPROTOTYPE;
11184314fd4740ad73550c76dee4a9578979d84af48James Smart			goto next_msg;
11284314fd4740ad73550c76dee4a9578979d84af48James Smart		}
11384314fd4740ad73550c76dee4a9578979d84af48James Smart
11484314fd4740ad73550c76dee4a9578979d84af48James Smart		if (security_netlink_recv(skb, CAP_SYS_ADMIN)) {
11584314fd4740ad73550c76dee4a9578979d84af48James Smart			err = -EPERM;
11684314fd4740ad73550c76dee4a9578979d84af48James Smart			goto next_msg;
11784314fd4740ad73550c76dee4a9578979d84af48James Smart		}
11884314fd4740ad73550c76dee4a9578979d84af48James Smart
11984314fd4740ad73550c76dee4a9578979d84af48James Smart		if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
12084314fd4740ad73550c76dee4a9578979d84af48James Smart			printk(KERN_WARNING "%s: discarding partial message\n",
121cadbd4a5e36dde7e6c49b587b2c419103c0b7218Harvey Harrison				 __func__);
12222447be7d15aefcfab84e9bec4859a28198b0c62James Smart			goto next_msg;
12384314fd4740ad73550c76dee4a9578979d84af48James Smart		}
12484314fd4740ad73550c76dee4a9578979d84af48James Smart
12584314fd4740ad73550c76dee4a9578979d84af48James Smart		/*
12622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		 * Deliver message to the appropriate transport
12784314fd4740ad73550c76dee4a9578979d84af48James Smart		 */
12822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_lock_irqsave(&scsi_nl_lock, flags);
12922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
13022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		tport = hdr->transport;
13122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		if ((tport < SCSI_NL_MAX_TRANSPORTS) &&
13222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		    !(transports[tport].flags & HANDLER_DELETING) &&
13322447be7d15aefcfab84e9bec4859a28198b0c62James Smart		    (transports[tport].msg_handler)) {
13422447be7d15aefcfab84e9bec4859a28198b0c62James Smart			transports[tport].refcnt++;
13522447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_unlock_irqrestore(&scsi_nl_lock, flags);
13622447be7d15aefcfab84e9bec4859a28198b0c62James Smart			err = transports[tport].msg_handler(skb);
13722447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_lock_irqsave(&scsi_nl_lock, flags);
13822447be7d15aefcfab84e9bec4859a28198b0c62James Smart			transports[tport].refcnt--;
13922447be7d15aefcfab84e9bec4859a28198b0c62James Smart		} else
14022447be7d15aefcfab84e9bec4859a28198b0c62James Smart			err = -ENOENT;
14122447be7d15aefcfab84e9bec4859a28198b0c62James Smart
14222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_unlock_irqrestore(&scsi_nl_lock, flags);
14384314fd4740ad73550c76dee4a9578979d84af48James Smart
14484314fd4740ad73550c76dee4a9578979d84af48James Smartnext_msg:
14584314fd4740ad73550c76dee4a9578979d84af48James Smart		if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
14684314fd4740ad73550c76dee4a9578979d84af48James Smart			netlink_ack(skb, nlh, err);
14784314fd4740ad73550c76dee4a9578979d84af48James Smart
14884314fd4740ad73550c76dee4a9578979d84af48James Smart		skb_pull(skb, rlen);
14984314fd4740ad73550c76dee4a9578979d84af48James Smart	}
15084314fd4740ad73550c76dee4a9578979d84af48James Smart}
15184314fd4740ad73550c76dee4a9578979d84af48James Smart
15284314fd4740ad73550c76dee4a9578979d84af48James Smart
15384314fd4740ad73550c76dee4a9578979d84af48James Smart/**
154eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley * scsi_nl_rcv_event - Event handler for a netlink socket.
15584314fd4740ad73550c76dee4a9578979d84af48James Smart * @this:		event notifier block
15684314fd4740ad73550c76dee4a9578979d84af48James Smart * @event:		event type
15784314fd4740ad73550c76dee4a9578979d84af48James Smart * @ptr:		event payload
15884314fd4740ad73550c76dee4a9578979d84af48James Smart *
15984314fd4740ad73550c76dee4a9578979d84af48James Smart **/
16084314fd4740ad73550c76dee4a9578979d84af48James Smartstatic int
16184314fd4740ad73550c76dee4a9578979d84af48James Smartscsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr)
16284314fd4740ad73550c76dee4a9578979d84af48James Smart{
16384314fd4740ad73550c76dee4a9578979d84af48James Smart	struct netlink_notify *n = ptr;
16422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct scsi_nl_drvr *driver;
16522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	unsigned long flags;
16622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int tport;
16784314fd4740ad73550c76dee4a9578979d84af48James Smart
16884314fd4740ad73550c76dee4a9578979d84af48James Smart	if (n->protocol != NETLINK_SCSITRANSPORT)
16984314fd4740ad73550c76dee4a9578979d84af48James Smart		return NOTIFY_DONE;
17084314fd4740ad73550c76dee4a9578979d84af48James Smart
17122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_lock_irqsave(&scsi_nl_lock, flags);
17222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	scsi_nl_state |= STATE_EHANDLER_BSY;
17322447be7d15aefcfab84e9bec4859a28198b0c62James Smart
17484314fd4740ad73550c76dee4a9578979d84af48James Smart	/*
17522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	 * Pass event on to any transports that may be listening
17684314fd4740ad73550c76dee4a9578979d84af48James Smart	 */
17722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	for (tport = 0; tport < SCSI_NL_MAX_TRANSPORTS; tport++) {
17822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		if (!(transports[tport].flags & HANDLER_DELETING) &&
17922447be7d15aefcfab84e9bec4859a28198b0c62James Smart		    (transports[tport].event_handler)) {
18022447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_unlock_irqrestore(&scsi_nl_lock, flags);
18122447be7d15aefcfab84e9bec4859a28198b0c62James Smart			transports[tport].event_handler(this, event, ptr);
18222447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_lock_irqsave(&scsi_nl_lock, flags);
18322447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
18422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
18522447be7d15aefcfab84e9bec4859a28198b0c62James Smart
18622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	/*
18722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	 * Pass event on to any drivers that may be listening
18822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	 */
18922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	list_for_each_entry(driver, &scsi_nl_drivers, next) {
19022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		if (!(driver->flags & HANDLER_DELETING) &&
19122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		    (driver->devt_handler)) {
19222447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_unlock_irqrestore(&scsi_nl_lock, flags);
19322447be7d15aefcfab84e9bec4859a28198b0c62James Smart			driver->devt_handler(this, event, ptr);
19422447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_lock_irqsave(&scsi_nl_lock, flags);
19522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
19622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
19722447be7d15aefcfab84e9bec4859a28198b0c62James Smart
19822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	scsi_nl_state &= ~STATE_EHANDLER_BSY;
19922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_unlock_irqrestore(&scsi_nl_lock, flags);
20084314fd4740ad73550c76dee4a9578979d84af48James Smart
20184314fd4740ad73550c76dee4a9578979d84af48James Smart	return NOTIFY_DONE;
20284314fd4740ad73550c76dee4a9578979d84af48James Smart}
20384314fd4740ad73550c76dee4a9578979d84af48James Smart
20484314fd4740ad73550c76dee4a9578979d84af48James Smartstatic struct notifier_block scsi_netlink_notifier = {
20584314fd4740ad73550c76dee4a9578979d84af48James Smart	.notifier_call  = scsi_nl_rcv_event,
20684314fd4740ad73550c76dee4a9578979d84af48James Smart};
20784314fd4740ad73550c76dee4a9578979d84af48James Smart
20884314fd4740ad73550c76dee4a9578979d84af48James Smart
209aa198bf1dadefaefeb2c4a32444dfc6c7f93938dRandy Dunlap/*
21022447be7d15aefcfab84e9bec4859a28198b0c62James Smart * GENERIC SCSI transport receive and event handlers
211aa198bf1dadefaefeb2c4a32444dfc6c7f93938dRandy Dunlap */
21222447be7d15aefcfab84e9bec4859a28198b0c62James Smart
21322447be7d15aefcfab84e9bec4859a28198b0c62James Smart/**
214aa198bf1dadefaefeb2c4a32444dfc6c7f93938dRandy Dunlap * scsi_generic_msg_handler - receive message handler for GENERIC transport messages
21522447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @skb:		socket receive buffer
21622447be7d15aefcfab84e9bec4859a28198b0c62James Smart **/
21722447be7d15aefcfab84e9bec4859a28198b0c62James Smartstatic int
21822447be7d15aefcfab84e9bec4859a28198b0c62James Smartscsi_generic_msg_handler(struct sk_buff *skb)
21922447be7d15aefcfab84e9bec4859a28198b0c62James Smart{
22022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct nlmsghdr *nlh = nlmsg_hdr(skb);
22122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct scsi_nl_hdr *snlh = NLMSG_DATA(nlh);
22222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct scsi_nl_drvr *driver;
22322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct Scsi_Host *shost;
22422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	unsigned long flags;
22522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int err = 0, match, pid;
22622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
22722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	pid = NETLINK_CREDS(skb)->pid;
22822447be7d15aefcfab84e9bec4859a28198b0c62James Smart
22922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	switch (snlh->msgtype) {
23022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	case SCSI_NL_SHOST_VENDOR:
23122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		{
23222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		struct scsi_nl_host_vendor_msg *msg = NLMSG_DATA(nlh);
23322447be7d15aefcfab84e9bec4859a28198b0c62James Smart
23422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		/* Locate the driver that corresponds to the message */
23522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_lock_irqsave(&scsi_nl_lock, flags);
23622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		match = 0;
23722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		list_for_each_entry(driver, &scsi_nl_drivers, next) {
23822447be7d15aefcfab84e9bec4859a28198b0c62James Smart			if (driver->vendor_id == msg->vendor_id) {
23922447be7d15aefcfab84e9bec4859a28198b0c62James Smart				match = 1;
24022447be7d15aefcfab84e9bec4859a28198b0c62James Smart				break;
24122447be7d15aefcfab84e9bec4859a28198b0c62James Smart			}
24222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
24322447be7d15aefcfab84e9bec4859a28198b0c62James Smart
24422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		if ((!match) || (!driver->dmsg_handler)) {
24522447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_unlock_irqrestore(&scsi_nl_lock, flags);
24622447be7d15aefcfab84e9bec4859a28198b0c62James Smart			err = -ESRCH;
24722447be7d15aefcfab84e9bec4859a28198b0c62James Smart			goto rcv_exit;
24822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
24922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
25022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		if (driver->flags & HANDLER_DELETING) {
25122447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_unlock_irqrestore(&scsi_nl_lock, flags);
25222447be7d15aefcfab84e9bec4859a28198b0c62James Smart			err = -ESHUTDOWN;
25322447be7d15aefcfab84e9bec4859a28198b0c62James Smart			goto rcv_exit;
25422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
25522447be7d15aefcfab84e9bec4859a28198b0c62James Smart
25622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		driver->refcnt++;
25722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_unlock_irqrestore(&scsi_nl_lock, flags);
25822447be7d15aefcfab84e9bec4859a28198b0c62James Smart
25922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
26022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		/* if successful, scsi_host_lookup takes a shost reference */
26122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		shost = scsi_host_lookup(msg->host_no);
26222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		if (!shost) {
26322447be7d15aefcfab84e9bec4859a28198b0c62James Smart			err = -ENODEV;
26422447be7d15aefcfab84e9bec4859a28198b0c62James Smart			goto driver_exit;
26522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
26622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
26722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		/* is this host owned by the vendor ? */
26822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		if (shost->hostt != driver->hostt) {
26922447be7d15aefcfab84e9bec4859a28198b0c62James Smart			err = -EINVAL;
27022447be7d15aefcfab84e9bec4859a28198b0c62James Smart			goto vendormsg_put;
27122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
27222447be7d15aefcfab84e9bec4859a28198b0c62James Smart
27322447be7d15aefcfab84e9bec4859a28198b0c62James Smart		/* pass message on to the driver */
27422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		err = driver->dmsg_handler(shost, (void *)&msg[1],
27522447be7d15aefcfab84e9bec4859a28198b0c62James Smart					 msg->vmsg_datalen, pid);
27622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
27722447be7d15aefcfab84e9bec4859a28198b0c62James Smartvendormsg_put:
27822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		/* release reference by scsi_host_lookup */
27922447be7d15aefcfab84e9bec4859a28198b0c62James Smart		scsi_host_put(shost);
28022447be7d15aefcfab84e9bec4859a28198b0c62James Smart
28122447be7d15aefcfab84e9bec4859a28198b0c62James Smartdriver_exit:
28222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		/* release our own reference on the registration object */
28322447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_lock_irqsave(&scsi_nl_lock, flags);
28422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		driver->refcnt--;
28522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_unlock_irqrestore(&scsi_nl_lock, flags);
28622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		break;
28722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
28822447be7d15aefcfab84e9bec4859a28198b0c62James Smart
28922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	default:
29022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		err = -EBADR;
29122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		break;
29222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
29322447be7d15aefcfab84e9bec4859a28198b0c62James Smart
29422447be7d15aefcfab84e9bec4859a28198b0c62James Smartrcv_exit:
29522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (err)
29622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		printk(KERN_WARNING "%s: Msgtype %d failed - err %d\n",
29722447be7d15aefcfab84e9bec4859a28198b0c62James Smart			 __func__, snlh->msgtype, err);
29822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	return err;
29922447be7d15aefcfab84e9bec4859a28198b0c62James Smart}
30022447be7d15aefcfab84e9bec4859a28198b0c62James Smart
30122447be7d15aefcfab84e9bec4859a28198b0c62James Smart
30222447be7d15aefcfab84e9bec4859a28198b0c62James Smart/**
30322447be7d15aefcfab84e9bec4859a28198b0c62James Smart * scsi_nl_add_transport -
30422447be7d15aefcfab84e9bec4859a28198b0c62James Smart *    Registers message and event handlers for a transport. Enables
30522447be7d15aefcfab84e9bec4859a28198b0c62James Smart *    receipt of netlink messages and events to a transport.
30622447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
30722447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @tport:		transport registering handlers
30822447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @msg_handler:	receive message handler callback
30922447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @event_handler:	receive event handler callback
31022447be7d15aefcfab84e9bec4859a28198b0c62James Smart **/
31122447be7d15aefcfab84e9bec4859a28198b0c62James Smartint
31222447be7d15aefcfab84e9bec4859a28198b0c62James Smartscsi_nl_add_transport(u8 tport,
31322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int (*msg_handler)(struct sk_buff *),
31422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	void (*event_handler)(struct notifier_block *, unsigned long, void *))
31522447be7d15aefcfab84e9bec4859a28198b0c62James Smart{
31622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	unsigned long flags;
31722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int err = 0;
31822447be7d15aefcfab84e9bec4859a28198b0c62James Smart
31922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (tport >= SCSI_NL_MAX_TRANSPORTS)
32022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		return -EINVAL;
32122447be7d15aefcfab84e9bec4859a28198b0c62James Smart
32222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_lock_irqsave(&scsi_nl_lock, flags);
32322447be7d15aefcfab84e9bec4859a28198b0c62James Smart
32422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (scsi_nl_state & STATE_EHANDLER_BSY) {
32522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_unlock_irqrestore(&scsi_nl_lock, flags);
32622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		msleep(1);
32722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_lock_irqsave(&scsi_nl_lock, flags);
32822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
32922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
33022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (transports[tport].msg_handler || transports[tport].event_handler) {
33122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		err = -EALREADY;
33222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		goto register_out;
33322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
33422447be7d15aefcfab84e9bec4859a28198b0c62James Smart
33522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	transports[tport].msg_handler = msg_handler;
33622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	transports[tport].event_handler = event_handler;
33722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	transports[tport].flags = 0;
33822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	transports[tport].refcnt = 0;
33922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
34022447be7d15aefcfab84e9bec4859a28198b0c62James Smartregister_out:
34122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_unlock_irqrestore(&scsi_nl_lock, flags);
34222447be7d15aefcfab84e9bec4859a28198b0c62James Smart
34322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	return err;
34422447be7d15aefcfab84e9bec4859a28198b0c62James Smart}
34522447be7d15aefcfab84e9bec4859a28198b0c62James SmartEXPORT_SYMBOL_GPL(scsi_nl_add_transport);
34622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
34722447be7d15aefcfab84e9bec4859a28198b0c62James Smart
34822447be7d15aefcfab84e9bec4859a28198b0c62James Smart/**
34922447be7d15aefcfab84e9bec4859a28198b0c62James Smart * scsi_nl_remove_transport -
35022447be7d15aefcfab84e9bec4859a28198b0c62James Smart *    Disable transport receiption of messages and events
35122447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
35222447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @tport:		transport deregistering handlers
35322447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
35422447be7d15aefcfab84e9bec4859a28198b0c62James Smart **/
35522447be7d15aefcfab84e9bec4859a28198b0c62James Smartvoid
35622447be7d15aefcfab84e9bec4859a28198b0c62James Smartscsi_nl_remove_transport(u8 tport)
35722447be7d15aefcfab84e9bec4859a28198b0c62James Smart{
35822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	unsigned long flags;
35922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
36022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_lock_irqsave(&scsi_nl_lock, flags);
36122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (scsi_nl_state & STATE_EHANDLER_BSY) {
36222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_unlock_irqrestore(&scsi_nl_lock, flags);
36322447be7d15aefcfab84e9bec4859a28198b0c62James Smart		msleep(1);
36422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_lock_irqsave(&scsi_nl_lock, flags);
36522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
36622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
36722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (tport < SCSI_NL_MAX_TRANSPORTS) {
36822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		transports[tport].flags |= HANDLER_DELETING;
36922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
37022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		while (transports[tport].refcnt != 0) {
37122447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_unlock_irqrestore(&scsi_nl_lock, flags);
37222447be7d15aefcfab84e9bec4859a28198b0c62James Smart			schedule_timeout_uninterruptible(HZ/4);
37322447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_lock_irqsave(&scsi_nl_lock, flags);
37422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
37522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		transports[tport].msg_handler = NULL;
37622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		transports[tport].event_handler = NULL;
37722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		transports[tport].flags = 0;
37822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
37922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
38022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_unlock_irqrestore(&scsi_nl_lock, flags);
38122447be7d15aefcfab84e9bec4859a28198b0c62James Smart
38222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	return;
38322447be7d15aefcfab84e9bec4859a28198b0c62James Smart}
38422447be7d15aefcfab84e9bec4859a28198b0c62James SmartEXPORT_SYMBOL_GPL(scsi_nl_remove_transport);
38522447be7d15aefcfab84e9bec4859a28198b0c62James Smart
38622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
38722447be7d15aefcfab84e9bec4859a28198b0c62James Smart/**
38822447be7d15aefcfab84e9bec4859a28198b0c62James Smart * scsi_nl_add_driver -
38922447be7d15aefcfab84e9bec4859a28198b0c62James Smart *    A driver is registering its interfaces for SCSI netlink messages
39022447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
39122447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @vendor_id:          A unique identification value for the driver.
39222447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @hostt:		address of the driver's host template. Used
39322447be7d15aefcfab84e9bec4859a28198b0c62James Smart *			to verify an shost is bound to the driver
39422447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @nlmsg_handler:	receive message handler callback
39522447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @nlevt_handler:	receive event handler callback
39622447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
39722447be7d15aefcfab84e9bec4859a28198b0c62James Smart * Returns:
39822447be7d15aefcfab84e9bec4859a28198b0c62James Smart *   0 on Success
39922447be7d15aefcfab84e9bec4859a28198b0c62James Smart *   error result otherwise
40022447be7d15aefcfab84e9bec4859a28198b0c62James Smart **/
40122447be7d15aefcfab84e9bec4859a28198b0c62James Smartint
40222447be7d15aefcfab84e9bec4859a28198b0c62James Smartscsi_nl_add_driver(u64 vendor_id, struct scsi_host_template *hostt,
40322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int (*nlmsg_handler)(struct Scsi_Host *shost, void *payload,
40422447be7d15aefcfab84e9bec4859a28198b0c62James Smart				 u32 len, u32 pid),
40522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	void (*nlevt_handler)(struct notifier_block *nb,
40622447be7d15aefcfab84e9bec4859a28198b0c62James Smart				 unsigned long event, void *notify_ptr))
40722447be7d15aefcfab84e9bec4859a28198b0c62James Smart{
40822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct scsi_nl_drvr *driver;
40922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	unsigned long flags;
41022447be7d15aefcfab84e9bec4859a28198b0c62James Smart
41122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	driver = kzalloc(sizeof(*driver), GFP_KERNEL);
41222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (unlikely(!driver)) {
41322447be7d15aefcfab84e9bec4859a28198b0c62James Smart		printk(KERN_ERR "%s: allocation failure\n", __func__);
41422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		return -ENOMEM;
41522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
41622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
41722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	driver->dmsg_handler = nlmsg_handler;
41822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	driver->devt_handler = nlevt_handler;
41922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	driver->hostt = hostt;
42022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	driver->vendor_id = vendor_id;
42122447be7d15aefcfab84e9bec4859a28198b0c62James Smart
42222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_lock_irqsave(&scsi_nl_lock, flags);
42322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (scsi_nl_state & STATE_EHANDLER_BSY) {
42422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_unlock_irqrestore(&scsi_nl_lock, flags);
42522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		msleep(1);
42622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_lock_irqsave(&scsi_nl_lock, flags);
42722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
42822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	list_add_tail(&driver->next, &scsi_nl_drivers);
42922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_unlock_irqrestore(&scsi_nl_lock, flags);
43022447be7d15aefcfab84e9bec4859a28198b0c62James Smart
43122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	return 0;
43222447be7d15aefcfab84e9bec4859a28198b0c62James Smart}
43322447be7d15aefcfab84e9bec4859a28198b0c62James SmartEXPORT_SYMBOL_GPL(scsi_nl_add_driver);
43422447be7d15aefcfab84e9bec4859a28198b0c62James Smart
43522447be7d15aefcfab84e9bec4859a28198b0c62James Smart
43622447be7d15aefcfab84e9bec4859a28198b0c62James Smart/**
43722447be7d15aefcfab84e9bec4859a28198b0c62James Smart * scsi_nl_remove_driver -
43822447be7d15aefcfab84e9bec4859a28198b0c62James Smart *    An driver is unregistering with the SCSI netlink messages
43922447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
44022447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @vendor_id:          The unique identification value for the driver.
44122447be7d15aefcfab84e9bec4859a28198b0c62James Smart **/
44222447be7d15aefcfab84e9bec4859a28198b0c62James Smartvoid
44322447be7d15aefcfab84e9bec4859a28198b0c62James Smartscsi_nl_remove_driver(u64 vendor_id)
44422447be7d15aefcfab84e9bec4859a28198b0c62James Smart{
44522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct scsi_nl_drvr *driver;
44622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	unsigned long flags;
44722447be7d15aefcfab84e9bec4859a28198b0c62James Smart
44822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_lock_irqsave(&scsi_nl_lock, flags);
44922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (scsi_nl_state & STATE_EHANDLER_BSY) {
45022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_unlock_irqrestore(&scsi_nl_lock, flags);
45122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		msleep(1);
45222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		spin_lock_irqsave(&scsi_nl_lock, flags);
45322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
45422447be7d15aefcfab84e9bec4859a28198b0c62James Smart
45522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	list_for_each_entry(driver, &scsi_nl_drivers, next) {
45622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		if (driver->vendor_id == vendor_id) {
45722447be7d15aefcfab84e9bec4859a28198b0c62James Smart			driver->flags |= HANDLER_DELETING;
45822447be7d15aefcfab84e9bec4859a28198b0c62James Smart			while (driver->refcnt != 0) {
45922447be7d15aefcfab84e9bec4859a28198b0c62James Smart				spin_unlock_irqrestore(&scsi_nl_lock, flags);
46022447be7d15aefcfab84e9bec4859a28198b0c62James Smart				schedule_timeout_uninterruptible(HZ/4);
46122447be7d15aefcfab84e9bec4859a28198b0c62James Smart				spin_lock_irqsave(&scsi_nl_lock, flags);
46222447be7d15aefcfab84e9bec4859a28198b0c62James Smart			}
46322447be7d15aefcfab84e9bec4859a28198b0c62James Smart			list_del(&driver->next);
46422447be7d15aefcfab84e9bec4859a28198b0c62James Smart			kfree(driver);
46522447be7d15aefcfab84e9bec4859a28198b0c62James Smart			spin_unlock_irqrestore(&scsi_nl_lock, flags);
46622447be7d15aefcfab84e9bec4859a28198b0c62James Smart			return;
46722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		}
46822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
46922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
47022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	spin_unlock_irqrestore(&scsi_nl_lock, flags);
47122447be7d15aefcfab84e9bec4859a28198b0c62James Smart
47222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	printk(KERN_ERR "%s: removal of driver failed - vendor_id 0x%llx\n",
47322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	       __func__, (unsigned long long)vendor_id);
47422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	return;
47522447be7d15aefcfab84e9bec4859a28198b0c62James Smart}
47622447be7d15aefcfab84e9bec4859a28198b0c62James SmartEXPORT_SYMBOL_GPL(scsi_nl_remove_driver);
47722447be7d15aefcfab84e9bec4859a28198b0c62James Smart
47822447be7d15aefcfab84e9bec4859a28198b0c62James Smart
47922447be7d15aefcfab84e9bec4859a28198b0c62James Smart/**
480b595076a180a56d1bb170e6eceda6eb9d76f4cd3Uwe Kleine-König * scsi_netlink_init - Called by SCSI subsystem to initialize
48122447be7d15aefcfab84e9bec4859a28198b0c62James Smart * 	the SCSI transport netlink interface
48284314fd4740ad73550c76dee4a9578979d84af48James Smart *
48384314fd4740ad73550c76dee4a9578979d84af48James Smart **/
48484314fd4740ad73550c76dee4a9578979d84af48James Smartvoid
48584314fd4740ad73550c76dee4a9578979d84af48James Smartscsi_netlink_init(void)
48684314fd4740ad73550c76dee4a9578979d84af48James Smart{
48784314fd4740ad73550c76dee4a9578979d84af48James Smart	int error;
48884314fd4740ad73550c76dee4a9578979d84af48James Smart
48922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	INIT_LIST_HEAD(&scsi_nl_drivers);
49022447be7d15aefcfab84e9bec4859a28198b0c62James Smart
49184314fd4740ad73550c76dee4a9578979d84af48James Smart	error = netlink_register_notifier(&scsi_netlink_notifier);
49284314fd4740ad73550c76dee4a9578979d84af48James Smart	if (error) {
49384314fd4740ad73550c76dee4a9578979d84af48James Smart		printk(KERN_ERR "%s: register of event handler failed - %d\n",
494cadbd4a5e36dde7e6c49b587b2c419103c0b7218Harvey Harrison				__func__, error);
49584314fd4740ad73550c76dee4a9578979d84af48James Smart		return;
49684314fd4740ad73550c76dee4a9578979d84af48James Smart	}
49784314fd4740ad73550c76dee4a9578979d84af48James Smart
498b4b510290b056b86611757ce1175a230f1080f53Eric W. Biederman	scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
499cd40b7d3983c708aabe3d3008ec64ffce56d33b0Denis V. Lunev				SCSI_NL_GRP_CNT, scsi_nl_rcv_msg, NULL,
500af65bdfce98d7965fbe93a48b8128444a2eea024Patrick McHardy				THIS_MODULE);
50184314fd4740ad73550c76dee4a9578979d84af48James Smart	if (!scsi_nl_sock) {
50225985edcedea6396277003854657b5f3cb31a628Lucas De Marchi		printk(KERN_ERR "%s: register of receive handler failed\n",
503cadbd4a5e36dde7e6c49b587b2c419103c0b7218Harvey Harrison				__func__);
50484314fd4740ad73550c76dee4a9578979d84af48James Smart		netlink_unregister_notifier(&scsi_netlink_notifier);
50522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		return;
50684314fd4740ad73550c76dee4a9578979d84af48James Smart	}
50784314fd4740ad73550c76dee4a9578979d84af48James Smart
50822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	/* Register the entry points for the generic SCSI transport */
50922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	error = scsi_nl_add_transport(SCSI_NL_TRANSPORT,
51022447be7d15aefcfab84e9bec4859a28198b0c62James Smart				scsi_generic_msg_handler, NULL);
51122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (error)
51222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		printk(KERN_ERR "%s: register of GENERIC transport handler"
51322447be7d15aefcfab84e9bec4859a28198b0c62James Smart				"  failed - %d\n", __func__, error);
51484314fd4740ad73550c76dee4a9578979d84af48James Smart	return;
51584314fd4740ad73550c76dee4a9578979d84af48James Smart}
51684314fd4740ad73550c76dee4a9578979d84af48James Smart
51784314fd4740ad73550c76dee4a9578979d84af48James Smart
51884314fd4740ad73550c76dee4a9578979d84af48James Smart/**
519eb44820c28bc9a042e1157b41c677018a8fdfc74Rob Landley * scsi_netlink_exit - Called by SCSI subsystem to disable the SCSI transport netlink interface
52084314fd4740ad73550c76dee4a9578979d84af48James Smart *
52184314fd4740ad73550c76dee4a9578979d84af48James Smart **/
52284314fd4740ad73550c76dee4a9578979d84af48James Smartvoid
52384314fd4740ad73550c76dee4a9578979d84af48James Smartscsi_netlink_exit(void)
52484314fd4740ad73550c76dee4a9578979d84af48James Smart{
52522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	scsi_nl_remove_transport(SCSI_NL_TRANSPORT);
52622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
52784314fd4740ad73550c76dee4a9578979d84af48James Smart	if (scsi_nl_sock) {
528b7c6ba6eb1234e35a74fb8ba8123232a7b1ba9e4Denis V. Lunev		netlink_kernel_release(scsi_nl_sock);
52984314fd4740ad73550c76dee4a9578979d84af48James Smart		netlink_unregister_notifier(&scsi_netlink_notifier);
53084314fd4740ad73550c76dee4a9578979d84af48James Smart	}
53184314fd4740ad73550c76dee4a9578979d84af48James Smart
53284314fd4740ad73550c76dee4a9578979d84af48James Smart	return;
53384314fd4740ad73550c76dee4a9578979d84af48James Smart}
53484314fd4740ad73550c76dee4a9578979d84af48James Smart
53584314fd4740ad73550c76dee4a9578979d84af48James Smart
53622447be7d15aefcfab84e9bec4859a28198b0c62James Smart/*
53722447be7d15aefcfab84e9bec4859a28198b0c62James Smart * Exported Interfaces
53822447be7d15aefcfab84e9bec4859a28198b0c62James Smart */
53922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
54022447be7d15aefcfab84e9bec4859a28198b0c62James Smart/**
54122447be7d15aefcfab84e9bec4859a28198b0c62James Smart * scsi_nl_send_transport_msg -
54222447be7d15aefcfab84e9bec4859a28198b0c62James Smart *    Generic function to send a single message from a SCSI transport to
54322447be7d15aefcfab84e9bec4859a28198b0c62James Smart *    a single process
54422447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
54522447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @pid:		receiving pid
54622447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @hdr:		message payload
54722447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
54822447be7d15aefcfab84e9bec4859a28198b0c62James Smart **/
54922447be7d15aefcfab84e9bec4859a28198b0c62James Smartvoid
55022447be7d15aefcfab84e9bec4859a28198b0c62James Smartscsi_nl_send_transport_msg(u32 pid, struct scsi_nl_hdr *hdr)
55122447be7d15aefcfab84e9bec4859a28198b0c62James Smart{
55222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct sk_buff *skb;
55322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct nlmsghdr	*nlh;
55422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	const char *fn;
55522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	char *datab;
55622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	u32 len, skblen;
55722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int err;
55822447be7d15aefcfab84e9bec4859a28198b0c62James Smart
55922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (!scsi_nl_sock) {
56022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		err = -ENOENT;
56122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		fn = "netlink socket";
56222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		goto msg_fail;
56322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
56422447be7d15aefcfab84e9bec4859a28198b0c62James Smart
56522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	len = NLMSG_SPACE(hdr->msglen);
56622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	skblen = NLMSG_SPACE(len);
56722447be7d15aefcfab84e9bec4859a28198b0c62James Smart
56822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	skb = alloc_skb(skblen, GFP_KERNEL);
56922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (!skb) {
57022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		err = -ENOBUFS;
57122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		fn = "alloc_skb";
57222447be7d15aefcfab84e9bec4859a28198b0c62James Smart		goto msg_fail;
57322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
57422447be7d15aefcfab84e9bec4859a28198b0c62James Smart
57522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	nlh = nlmsg_put(skb, pid, 0, SCSI_TRANSPORT_MSG, len - sizeof(*nlh), 0);
57622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (!nlh) {
57722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		err = -ENOBUFS;
57822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		fn = "nlmsg_put";
57922447be7d15aefcfab84e9bec4859a28198b0c62James Smart		goto msg_fail_skb;
58022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
58122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	datab = NLMSG_DATA(nlh);
58222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	memcpy(datab, hdr, hdr->msglen);
58322447be7d15aefcfab84e9bec4859a28198b0c62James Smart
58422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	err = nlmsg_unicast(scsi_nl_sock, skb, pid);
58522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (err < 0) {
58622447be7d15aefcfab84e9bec4859a28198b0c62James Smart		fn = "nlmsg_unicast";
58722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		/* nlmsg_unicast already kfree_skb'd */
58822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		goto msg_fail;
58922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
59022447be7d15aefcfab84e9bec4859a28198b0c62James Smart
59122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	return;
59222447be7d15aefcfab84e9bec4859a28198b0c62James Smart
59322447be7d15aefcfab84e9bec4859a28198b0c62James Smartmsg_fail_skb:
59422447be7d15aefcfab84e9bec4859a28198b0c62James Smart	kfree_skb(skb);
59522447be7d15aefcfab84e9bec4859a28198b0c62James Smartmsg_fail:
59622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	printk(KERN_WARNING
59722447be7d15aefcfab84e9bec4859a28198b0c62James Smart		"%s: Dropped Message : pid %d Transport %d, msgtype x%x, "
59822447be7d15aefcfab84e9bec4859a28198b0c62James Smart		"msglen %d: %s : err %d\n",
59922447be7d15aefcfab84e9bec4859a28198b0c62James Smart		__func__, pid, hdr->transport, hdr->msgtype, hdr->msglen,
60022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		fn, err);
60122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	return;
60222447be7d15aefcfab84e9bec4859a28198b0c62James Smart}
60322447be7d15aefcfab84e9bec4859a28198b0c62James SmartEXPORT_SYMBOL_GPL(scsi_nl_send_transport_msg);
60422447be7d15aefcfab84e9bec4859a28198b0c62James Smart
60522447be7d15aefcfab84e9bec4859a28198b0c62James Smart
60622447be7d15aefcfab84e9bec4859a28198b0c62James Smart/**
60722447be7d15aefcfab84e9bec4859a28198b0c62James Smart * scsi_nl_send_vendor_msg - called to send a shost vendor unique message
60822447be7d15aefcfab84e9bec4859a28198b0c62James Smart *                      to a specific process id.
60922447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
61022447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @pid:		process id of the receiver
61122447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @host_no:		host # sending the message
61222447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @vendor_id:		unique identifier for the driver's vendor
61322447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @data_len:		amount, in bytes, of vendor unique payload data
61422447be7d15aefcfab84e9bec4859a28198b0c62James Smart * @data_buf:		pointer to vendor unique data buffer
61522447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
61622447be7d15aefcfab84e9bec4859a28198b0c62James Smart * Returns:
617af901ca181d92aac3a7dc265144a9081a86d8f39André Goddard Rosa *   0 on successful return
61822447be7d15aefcfab84e9bec4859a28198b0c62James Smart *   otherwise, failing error code
61922447be7d15aefcfab84e9bec4859a28198b0c62James Smart *
62022447be7d15aefcfab84e9bec4859a28198b0c62James Smart * Notes:
62122447be7d15aefcfab84e9bec4859a28198b0c62James Smart *	This routine assumes no locks are held on entry.
62222447be7d15aefcfab84e9bec4859a28198b0c62James Smart */
62322447be7d15aefcfab84e9bec4859a28198b0c62James Smartint
62422447be7d15aefcfab84e9bec4859a28198b0c62James Smartscsi_nl_send_vendor_msg(u32 pid, unsigned short host_no, u64 vendor_id,
62522447be7d15aefcfab84e9bec4859a28198b0c62James Smart			 char *data_buf, u32 data_len)
62622447be7d15aefcfab84e9bec4859a28198b0c62James Smart{
62722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct sk_buff *skb;
62822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct nlmsghdr	*nlh;
62922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	struct scsi_nl_host_vendor_msg *msg;
63022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	u32 len, skblen;
63122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	int err;
63222447be7d15aefcfab84e9bec4859a28198b0c62James Smart
63322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (!scsi_nl_sock) {
63422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		err = -ENOENT;
63522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		goto send_vendor_fail;
63622447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
63722447be7d15aefcfab84e9bec4859a28198b0c62James Smart
63822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	len = SCSI_NL_MSGALIGN(sizeof(*msg) + data_len);
63922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	skblen = NLMSG_SPACE(len);
64022447be7d15aefcfab84e9bec4859a28198b0c62James Smart
64122447be7d15aefcfab84e9bec4859a28198b0c62James Smart	skb = alloc_skb(skblen, GFP_KERNEL);
64222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (!skb) {
64322447be7d15aefcfab84e9bec4859a28198b0c62James Smart		err = -ENOBUFS;
64422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		goto send_vendor_fail;
64522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
64622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
64722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG,
64822447be7d15aefcfab84e9bec4859a28198b0c62James Smart				skblen - sizeof(*nlh), 0);
64922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (!nlh) {
65022447be7d15aefcfab84e9bec4859a28198b0c62James Smart		err = -ENOBUFS;
65122447be7d15aefcfab84e9bec4859a28198b0c62James Smart		goto send_vendor_fail_skb;
65222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	}
65322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	msg = NLMSG_DATA(nlh);
65422447be7d15aefcfab84e9bec4859a28198b0c62James Smart
65522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	INIT_SCSI_NL_HDR(&msg->snlh, SCSI_NL_TRANSPORT,
65622447be7d15aefcfab84e9bec4859a28198b0c62James Smart				SCSI_NL_SHOST_VENDOR, len);
65722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	msg->vendor_id = vendor_id;
65822447be7d15aefcfab84e9bec4859a28198b0c62James Smart	msg->host_no = host_no;
65922447be7d15aefcfab84e9bec4859a28198b0c62James Smart	msg->vmsg_datalen = data_len;	/* bytes */
66022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	memcpy(&msg[1], data_buf, data_len);
66122447be7d15aefcfab84e9bec4859a28198b0c62James Smart
66222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	err = nlmsg_unicast(scsi_nl_sock, skb, pid);
66322447be7d15aefcfab84e9bec4859a28198b0c62James Smart	if (err)
66422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		/* nlmsg_multicast already kfree_skb'd */
66522447be7d15aefcfab84e9bec4859a28198b0c62James Smart		goto send_vendor_fail;
66622447be7d15aefcfab84e9bec4859a28198b0c62James Smart
66722447be7d15aefcfab84e9bec4859a28198b0c62James Smart	return 0;
66822447be7d15aefcfab84e9bec4859a28198b0c62James Smart
66922447be7d15aefcfab84e9bec4859a28198b0c62James Smartsend_vendor_fail_skb:
67022447be7d15aefcfab84e9bec4859a28198b0c62James Smart	kfree_skb(skb);
67122447be7d15aefcfab84e9bec4859a28198b0c62James Smartsend_vendor_fail:
67222447be7d15aefcfab84e9bec4859a28198b0c62James Smart	printk(KERN_WARNING
67322447be7d15aefcfab84e9bec4859a28198b0c62James Smart		"%s: Dropped SCSI Msg : host %d vendor_unique - err %d\n",
67422447be7d15aefcfab84e9bec4859a28198b0c62James Smart		__func__, host_no, err);
67522447be7d15aefcfab84e9bec4859a28198b0c62James Smart	return err;
67622447be7d15aefcfab84e9bec4859a28198b0c62James Smart}
67722447be7d15aefcfab84e9bec4859a28198b0c62James SmartEXPORT_SYMBOL(scsi_nl_send_vendor_msg);
67822447be7d15aefcfab84e9bec4859a28198b0c62James Smart
67922447be7d15aefcfab84e9bec4859a28198b0c62James Smart
680