Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/lkm/net/ethfoo/ethfoo Add tap (as in Linux's tap) functi...



details:   https://anonhg.NetBSD.org/src/rev/8e55beab0ebf
branches:  trunk
changeset: 571267:8e55beab0ebf
user:      cube <cube%NetBSD.org@localhost>
date:      Sun Nov 14 20:05:42 2004 +0000

description:
Add tap (as in Linux's tap) functionality to ethfoo.  That means you now
have a device interface to the ethfoo devices through a device node.

select, poll, kqueue and SIGIO are possible on that device node.  See TODO
for what remains to be done at that level.

diffstat:

 sys/lkm/net/ethfoo/ethfoo/TODO         |    2 +
 sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c |  586 ++++++++++++++++++++++++++++----
 2 files changed, 514 insertions(+), 74 deletions(-)

diffs (truncated from 827 to 300 lines):

diff -r 88ded67fe1b7 -r 8e55beab0ebf sys/lkm/net/ethfoo/ethfoo/TODO
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/lkm/net/ethfoo/ethfoo/TODO    Sun Nov 14 20:05:42 2004 +0000
@@ -0,0 +1,2 @@
+o hunt a bug in kqueue code that makes ethfoo crash under heavy load
+o add comments for all the device-related functions
diff -r 88ded67fe1b7 -r 8e55beab0ebf sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c
--- a/sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c    Sun Nov 14 19:53:59 2004 +0000
+++ b/sys/lkm/net/ethfoo/ethfoo/ethfoo_lkm.c    Sun Nov 14 20:05:42 2004 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ethfoo_lkm.c,v 1.5 2004/10/15 04:51:48 thorpej Exp $   */
+/*     $NetBSD: ethfoo_lkm.c,v 1.6 2004/11/14 20:05:42 cube Exp $      */
 
 /*
  *  Copyright (c) 2003, 2004 The NetBSD Foundation.
@@ -40,11 +40,13 @@
  * ethfoo is a NetBSD Loadable Kernel Module that demonstrates the use of
  * several kernel mechanisms, mostly in the networking subsytem.
  *
- * First, it is sample LKM, with the standard LKM management routines and
- * Second, sample Ethernet driver.
- * Third, sample of use of autoconf stuff inside a LKM.
- * Fourth, sample clonable interface
- * Fifth, sample sysctl interface use from a LKM.
+ * 1. it is example LKM, with the standard LKM management routines and
+ * 2. example Ethernet driver.
+ * 3. example of use of autoconf stuff inside a LKM.
+ * 4. example clonable network interface.
+ * 5. example sysctl interface use from a LKM.
+ * 6. example LKM character device, with read, write, ioctl, poll
+ *    and kqueue available.
  *
  * XXX Hacks
  * 1. NetBSD doesn't offer a way to change an Ethernet address, so I chose
@@ -57,11 +59,15 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
+#include <sys/conf.h>
 #include <sys/device.h>
+#include <sys/file.h>
 #if __NetBSD_Version__ > 106200000
 #include <sys/ksyms.h>
 #endif
 #include <sys/lkm.h>
+#include <sys/poll.h>
+#include <sys/select.h>
 #include <sys/sockio.h>
 #include <sys/sysctl.h>
 
@@ -74,10 +80,6 @@
 
 CFDRIVER_DECL(ethfoo, DV_DULL, NULL);
 
-static struct cfdata ethfoo_cfdata = {
-       "ethfoo", "ethfoo", 0, FSTATE_STAR,
-};
-
 /*
  * sysctl node management
  *
@@ -94,7 +96,7 @@
  */
 static int ethfoo_node;
 static struct sysctllog *ethfoo_log;
-static int     ethfoo_sysctl_setup();
+static int     ethfoo_sysctl_setup(void);
 static int     ethfoo_sysctl_handler(SYSCTLFN_PROTO);
 
 /*
@@ -115,7 +117,14 @@
        struct ifmedia  sc_im;
        struct ethercom sc_ec;
        void            (*sc_bpf_mtap)(caddr_t, struct mbuf *);
-       int             sc_mibnum;
+       int             sc_flags;
+#define        ETHFOO_INUSE    0x00000001      /* ethfoo device can only be opened once */
+#define ETHFOO_ASYNCIO 0x00000002      /* user is using async I/O (SIGIO) on the device */
+#define ETHFOO_NBIO    0x00000004      /* user wants calls to avoid blocking */
+       struct selinfo  sc_rsel;
+       pid_t           sc_pgid; /* For async. IO */
+       struct lock     sc_rdlock;
+       struct simplelock       sc_kqlock;
 };
 
 /* LKM management routines */
@@ -133,18 +142,44 @@
 /* Ethernet address helper functions */
 
 static char    *ethfoo_ether_sprintf(char *, const u_char *);
-static void    ethfoo_ether_aton(u_char *, char *);
+static int     ethfoo_ether_aton(u_char *, char *);
 
 CFATTACH_DECL(ethfoo, sizeof(struct ethfoo_softc),
     ethfoo_match, ethfoo_attach, ethfoo_detach, NULL);
 
+/* Character device routines */
+static int     ethfoo_cdev_open(dev_t, int, int, struct proc *);
+static int     ethfoo_cdev_close(dev_t, int, int, struct proc *);
+static int     ethfoo_cdev_read(dev_t, struct uio *, int);
+static int     ethfoo_cdev_write(dev_t, struct uio *, int);
+static int     ethfoo_cdev_ioctl(dev_t, u_long, caddr_t, int, struct proc *);
+static int     ethfoo_cdev_poll(dev_t, int, struct proc *);
+static int     ethfoo_cdev_kqfilter(dev_t, struct knote *);
+
+static struct cdevsw ethfoo_cdevsw = {
+       ethfoo_cdev_open, ethfoo_cdev_close,
+       ethfoo_cdev_read, ethfoo_cdev_write,
+       ethfoo_cdev_ioctl, nostop, notty,
+       ethfoo_cdev_poll, nommap,
+       ethfoo_cdev_kqfilter,
+};
+
+/* kqueue-related routines */
+static void    ethfoo_kqdetach(struct knote *);
+static int     ethfoo_kqread(struct knote *, long);
+
 /*
- * We don't need any particular functionality available to LKMs,
- * such as a device number or a syscall number, thus MOD_MISC is
- * enough.
+ * The type of the module is actually userland-oriented.  For a
+ * traditional Ethernet driver, MOD_MISC would be enough since
+ * the userland manipulates interfaces through operations on
+ * sockets.
+ *
+ * Here MOD_DEV is chosen because a direct access interface is
+ * exposed, and the easiest way to achieve this is through a
+ * regular device node.
  */
 
-MOD_MISC("ethfoo");
+MOD_DEV("ethfoo", "ethfoo", NULL, -1, &ethfoo_cdevsw, -1);
 
 /*
  * Those are needed by the if_media interface.
@@ -184,7 +219,8 @@
 int
 ethfoo_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
 {
-       DISPATCH(lkmtp, cmd, ver, ethfoo_lkmload, ethfoo_lkmunload, lkm_nofunc);
+       DISPATCH(lkmtp, cmd, ver,
+           ethfoo_lkmload, ethfoo_lkmunload, lkm_nofunc);
 }
 
 /*
@@ -235,9 +271,7 @@
 /*
  * Cleaning up is the most critical part of a LKM, since a module is not
  * actually made to be loadable, but rather "unloadable".  If it is only
- * to be loaded, you'd better link it to the kernel in the first place,
- * either through compilation or through static linking (I think modload
- * allows that).
+ * to be loaded, you'd better link it to the kernel in the first place.
  *
  * The interface cloning mechanism is really simple, with only two void
  * returning functions.  It will always do its job. You should note though
@@ -268,7 +302,8 @@
                        return error;
                }
 
-       if ((error = config_cfattach_detach(ethfoo_cd.cd_name, &ethfoo_ca)) != 0) {
+       if ((error = config_cfattach_detach(ethfoo_cd.cd_name,
+           &ethfoo_ca)) != 0) {
                aprint_error("%s: unable to deregister cfattach\n",
                    ethfoo_cd.cd_name);
                return error;
@@ -295,7 +330,8 @@
 {
        struct ethfoo_softc *sc = (struct ethfoo_softc *)self;
        struct ifnet *ifp;
-       u_int8_t enaddr[ETHER_ADDR_LEN] = { 0xf0, 0x0b, 0xa4, 0xff, 0xff, 0xff };
+       u_int8_t enaddr[ETHER_ADDR_LEN] =
+           { 0xf0, 0x0b, 0xa4, 0xff, 0xff, 0xff };
        char enaddrstr[18];
        unsigned long u;
        uint32_t ui;
@@ -319,15 +355,19 @@
        /* ksyms interface is only available since mid-1.6R, so require
         * at least 1.6T
         */
+       {
 #if __NetBSD_Version__ > 106200000
 # ifndef ksyms_getval_from_kernel
 #  define ksyms_getval_from_kernel ksyms_getval
+# endif
+               char sym[] = "bpf_mtap";
+               if (ksyms_getval_from_kernel(NULL, sym, &u,
+                   KSYMS_PROC) != ENOENT)
+                       sc->sc_bpf_mtap = (void *)u;
+               else
 #endif
-       if (ksyms_getval_from_kernel(NULL, "bpf_mtap", &u, KSYMS_PROC) != ENOENT)
-               sc->sc_bpf_mtap = (void *)u;
-       else
-#endif
-               sc->sc_bpf_mtap = NULL;
+                       sc->sc_bpf_mtap = NULL;
+       }
 
        /*
         * Why 1000baseT? Why not? You can add more.
@@ -338,7 +378,13 @@
         */
        ifmedia_init(&sc->sc_im, 0, ethfoo_mediachange, ethfoo_mediastatus);
        ifmedia_add(&sc->sc_im, IFM_ETHER|IFM_1000_T, 0, NULL);
-       ifmedia_set(&sc->sc_im, IFM_ETHER|IFM_1000_T);
+       ifmedia_add(&sc->sc_im, IFM_ETHER|IFM_1000_T|IFM_FDX, 0, NULL);
+       ifmedia_add(&sc->sc_im, IFM_ETHER|IFM_100_TX, 0, NULL);
+       ifmedia_add(&sc->sc_im, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
+       ifmedia_add(&sc->sc_im, IFM_ETHER|IFM_10_T, 0, NULL);
+       ifmedia_add(&sc->sc_im, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
+       ifmedia_add(&sc->sc_im, IFM_ETHER|IFM_AUTO, 0, NULL);
+       ifmedia_set(&sc->sc_im, IFM_ETHER|IFM_AUTO);
 
        /*
         * One should note that an interface must do multicast in order
@@ -361,27 +407,45 @@
        if_attach(ifp);
        ether_ifattach(ifp, enaddr);
 
+       sc->sc_flags = 0;
+
        /*
         * Add a sysctl node for that interface.
         *
-        * The pointer transmitted is not a string, but instead a pointer to the softc
-        * structure, which we can use to build the string value on the fly in the helper
-        * function of the node.  See the comments for ethfoo_sysctl_handler for details.
-        *
-        * As in ethfoo_sysctl_setup, we use ethfoo_log.  This allows the use of
-        * sysctl_teardown() in ethfoo_lkmunload, which undoes every creation we made in the
-        * module.
+        * The pointer transmitted is not a string, but instead a pointer to
+        * the softc structure, which we can use to build the string value on
+        * the fly in the helper function of the node.  See the comments for
+        * ethfoo_sysctl_handler for details.
+        *
+        * As in ethfoo_sysctl_setup, we use ethfoo_log.  This allows the use
+        * of sysctl_teardown() in ethfoo_lkmunload, which undoes every
+        * creation we made in the module.
         */
        if ((error = sysctl_createv(&ethfoo_log, 0, NULL,
            &node, CTLFLAG_READWRITE,
            CTLTYPE_STRING, sc->sc_dev.dv_xname, NULL,
            ethfoo_sysctl_handler, 0, sc, 18,
-           CTL_NET, PF_LINK, ethfoo_node, CTL_CREATE, CTL_EOL)) != 0) {
-               sc->sc_mibnum = -1;
+           CTL_NET, PF_LINK, ethfoo_node, sc->sc_dev.dv_unit, CTL_EOL)) != 0)
                aprint_error("%s: sysctl_createv returned %d, ignoring\n",
                    sc->sc_dev.dv_xname, error);
-       } else
-               sc->sc_mibnum = node->sysctl_num;
+
+       /*
+        * Initialize the two locks for the device.
+        *
+        * We need a lock here because even though the ethfoo device can be
+        * opened only once, the file descriptor might be passed to another
+        * process, say a fork(2)ed child.
+        *
+        * The Giant saves us from most of the hassle, but since the read
+        * operation can sleep, we don't want two processes to wake up at
+        * the same moment and both try and dequeue a single packet.
+        *
+        * The queue for event listeners (used by kqueue(9), see below) has
+        * to be protected, too, but we don't need the same level of
+        * complexity for that lock, so a simple spinning lock is fine.
+        */
+       lockinit(&sc->sc_rdlock, PSOCK|PCATCH, "ethfool", 0, LK_SLEEPFAIL);
+       simple_lock_init(&sc->sc_kqlock);
 }
 
 /*
@@ -393,25 +457,34 @@
 {
        struct ethfoo_softc *sc = (struct ethfoo_softc *)self;
        struct ifnet *ifp = &sc->sc_ec.ec_if;
-       int error;
+       int error, s;
+
+       /*
+        * Some processes might be sleeping on "ethfoo", so we have to make
+        * them release their hold on the device.
+        *
+        * The LK_DRAIN operation will wait for every locked process to
+        * release their hold.
+        */
+       s = splnet();
+       ethfoo_stop(ifp, 1);
+       if_down(ifp);
+       splx(s);
+       lockmgr(&sc->sc_rdlock, LK_DRAIN, NULL);
 
        /*
         * Destroying a single leaf is a very straightforward operation using
         * sysctl_destroyv.  One should be sure to always end the path with
         * CTL_EOL.
         */
-       if (sc->sc_mibnum != -1 && (error = sysctl_destroyv(NULL, CTL_NET, PF_LINK,



Home | Main Index | Thread Index | Old Index