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