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