patch-2.1.15 linux/net/netlink.c

Next file: linux/net/netrom/af_netrom.c
Previous file: linux/net/netbeui/netbeui_name.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.14/linux/net/netlink.c linux/net/netlink.c
@@ -1,5 +1,5 @@
 /*
- * SKIPLINK	An implementation of a loadable kernel mode driver providing
+ * NETLINK	An implementation of a loadable kernel mode driver providing
  *		multiple kernel/user space bidirectional communications links.
  *
  * 		Author: 	Alan Cox <alan@cymru.net>
@@ -17,11 +17,11 @@
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/sched.h>
-#include <linux/lp.h>
 #include <linux/malloc.h>
 #include <linux/ioport.h>
 #include <linux/fcntl.h>
 #include <linux/delay.h>
+#include <linux/interrupt.h>
 #include <linux/skbuff.h>
 
 #include <net/netlink.h>
@@ -35,8 +35,8 @@
 static int rdq_size[MAX_LINKS];
 static struct wait_queue *read_space_wait[MAX_LINKS];
 
-static int active_map = 0;
-static int open_map = 0;
+static unsigned active_map = 0;
+static unsigned open_map = 0;
 
 /*
  *	Device operations
@@ -89,7 +89,6 @@
 	unsigned int minor = MINOR(inode->i_rdev);
 	struct sk_buff *skb;
 	skb=alloc_skb(count, GFP_KERNEL);
-	skb->free=1;
 	err = copy_from_user(skb_put(skb,count),buf, count);
 	return err ? -EFAULT : (netlink_handler[minor])(minor,skb);
 }
@@ -140,11 +139,14 @@
 	
 	if(minor>=MAX_LINKS)
 		return -ENODEV;
-	if(open_map&(1<<minor))
-		return -EBUSY;
 	if(active_map&(1<<minor))
 	{
-		open_map|=(1<<minor);
+		if (file->f_mode & FMODE_READ)
+		{
+			if (open_map&(1<<minor))
+				return -EBUSY;
+			open_map|=(1<<minor);
+		}
 		MOD_INC_USE_COUNT;
 		return 0;
 	}
@@ -154,7 +156,8 @@
 static void netlink_release(struct inode * inode, struct file * file)
 {
 	unsigned int minor = MINOR(inode->i_rdev);
-	open_map&=~(1<<minor);	
+	if (file->f_mode & FMODE_READ)
+		open_map&=~(1<<minor);
 	MOD_DEC_USE_COUNT;
 }
 
@@ -231,6 +234,215 @@
 	}
 	return ret;
 }
+
+
+/*
+ *	"High" level netlink interface. (ANK)
+ *	
+ *	Features:
+ *		- standard message format.
+ *		- pseudo-reliable delivery. Messages can be still lost, but
+ *		  user level will know that they were lost and can
+ *		  recover (f.e. gated could reread FIB and device list)
+ *		- messages are batched.
+ *		- if user is not attached, we do not make useless work.
+ *
+ *	Examples:
+ *		- netlink_post equivalent (but with pseudo-reliable delivery)
+ *			ctl.nlmsg_delay = 0;
+ *			ctl.nlmsg_maxsize = <one message size>;
+ *			....
+ *			msg = nlmsg_send(&ctl, ...);
+ *			if (msg) {
+ *				... make it ...
+ *				nlmsg_transmit(&ctl);
+ *			}
+ *
+ *		- batched messages.
+ *		  	if nlmsg_delay==0, messages are delivered only
+ *			by nlmsg_transmit, or when batch is completed,
+ *			otherwise nlmsg_transmit is noop (only starts
+ *			timer)
+ *
+ *			ctl.nlmsg_delay = ...;
+ *			ctl.nlmsg_maxsize = <one batch size>;
+ *			....
+ *			msg = nlmsg_send(&ctl, ...);
+ *			if (msg)
+ *				... make it ...
+ *			....
+ *			msg = nlmsg_send(&ctl, ...);
+ *			if (msg)
+ *				... make it ...
+ *			....
+ *			if (ctl.nlmsg_skb)
+ *				nlmsg_transmit(&ctl);
+ *
+ */
+
+/*
+ *	Try to deliver queued messages.
+ *	If the delivery fails (netlink is not attached or congested),
+ *	do not free skb to avoid useless new message creation.
+ *
+ *	Notes:
+ *		- timer should be already stopped.
+ *		- NET SPL.
+ */
+
+void nlmsg_flush(struct nlmsg_ctl *ctl)
+{
+	if (ctl->nlmsg_skb == NULL)
+		return;
+
+	if (netlink_post(ctl->nlmsg_unit, ctl->nlmsg_skb) == 0)
+	{
+		ctl->nlmsg_skb = NULL;
+		return;
+	}
+
+	ctl->nlmsg_timer.expires = jiffies + NLMSG_RECOVERY_TIMEO;
+	ctl->nlmsg_timer.data = (unsigned long)ctl;
+	ctl->nlmsg_timer.function = (void (*)(unsigned long))nlmsg_flush;
+	add_timer(&ctl->nlmsg_timer);
+	return;
+}
+
+
+/*
+ *	Allocate room for new message. If it is impossible,
+ *	start "overrun" mode and return NULL.
+ *
+ *	Notes:
+ *		- NET SPL.
+ */
+
+void* nlmsg_send(struct nlmsg_ctl *ctl, unsigned long type, int len,
+		 unsigned long seq, unsigned long pid)
+{
+	struct nlmsghdr *nlh;
+	struct sk_buff *skb;
+	int	rlen;
+
+	static __inline__ void nlmsg_lost(struct nlmsg_ctl *ctl,
+					  unsigned long seq)
+	{
+		if (!ctl->nlmsg_overrun)
+		{
+			ctl->nlmsg_overrun_start = seq;
+			ctl->nlmsg_overrun_end = seq;
+			ctl->nlmsg_overrun = 1;
+			return;
+		}
+		if (!ctl->nlmsg_overrun_start)
+			ctl->nlmsg_overrun_start = seq;
+		if (seq)
+			ctl->nlmsg_overrun_end = seq;
+	}
+
+	if (!(open_map&(1<<ctl->nlmsg_unit)))
+	{
+		nlmsg_lost(ctl, seq);
+		return NULL;
+	}
+
+	rlen = NLMSG_ALIGN(len + sizeof(struct nlmsghdr));
+
+	if (rlen > ctl->nlmsg_maxsize)
+	{
+		printk(KERN_ERR "nlmsg_send: too big message\n");
+		return NULL;
+	}
+
+	if ((skb=ctl->nlmsg_skb) == NULL || skb_tailroom(skb) < rlen)
+	{
+		if (skb)
+		{
+			ctl->nlmsg_force++;
+			nlmsg_flush(ctl);
+			ctl->nlmsg_force--;
+		}
+
+		if (ctl->nlmsg_skb ||
+		    (skb=alloc_skb(ctl->nlmsg_maxsize, GFP_ATOMIC)) == NULL)
+		{
+			printk (KERN_WARNING "nlmsg at unit %d overrunned\n", ctl->nlmsg_unit);
+			nlmsg_lost(ctl, seq);
+			return NULL;
+		}
+
+		ctl->nlmsg_skb = skb;
+
+		if (ctl->nlmsg_overrun)
+		{
+			int *seqp;
+			nlh = (struct nlmsghdr*)skb_put(skb, sizeof(struct nlmsghdr) + 2*sizeof(unsigned long));
+			nlh->nlmsg_type = NLMSG_OVERRUN;
+			nlh->nlmsg_len = sizeof(struct nlmsghdr) + 2*sizeof(unsigned long);
+			nlh->nlmsg_seq = 0;
+			nlh->nlmsg_pid = 0;
+			seqp = (int*)nlh->nlmsg_data;
+			seqp[0] = ctl->nlmsg_overrun_start;
+			seqp[1] = ctl->nlmsg_overrun_end;
+			ctl->nlmsg_overrun = 0;
+		}
+		if (ctl->nlmsg_timer.function)
+		{
+			del_timer(&ctl->nlmsg_timer);
+			ctl->nlmsg_timer.function = NULL;
+		}
+		if (ctl->nlmsg_delay)
+		{
+			ctl->nlmsg_timer.expires = jiffies + ctl->nlmsg_delay;
+			ctl->nlmsg_timer.function = (void (*)(unsigned long))nlmsg_flush;
+			ctl->nlmsg_timer.data = (unsigned long)ctl;
+			add_timer(&ctl->nlmsg_timer);
+		}
+	}
+
+	nlh = (struct nlmsghdr*)skb_put(skb, rlen);
+	nlh->nlmsg_type = type;
+	nlh->nlmsg_len = sizeof(struct nlmsghdr) + len;
+	nlh->nlmsg_seq = seq;
+	nlh->nlmsg_pid = pid;
+	return nlh->nlmsg_data;
+}
+
+/*
+ *	Kick message queue.
+ *	Two modes:
+ *		- synchronous (delay==0). Messages are delivered immediately.
+ *		- delayed. Do not deliver, but start delivery timer.
+ */
+
+void nlmsg_transmit(struct nlmsg_ctl *ctl)
+{
+	start_bh_atomic();
+
+	if (!ctl->nlmsg_delay)
+	{
+		if (ctl->nlmsg_timer.function)
+		{
+			del_timer(&ctl->nlmsg_timer);
+			ctl->nlmsg_timer.function = NULL;
+		}
+		ctl->nlmsg_force++;
+		nlmsg_flush(ctl);
+		ctl->nlmsg_force--;
+		end_bh_atomic();
+		return;
+	}
+	if (!ctl->nlmsg_timer.function)
+	{
+		ctl->nlmsg_timer.expires = jiffies + ctl->nlmsg_delay;
+		ctl->nlmsg_timer.function = (void (*)(unsigned long))nlmsg_flush;
+		ctl->nlmsg_timer.data = (unsigned long)ctl;
+		add_timer(&ctl->nlmsg_timer);
+	}
+
+	end_bh_atomic();
+}
+
 
 int init_netlink(void)
 {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov