• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/scsi/
1/*
2 *  scsi_netlink.c  - SCSI Transport Netlink Interface
3 *
4 *  Copyright (C) 2006   James Smart, Emulex Corporation
5 *
6 *  This program is free software; you can redistribute it and/or modify
7 *  it under the terms of the GNU General Public License as published by
8 *  the Free Software Foundation; either version 2 of the License, or
9 *  (at your option) any later version.
10 *
11 *  This program is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 *  GNU General Public License for more details.
15 *
16 *  You should have received a copy of the GNU General Public License
17 *  along with this program; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 *
20 */
21#include <linux/time.h>
22#include <linux/jiffies.h>
23#include <linux/security.h>
24#include <linux/delay.h>
25#include <linux/slab.h>
26#include <net/sock.h>
27#include <net/netlink.h>
28
29#include <scsi/scsi_netlink.h>
30#include "scsi_priv.h"
31
32struct sock *scsi_nl_sock = NULL;
33EXPORT_SYMBOL_GPL(scsi_nl_sock);
34
35static DEFINE_SPINLOCK(scsi_nl_lock);
36static struct list_head scsi_nl_drivers;
37
38static u32	scsi_nl_state;
39#define STATE_EHANDLER_BSY		0x00000001
40
41struct scsi_nl_transport {
42	int (*msg_handler)(struct sk_buff *);
43	void (*event_handler)(struct notifier_block *, unsigned long, void *);
44	unsigned int refcnt;
45	int flags;
46};
47
48/* flags values (bit flags) */
49#define HANDLER_DELETING		0x1
50
51static struct scsi_nl_transport transports[SCSI_NL_MAX_TRANSPORTS] =
52	{ {NULL, }, };
53
54
55struct scsi_nl_drvr {
56	struct list_head next;
57	int (*dmsg_handler)(struct Scsi_Host *shost, void *payload,
58				 u32 len, u32 pid);
59	void (*devt_handler)(struct notifier_block *nb,
60				 unsigned long event, void *notify_ptr);
61	struct scsi_host_template *hostt;
62	u64 vendor_id;
63	unsigned int refcnt;
64	int flags;
65};
66
67
68
69/**
70 * scsi_nl_rcv_msg - Receive message handler.
71 * @skb:		socket receive buffer
72 *
73 * Description: Extracts message from a receive buffer.
74 *    Validates message header and calls appropriate transport message handler
75 *
76 *
77 **/
78static void
79scsi_nl_rcv_msg(struct sk_buff *skb)
80{
81	struct nlmsghdr *nlh;
82	struct scsi_nl_hdr *hdr;
83	unsigned long flags;
84	u32 rlen;
85	int err, tport;
86
87	while (skb->len >= NLMSG_SPACE(0)) {
88		err = 0;
89
90		nlh = nlmsg_hdr(skb);
91		if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
92		    (skb->len < nlh->nlmsg_len)) {
93			printk(KERN_WARNING "%s: discarding partial skb\n",
94				 __func__);
95			return;
96		}
97
98		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
99		if (rlen > skb->len)
100			rlen = skb->len;
101
102		if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
103			err = -EBADMSG;
104			goto next_msg;
105		}
106
107		hdr = NLMSG_DATA(nlh);
108		if ((hdr->version != SCSI_NL_VERSION) ||
109		    (hdr->magic != SCSI_NL_MAGIC)) {
110			err = -EPROTOTYPE;
111			goto next_msg;
112		}
113
114		if (security_netlink_recv(skb, CAP_SYS_ADMIN)) {
115			err = -EPERM;
116			goto next_msg;
117		}
118
119		if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
120			printk(KERN_WARNING "%s: discarding partial message\n",
121				 __func__);
122			goto next_msg;
123		}
124
125		/*
126		 * Deliver message to the appropriate transport
127		 */
128		spin_lock_irqsave(&scsi_nl_lock, flags);
129
130		tport = hdr->transport;
131		if ((tport < SCSI_NL_MAX_TRANSPORTS) &&
132		    !(transports[tport].flags & HANDLER_DELETING) &&
133		    (transports[tport].msg_handler)) {
134			transports[tport].refcnt++;
135			spin_unlock_irqrestore(&scsi_nl_lock, flags);
136			err = transports[tport].msg_handler(skb);
137			spin_lock_irqsave(&scsi_nl_lock, flags);
138			transports[tport].refcnt--;
139		} else
140			err = -ENOENT;
141
142		spin_unlock_irqrestore(&scsi_nl_lock, flags);
143
144next_msg:
145		if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
146			netlink_ack(skb, nlh, err);
147
148		skb_pull(skb, rlen);
149	}
150}
151
152
153/**
154 * scsi_nl_rcv_event - Event handler for a netlink socket.
155 * @this:		event notifier block
156 * @event:		event type
157 * @ptr:		event payload
158 *
159 **/
160static int
161scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr)
162{
163	struct netlink_notify *n = ptr;
164	struct scsi_nl_drvr *driver;
165	unsigned long flags;
166	int tport;
167
168	if (n->protocol != NETLINK_SCSITRANSPORT)
169		return NOTIFY_DONE;
170
171	spin_lock_irqsave(&scsi_nl_lock, flags);
172	scsi_nl_state |= STATE_EHANDLER_BSY;
173
174	/*
175	 * Pass event on to any transports that may be listening
176	 */
177	for (tport = 0; tport < SCSI_NL_MAX_TRANSPORTS; tport++) {
178		if (!(transports[tport].flags & HANDLER_DELETING) &&
179		    (transports[tport].event_handler)) {
180			spin_unlock_irqrestore(&scsi_nl_lock, flags);
181			transports[tport].event_handler(this, event, ptr);
182			spin_lock_irqsave(&scsi_nl_lock, flags);
183		}
184	}
185
186	/*
187	 * Pass event on to any drivers that may be listening
188	 */
189	list_for_each_entry(driver, &scsi_nl_drivers, next) {
190		if (!(driver->flags & HANDLER_DELETING) &&
191		    (driver->devt_handler)) {
192			spin_unlock_irqrestore(&scsi_nl_lock, flags);
193			driver->devt_handler(this, event, ptr);
194			spin_lock_irqsave(&scsi_nl_lock, flags);
195		}
196	}
197
198	scsi_nl_state &= ~STATE_EHANDLER_BSY;
199	spin_unlock_irqrestore(&scsi_nl_lock, flags);
200
201	return NOTIFY_DONE;
202}
203
204static struct notifier_block scsi_netlink_notifier = {
205	.notifier_call  = scsi_nl_rcv_event,
206};
207
208
209/*
210 * GENERIC SCSI transport receive and event handlers
211 */
212
213/**
214 * scsi_generic_msg_handler - receive message handler for GENERIC transport messages
215 * @skb:		socket receive buffer
216 **/
217static int
218scsi_generic_msg_handler(struct sk_buff *skb)
219{
220	struct nlmsghdr *nlh = nlmsg_hdr(skb);
221	struct scsi_nl_hdr *snlh = NLMSG_DATA(nlh);
222	struct scsi_nl_drvr *driver;
223	struct Scsi_Host *shost;
224	unsigned long flags;
225	int err = 0, match, pid;
226
227	pid = NETLINK_CREDS(skb)->pid;
228
229	switch (snlh->msgtype) {
230	case SCSI_NL_SHOST_VENDOR:
231		{
232		struct scsi_nl_host_vendor_msg *msg = NLMSG_DATA(nlh);
233
234		/* Locate the driver that corresponds to the message */
235		spin_lock_irqsave(&scsi_nl_lock, flags);
236		match = 0;
237		list_for_each_entry(driver, &scsi_nl_drivers, next) {
238			if (driver->vendor_id == msg->vendor_id) {
239				match = 1;
240				break;
241			}
242		}
243
244		if ((!match) || (!driver->dmsg_handler)) {
245			spin_unlock_irqrestore(&scsi_nl_lock, flags);
246			err = -ESRCH;
247			goto rcv_exit;
248		}
249
250		if (driver->flags & HANDLER_DELETING) {
251			spin_unlock_irqrestore(&scsi_nl_lock, flags);
252			err = -ESHUTDOWN;
253			goto rcv_exit;
254		}
255
256		driver->refcnt++;
257		spin_unlock_irqrestore(&scsi_nl_lock, flags);
258
259
260		/* if successful, scsi_host_lookup takes a shost reference */
261		shost = scsi_host_lookup(msg->host_no);
262		if (!shost) {
263			err = -ENODEV;
264			goto driver_exit;
265		}
266
267		/* is this host owned by the vendor ? */
268		if (shost->hostt != driver->hostt) {
269			err = -EINVAL;
270			goto vendormsg_put;
271		}
272
273		/* pass message on to the driver */
274		err = driver->dmsg_handler(shost, (void *)&msg[1],
275					 msg->vmsg_datalen, pid);
276
277vendormsg_put:
278		/* release reference by scsi_host_lookup */
279		scsi_host_put(shost);
280
281driver_exit:
282		/* release our own reference on the registration object */
283		spin_lock_irqsave(&scsi_nl_lock, flags);
284		driver->refcnt--;
285		spin_unlock_irqrestore(&scsi_nl_lock, flags);
286		break;
287		}
288
289	default:
290		err = -EBADR;
291		break;
292	}
293
294rcv_exit:
295	if (err)
296		printk(KERN_WARNING "%s: Msgtype %d failed - err %d\n",
297			 __func__, snlh->msgtype, err);
298	return err;
299}
300
301
302/**
303 * scsi_nl_add_transport -
304 *    Registers message and event handlers for a transport. Enables
305 *    receipt of netlink messages and events to a transport.
306 *
307 * @tport:		transport registering handlers
308 * @msg_handler:	receive message handler callback
309 * @event_handler:	receive event handler callback
310 **/
311int
312scsi_nl_add_transport(u8 tport,
313	int (*msg_handler)(struct sk_buff *),
314	void (*event_handler)(struct notifier_block *, unsigned long, void *))
315{
316	unsigned long flags;
317	int err = 0;
318
319	if (tport >= SCSI_NL_MAX_TRANSPORTS)
320		return -EINVAL;
321
322	spin_lock_irqsave(&scsi_nl_lock, flags);
323
324	if (scsi_nl_state & STATE_EHANDLER_BSY) {
325		spin_unlock_irqrestore(&scsi_nl_lock, flags);
326		msleep(1);
327		spin_lock_irqsave(&scsi_nl_lock, flags);
328	}
329
330	if (transports[tport].msg_handler || transports[tport].event_handler) {
331		err = -EALREADY;
332		goto register_out;
333	}
334
335	transports[tport].msg_handler = msg_handler;
336	transports[tport].event_handler = event_handler;
337	transports[tport].flags = 0;
338	transports[tport].refcnt = 0;
339
340register_out:
341	spin_unlock_irqrestore(&scsi_nl_lock, flags);
342
343	return err;
344}
345EXPORT_SYMBOL_GPL(scsi_nl_add_transport);
346
347
348/**
349 * scsi_nl_remove_transport -
350 *    Disable transport receiption of messages and events
351 *
352 * @tport:		transport deregistering handlers
353 *
354 **/
355void
356scsi_nl_remove_transport(u8 tport)
357{
358	unsigned long flags;
359
360	spin_lock_irqsave(&scsi_nl_lock, flags);
361	if (scsi_nl_state & STATE_EHANDLER_BSY) {
362		spin_unlock_irqrestore(&scsi_nl_lock, flags);
363		msleep(1);
364		spin_lock_irqsave(&scsi_nl_lock, flags);
365	}
366
367	if (tport < SCSI_NL_MAX_TRANSPORTS) {
368		transports[tport].flags |= HANDLER_DELETING;
369
370		while (transports[tport].refcnt != 0) {
371			spin_unlock_irqrestore(&scsi_nl_lock, flags);
372			schedule_timeout_uninterruptible(HZ/4);
373			spin_lock_irqsave(&scsi_nl_lock, flags);
374		}
375		transports[tport].msg_handler = NULL;
376		transports[tport].event_handler = NULL;
377		transports[tport].flags = 0;
378	}
379
380	spin_unlock_irqrestore(&scsi_nl_lock, flags);
381
382	return;
383}
384EXPORT_SYMBOL_GPL(scsi_nl_remove_transport);
385
386
387/**
388 * scsi_nl_add_driver -
389 *    A driver is registering its interfaces for SCSI netlink messages
390 *
391 * @vendor_id:          A unique identification value for the driver.
392 * @hostt:		address of the driver's host template. Used
393 *			to verify an shost is bound to the driver
394 * @nlmsg_handler:	receive message handler callback
395 * @nlevt_handler:	receive event handler callback
396 *
397 * Returns:
398 *   0 on Success
399 *   error result otherwise
400 **/
401int
402scsi_nl_add_driver(u64 vendor_id, struct scsi_host_template *hostt,
403	int (*nlmsg_handler)(struct Scsi_Host *shost, void *payload,
404				 u32 len, u32 pid),
405	void (*nlevt_handler)(struct notifier_block *nb,
406				 unsigned long event, void *notify_ptr))
407{
408	struct scsi_nl_drvr *driver;
409	unsigned long flags;
410
411	driver = kzalloc(sizeof(*driver), GFP_KERNEL);
412	if (unlikely(!driver)) {
413		printk(KERN_ERR "%s: allocation failure\n", __func__);
414		return -ENOMEM;
415	}
416
417	driver->dmsg_handler = nlmsg_handler;
418	driver->devt_handler = nlevt_handler;
419	driver->hostt = hostt;
420	driver->vendor_id = vendor_id;
421
422	spin_lock_irqsave(&scsi_nl_lock, flags);
423	if (scsi_nl_state & STATE_EHANDLER_BSY) {
424		spin_unlock_irqrestore(&scsi_nl_lock, flags);
425		msleep(1);
426		spin_lock_irqsave(&scsi_nl_lock, flags);
427	}
428	list_add_tail(&driver->next, &scsi_nl_drivers);
429	spin_unlock_irqrestore(&scsi_nl_lock, flags);
430
431	return 0;
432}
433EXPORT_SYMBOL_GPL(scsi_nl_add_driver);
434
435
436/**
437 * scsi_nl_remove_driver -
438 *    An driver is unregistering with the SCSI netlink messages
439 *
440 * @vendor_id:          The unique identification value for the driver.
441 **/
442void
443scsi_nl_remove_driver(u64 vendor_id)
444{
445	struct scsi_nl_drvr *driver;
446	unsigned long flags;
447
448	spin_lock_irqsave(&scsi_nl_lock, flags);
449	if (scsi_nl_state & STATE_EHANDLER_BSY) {
450		spin_unlock_irqrestore(&scsi_nl_lock, flags);
451		msleep(1);
452		spin_lock_irqsave(&scsi_nl_lock, flags);
453	}
454
455	list_for_each_entry(driver, &scsi_nl_drivers, next) {
456		if (driver->vendor_id == vendor_id) {
457			driver->flags |= HANDLER_DELETING;
458			while (driver->refcnt != 0) {
459				spin_unlock_irqrestore(&scsi_nl_lock, flags);
460				schedule_timeout_uninterruptible(HZ/4);
461				spin_lock_irqsave(&scsi_nl_lock, flags);
462			}
463			list_del(&driver->next);
464			kfree(driver);
465			spin_unlock_irqrestore(&scsi_nl_lock, flags);
466			return;
467		}
468	}
469
470	spin_unlock_irqrestore(&scsi_nl_lock, flags);
471
472	printk(KERN_ERR "%s: removal of driver failed - vendor_id 0x%llx\n",
473	       __func__, (unsigned long long)vendor_id);
474	return;
475}
476EXPORT_SYMBOL_GPL(scsi_nl_remove_driver);
477
478
479/**
480 * scsi_netlink_init - Called by SCSI subsystem to intialize
481 * 	the SCSI transport netlink interface
482 *
483 **/
484void
485scsi_netlink_init(void)
486{
487	int error;
488
489	INIT_LIST_HEAD(&scsi_nl_drivers);
490
491	error = netlink_register_notifier(&scsi_netlink_notifier);
492	if (error) {
493		printk(KERN_ERR "%s: register of event handler failed - %d\n",
494				__func__, error);
495		return;
496	}
497
498	scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
499				SCSI_NL_GRP_CNT, scsi_nl_rcv_msg, NULL,
500				THIS_MODULE);
501	if (!scsi_nl_sock) {
502		printk(KERN_ERR "%s: register of recieve handler failed\n",
503				__func__);
504		netlink_unregister_notifier(&scsi_netlink_notifier);
505		return;
506	}
507
508	/* Register the entry points for the generic SCSI transport */
509	error = scsi_nl_add_transport(SCSI_NL_TRANSPORT,
510				scsi_generic_msg_handler, NULL);
511	if (error)
512		printk(KERN_ERR "%s: register of GENERIC transport handler"
513				"  failed - %d\n", __func__, error);
514	return;
515}
516
517
518/**
519 * scsi_netlink_exit - Called by SCSI subsystem to disable the SCSI transport netlink interface
520 *
521 **/
522void
523scsi_netlink_exit(void)
524{
525	scsi_nl_remove_transport(SCSI_NL_TRANSPORT);
526
527	if (scsi_nl_sock) {
528		netlink_kernel_release(scsi_nl_sock);
529		netlink_unregister_notifier(&scsi_netlink_notifier);
530	}
531
532	return;
533}
534
535
536/*
537 * Exported Interfaces
538 */
539
540/**
541 * scsi_nl_send_transport_msg -
542 *    Generic function to send a single message from a SCSI transport to
543 *    a single process
544 *
545 * @pid:		receiving pid
546 * @hdr:		message payload
547 *
548 **/
549void
550scsi_nl_send_transport_msg(u32 pid, struct scsi_nl_hdr *hdr)
551{
552	struct sk_buff *skb;
553	struct nlmsghdr	*nlh;
554	const char *fn;
555	char *datab;
556	u32 len, skblen;
557	int err;
558
559	if (!scsi_nl_sock) {
560		err = -ENOENT;
561		fn = "netlink socket";
562		goto msg_fail;
563	}
564
565	len = NLMSG_SPACE(hdr->msglen);
566	skblen = NLMSG_SPACE(len);
567
568	skb = alloc_skb(skblen, GFP_KERNEL);
569	if (!skb) {
570		err = -ENOBUFS;
571		fn = "alloc_skb";
572		goto msg_fail;
573	}
574
575	nlh = nlmsg_put(skb, pid, 0, SCSI_TRANSPORT_MSG, len - sizeof(*nlh), 0);
576	if (!nlh) {
577		err = -ENOBUFS;
578		fn = "nlmsg_put";
579		goto msg_fail_skb;
580	}
581	datab = NLMSG_DATA(nlh);
582	memcpy(datab, hdr, hdr->msglen);
583
584	err = nlmsg_unicast(scsi_nl_sock, skb, pid);
585	if (err < 0) {
586		fn = "nlmsg_unicast";
587		/* nlmsg_unicast already kfree_skb'd */
588		goto msg_fail;
589	}
590
591	return;
592
593msg_fail_skb:
594	kfree_skb(skb);
595msg_fail:
596	printk(KERN_WARNING
597		"%s: Dropped Message : pid %d Transport %d, msgtype x%x, "
598		"msglen %d: %s : err %d\n",
599		__func__, pid, hdr->transport, hdr->msgtype, hdr->msglen,
600		fn, err);
601	return;
602}
603EXPORT_SYMBOL_GPL(scsi_nl_send_transport_msg);
604
605
606/**
607 * scsi_nl_send_vendor_msg - called to send a shost vendor unique message
608 *                      to a specific process id.
609 *
610 * @pid:		process id of the receiver
611 * @host_no:		host # sending the message
612 * @vendor_id:		unique identifier for the driver's vendor
613 * @data_len:		amount, in bytes, of vendor unique payload data
614 * @data_buf:		pointer to vendor unique data buffer
615 *
616 * Returns:
617 *   0 on successful return
618 *   otherwise, failing error code
619 *
620 * Notes:
621 *	This routine assumes no locks are held on entry.
622 */
623int
624scsi_nl_send_vendor_msg(u32 pid, unsigned short host_no, u64 vendor_id,
625			 char *data_buf, u32 data_len)
626{
627	struct sk_buff *skb;
628	struct nlmsghdr	*nlh;
629	struct scsi_nl_host_vendor_msg *msg;
630	u32 len, skblen;
631	int err;
632
633	if (!scsi_nl_sock) {
634		err = -ENOENT;
635		goto send_vendor_fail;
636	}
637
638	len = SCSI_NL_MSGALIGN(sizeof(*msg) + data_len);
639	skblen = NLMSG_SPACE(len);
640
641	skb = alloc_skb(skblen, GFP_KERNEL);
642	if (!skb) {
643		err = -ENOBUFS;
644		goto send_vendor_fail;
645	}
646
647	nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG,
648				skblen - sizeof(*nlh), 0);
649	if (!nlh) {
650		err = -ENOBUFS;
651		goto send_vendor_fail_skb;
652	}
653	msg = NLMSG_DATA(nlh);
654
655	INIT_SCSI_NL_HDR(&msg->snlh, SCSI_NL_TRANSPORT,
656				SCSI_NL_SHOST_VENDOR, len);
657	msg->vendor_id = vendor_id;
658	msg->host_no = host_no;
659	msg->vmsg_datalen = data_len;	/* bytes */
660	memcpy(&msg[1], data_buf, data_len);
661
662	err = nlmsg_unicast(scsi_nl_sock, skb, pid);
663	if (err)
664		/* nlmsg_multicast already kfree_skb'd */
665		goto send_vendor_fail;
666
667	return 0;
668
669send_vendor_fail_skb:
670	kfree_skb(skb);
671send_vendor_fail:
672	printk(KERN_WARNING
673		"%s: Dropped SCSI Msg : host %d vendor_unique - err %d\n",
674		__func__, host_no, err);
675	return err;
676}
677EXPORT_SYMBOL(scsi_nl_send_vendor_msg);
678