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, ðfoo_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, ðfoo_ca)) != 0) {
+ if ((error = config_cfattach_detach(ethfoo_cd.cd_name,
+ ðfoo_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(ðfoo_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