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
- Lines: 283
- Date:
Thu Dec 12 16:54:26 1996
- Orig file:
v2.1.14/linux/net/netlink.c
- Orig date:
Tue Nov 19 15:54:02 1996
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