diff --git a/bsdmodule/Makefile.in b/bsdmodule/Makefile.in index e8bc04e..feab714 100644 --- a/bsdmodule/Makefile.in +++ b/bsdmodule/Makefile.in @@ -27,6 +27,20 @@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL@ -m 644 mkinstalldirs = @top_srcdir@/mkinstalldirs +ifeq ($(V),1) +ccompile = $(COMPILE) $(1) +cxxcompile = $(CXXCOMPILE) $(1) +else +ccompile = @/bin/echo ' ' $(2) $< && $(COMPILE) $(1) +cxxcompile = @/bin/echo ' ' $(2) $< && $(CXXCOMPILE) $(1) +endif + +quiet_cmd_cxxcompile = CXX $(quiet_modtag) $(subst $(obj)/,,$@) +cmd_cxxcompile = $(CXXCOMPILE) -c -o $@ $< + +quiet_cmd_ccompile = CC $(quiet_modtag) $(subst $(obj)/,,$@) +cmd_ccompile = $(COMPILE) -c -o $@ $< + .SUFFIXES: .SUFFIXES: .S .c .cc .o .s .ii @@ -61,19 +75,21 @@ GENERIC_OBJS = string.o straccum.o nameinfo.o \ $(EXTRA_DRIVER_OBJS) BSDMODULE_OBJS = config.o sched.o module.o \ - clickfs.o clickfs_vnops.o clickfs_tree.o clickfs_element.o + clickpfs.o module_pfs.o + +#clickfs.o clickfs_vnops.o clickfs_tree.o clickfs_element.o EXTRA_DRIVER_OBJS = @EXTRA_DRIVER_OBJS@ -OBJS = setdef0.o $(GENERIC_OBJS) $(ELEMENT_OBJS) $(BSDMODULE_OBJS) elements.o setdef1.o +OBJS = $(GENERIC_OBJS) $(ELEMENT_OBJS) $(BSDMODULE_OBJS) elements.o -CPPFLAGS = @CPPFLAGS@ -DCLICK_BSDMODULE -DBSD_NETISRSCHED -CFLAGS = @CFLAGS_NDEBUG@ -g -CXXFLAGS = @CXXFLAGS_NDEBUG@ -g +CPPFLAGS = @CPPFLAGS@ -D_KERNEL -DKLD_MODULE -DCLICK_BSDMODULE -D__MTCLICK__ -DNUM_CLICK_CPUS=2 #-DBSD_NETISRSCHED #-D__MTCLICK__ #-DBSD_NETISRSCHED +CFLAGS = @CFLAGS_NDEBUG@ -g -fno-strict-aliasing -ffreestanding -fno-builtin +CXXFLAGS = @CXXFLAGS_NDEBUG@ -g -ffreestanding -fno-builtin DEFS = @DEFS@ INCLUDES = -nostdinc -I$(top_builddir)/include -I$(top_srcdir)/include \ - -I$(srcdir) -I$(top_srcdir) -I. -I$(freebsd_includedir) + -I$(srcdir) -I$(top_srcdir) -I. -I$(freebsd_includedir) -I/usr/src/sys LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ @@ -90,7 +106,7 @@ ifneq ($(MAKECMDGOALS),clean) -include elements.mk endif -click.ko: Makefile vnode_if.h $(OBJS) +click.ko: Makefile vnode_if.h vnode_if_newproto.h vnode_if_typedef.h $(OBJS) $(LD) -Bshareable -o click.ko $(OBJS) Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @@ -108,11 +124,13 @@ elements.cc: elements.conf $(top_builddir)/click-buildtool $(top_builddir)/click-buildtool elem2export < elements.conf > elements.cc @rm -f elements.d -setdef0.c setdef1.c: module.o - gensetdefs module.o -vnode_if.h: $(freebsd_srcdir)/kern/vnode_if.pl $(freebsd_srcdir)/kern/vnode_if.src $(srcdir)/massage-vnode_if.pl - $(PERL) $(freebsd_srcdir)/kern/vnode_if.pl -h $(freebsd_srcdir)/kern/vnode_if.src - $(PERL) $(srcdir)/massage-vnode_if.pl vnode_if.h +vnode_if.h: $(freebsd_srcdir)/kern/vnode_if.src + /usr/bin/awk -f $(freebsd_srcdir)/tools/vnode_if.awk $(freebsd_srcdir)/kern/vnode_if.src -h +vnode_if_newproto.h: $(freebsd_srcdir)/kern/vnode_if.src + /usr/bin/awk -f $(freebsd_srcdir)/tools/vnode_if.awk $(freebsd_srcdir)/kern/vnode_if.src -p +vnode_if_typedef.h: $(freebsd_srcdir)/kern/vnode_if.src + /usr/bin/awk -f $(freebsd_srcdir)/tools/vnode_if.awk $(freebsd_srcdir)/kern/vnode_if.src -q + DEPFILES := $(wildcard *.d) ifneq ($(DEPFILES),) @@ -129,7 +147,7 @@ uninstall: clean: -rm -f *.d *.o click.ko elements.mk elements.cc elements.conf \ - vnode_if.h vnode_if.h~ setdef0.c setdef1.c setdefs.h + vnode_if.h vnode_if_newproto.h vnode_if_typedef.h distclean: clean -rm -f Makefile diff --git a/bsdmodule/clickfs.cc b/bsdmodule/clickfs.cc deleted file mode 100644 index a7cbe66..0000000 --- a/bsdmodule/clickfs.cc +++ /dev/null @@ -1,163 +0,0 @@ -/* - * clickfs.cc -- Click configuration filesystem for BSD - * Nickolai Zeldovich - * - * Copyright (c) 2001 Massachusetts Institute of Technology - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, subject to the conditions - * listed in the Click LICENSE file. These conditions include: you must - * preserve this copyright notice, and you cannot mention the copyright - * holders in advertising related to the Software without their permission. - * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This - * notice is a summary of the Click LICENSE file; the license in that file is - * legally binding. - */ - -#include -#include "modulepriv.hh" -#include "clickfs_tree.hh" - -#include -CLICK_CXX_PROTECT -#include -#include -#include -CLICK_CXX_UNPROTECT -#include - -#include - -extern vop_t **clickfs_root_vnops; -extern struct vnodeopv_desc clickfs_vnodeop_opv_desc; - -struct clickfs_mount { - struct vnode *click_root; -}; - -static int -clickfs_mount(struct mount *mp, char *user_path, caddr_t data, - struct nameidata *ndp, struct proc *p) -{ - char path[MAXPATHLEN]; - size_t count; - int error; - struct clickfs_mount *cmp; - - if (mp->mnt_flag & MNT_UPDATE) - return EOPNOTSUPP; - - vfs_getnewfsid(mp); - - error = copyinstr(user_path, path, MAXPATHLEN, &count); - if (error) - return error; - - MALLOC(cmp, struct clickfs_mount *, sizeof(struct clickfs_mount), - M_CLICKFS, M_WAITOK | M_ZERO); - mp->mnt_data = (qaddr_t) cmp; - - mp->mnt_stat.f_bsize = DEV_BSIZE; - mp->mnt_stat.f_iosize = DEV_BSIZE; - mp->mnt_stat.f_owner = 0; - mp->mnt_stat.f_blocks = 1; - mp->mnt_stat.f_bfree = 1; - mp->mnt_stat.f_bavail = 1; - mp->mnt_stat.f_files = 1; - mp->mnt_stat.f_ffree = 1; - mp->mnt_stat.f_flags = mp->mnt_flag; - - strcpy(mp->mnt_stat.f_mntonname, path); - strcpy(mp->mnt_stat.f_mntfromname, "clickfs"); - strcpy(mp->mnt_stat.f_fstypename, "clickfs"); - - error = clickfs_rootvnode(mp, &cmp->click_root); - if (error < 0) { - free(cmp, M_CLICKFS); - return error; - } - - return 0; -} - -static int -clickfs_start(struct mount *mp, int flags, struct proc *p) -{ - return 0; -} - -static int -clickfs_unmount(struct mount *mp, int mntflags, struct proc *p) -{ - struct clickfs_mount *cmp = (struct clickfs_mount *)mp->mnt_data; - int error; - int flags = 0; - - if (mntflags & MNT_FORCE) - flags |= FORCECLOSE; - - error = vflush(mp, 1, flags); // there is 1 extra vnode ref. - if (error) - return error; - - free(mp->mnt_data, M_CLICKFS); - mp->mnt_data = 0; - - return 0; -} - -static int -clickfs_root(struct mount *mp, struct vnode **vpp) -{ - struct clickfs_mount *cmp = (struct clickfs_mount *)mp->mnt_data; - - *vpp = cmp->click_root; - VREF(*vpp); - vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY, curproc); - - return 0; -} - -static int -clickfs_statfs(struct mount *mp, struct statfs *sbp, struct proc *p) -{ - memcpy(sbp, &mp->mnt_stat, sizeof(*sbp)); - return 0; -} - -static int -clickfs_sync(struct mount *mp, int waitfor, struct ucred *cred, - struct proc *p) -{ - return 0; -} - -static int -clickfs_init(struct vfsconf *vfsp) -{ - return 0; -} - -static int -clickfs_uninit(struct vfsconf *vfsp) -{ - return 0; -} - -struct vfsops clickfs_vfsops = { - clickfs_mount, - clickfs_start, - clickfs_unmount, - clickfs_root, - vfs_stdquotactl, - clickfs_statfs, - clickfs_sync, - vfs_stdvget, - vfs_stdfhtovp, - vfs_stdcheckexp, - vfs_stdvptofh, - clickfs_init, - clickfs_uninit, - vfs_stdextattrctl -}; diff --git a/bsdmodule/clickpfs.cc b/bsdmodule/clickpfs.cc new file mode 100644 index 0000000..e2924c0 --- /dev/null +++ b/bsdmodule/clickpfs.cc @@ -0,0 +1,753 @@ +/*- + * Copyright (c) 2006 Aniruddha Bohra + * + */ + +#include +#include "modulepriv.hh" + +#include +#include + +#include +CLICK_CXX_PROTECT +#include +#include +#include +#include +#include +#include +#include +#include +#if __FreeBSD_version >= 700000 +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +CLICK_CXX_UNPROTECT +#include + +#include +#include + +#include "clickpfs.hh" + +MALLOC_DEFINE(M_CLICKFS, "clickfs", "Click filesystem node data"); + +#if __FreeBSD_version >= 700000 +static struct rwlock _handler_lock; +#define HANDLER_LOCK_INIT() rw_init(&_handler_lock, "ClickHandlerRWLock") +#define HANDLER_LOCK_DESTROY() rw_destroy(&_handler_lock) +#define HANDLER_LOCK_READ() rw_rlock(&_handler_lock) +#define HANDLER_LOCK_WRITE() rw_wlock(&_handler_lock) +#define HANDLER_UNLOCK_READ() rw_runlock(&_handler_lock) +#define HANDLER_UNLOCK_WRITE() rw_wunlock(&_handler_lock) +#else +static struct mtx _handler_lock; +#define HANDLER_LOCK_INIT() mtx_init(&_handler_lock, \ + "ClickHandlerMtx", NULL, MTX_DEF) +#define HANDLER_LOCK_DESTROY() mtx_destroy(&_handler_lock) +#define HANDLER_LOCK() mtx_lock(&_handler_lock) +#define HANDLER_UNLOCK() mtx_unlock(&_handler_lock) +#define HANDLER_LOCK_READ() HANDLER_LOCK() +#define HANDLER_LOCK_WRITE() HANDLER_LOCK() +#define HANDLER_UNLOCK_READ() HANDLER_UNLOCK() +#define HANDLER_UNLOCK_WRITE() HANDLER_UNLOCK() +#endif +/******************** Clickfs pseudofs callbacks **********************/ + +static void +lock_threads() +{ + for(int i = 0; i < click_master->nthreads(); i++) + click_master->thread(i)->lock_tasks(); + + click_master->acquire_lock(); +} + +static void +unlock_threads(void) +{ + click_master->release_lock(); + + for(int i = click_master->nthreads() - 1; i >= 0; i--) + click_master->thread(i)->unlock_tasks(); +} + +/* + * The wrapper around the click functions that allow a handler to call into + * the click handler and return the data associated with it. + * + * XXX:locking + * + * For read handlers, use RLOCK. + * For write handlers, use WLOCK. + * + * We need to protect against multiple entries. + * XXX: Does pseudofs do it for us? + */ +static int +clickfs_fill_read_raw(struct pfs_node *pn, struct uio *uio) +{ + off_t off = 0; + off_t len = 0; + Element *e = NULL; + struct clickfs_data *data = (struct clickfs_data *)pn->pn_data; + + if(data->eindex >= 0) + e = click_router->element(data->eindex); + + const Handler *h = Router::handler(click_router, data->handle); + + if(h == NULL) { + click_chatter("Router handler %d not found", data->handle); + return ENOENT; + } + + /* + * Find the operation from the uio and call the appropriate handler. + */ + if(!h->read_visible()) + return EPERM; + + off = uio->uio_offset - data->r_offset; + if(off < 0) { + /* + * seek back. Free the old string and reload + */ + if(data->rbuf) { + delete data->rbuf; + data->rbuf = NULL; + } + data->r_offset = uio->uio_offset; + } + + /* + * Try to read + */ + if(data->rbuf == NULL) { + if(h->exclusive()) { + lock_threads(); + data->rbuf = new String(h->call_read(e)); + unlock_threads(); + } + else + data->rbuf = new String(h->call_read(e)); + } + /* + * If we ran out of memory, return + */ + if(data->rbuf == NULL) + return ENOMEM; + + if(data->rbuf->out_of_memory()) { + delete data->rbuf; + data->rbuf = NULL; + return ENOMEM; + } + + /* + * Now, move the data + */ + len = data->rbuf->length(); + if(off >= len) { + /* + * No more data. Return 0, but get rid of the string + * so future reads will refresh the data. + */ + data->r_offset += len; + len = 0; + delete data->rbuf; + } + else { + len = uiomove((char *)data->rbuf->data() + off, + len - off, uio); + } + + return len; +} + +static int +clickfs_fill_read_sbuf(struct pfs_node *pn, struct sbuf *sb) +{ + off_t off = 0; + off_t len = 0; + Element *e = NULL; + struct clickfs_data *data = (struct clickfs_data *)pn->pn_data; + + if(data->eindex >= 0) + e = click_router->element(data->eindex); + + const Handler *h = Router::handler(click_router, data->handle); + + if(h == NULL) { + click_chatter("Router handler %d not found", data->handle); + return ENOENT; + } + + /* + * Find the operation from the uio and call the appropriate handler. + */ + if(!h->read_visible()) + return EPERM; + + /* + * Try to read + */ + if(data->rbuf == NULL) { + if(h->exclusive()) { + lock_threads(); + data->rbuf = new String(h->call_read(e)); + unlock_threads(); + } + else + data->rbuf = new String(h->call_read(e)); + } + /* + * If we ran out of memory, return + */ + if(data->rbuf == NULL) + return ENOMEM; + + if(data->rbuf->out_of_memory()) { + delete data->rbuf; + data->rbuf = NULL; + return ENOMEM; + } + + /* + * Now, move the data + */ + len = data->rbuf->length(); + if(off >= len) { + /* + * No more data. Return 0, but get rid of the string + * so future reads will refresh the data. + */ + data->r_offset += len; + delete data->rbuf; + } + + int error = sbuf_bcpy(sb, data->rbuf->c_str(), len); + + return error; +} + +static int +clickfs_fill_write_raw(struct pfs_node *pn, struct uio *uio) +{ + off_t off = 0; + off_t len = 0; + Element *e = NULL; + struct clickfs_data *data = (struct clickfs_data *)pn->pn_data; + + if(data->eindex >= 0) + e = click_router->element(data->eindex); + + const Handler *h = Router::handler(click_router, data->handle); + + if(h == NULL) { + click_chatter("Router handler %d not found", data->handle); + return ENOENT; + } + + if(!h->write_visible()) + return EPERM; + + if(data->wbuf == NULL) { + data->wbuf = new String(); + if(data->wbuf == NULL) + return ENOMEM; + } + + off = uio->uio_offset - data->w_offset; + len = uio->uio_resid; + + off_t last_len = data->wbuf->length(); + off_t end_pos = off + len; + + if(end_pos > last_len) + data->wbuf->append_fill(0, end_pos - last_len); + + char *x = data->wbuf->mutable_data() + off; + if(end_pos > last_len) + bzero(x, end_pos - last_len); + + len = uiomove(x, len, uio); + + return len; +} + +static int +clickfs_fill_write_sbuf(struct pfs_node *pn, struct sbuf *sb, struct uio *uio) +{ + off_t off = 0; + off_t len = 0; + Element *e = NULL; + struct clickfs_data *data = (struct clickfs_data *)pn->pn_data; + + if(data->eindex >= 0) + e = click_router->element(data->eindex); + + const Handler *h = Router::handler(click_router, data->handle); + + if(h == NULL) { + click_chatter("Router handler %d not found", data->handle); + return ENOENT; + } + + if(!h->write_visible()) + return EPERM; + + if(data->wbuf == NULL) { + data->wbuf = new String(); + if(data->wbuf == NULL) + return ENOMEM; + } + + off = uio->uio_offset - data->w_offset; + len = uio->uio_resid; + + off_t last_len = data->wbuf->length(); + off_t end_pos = off + len; + + + char *x = data->wbuf->mutable_data() + off; + if(end_pos > last_len) + bzero(x, end_pos - last_len); + + /* + * Now copy the data to x + */ + bcopy(sbuf_data(sb), x, sbuf_len(sb)); + + return 0; +} + +static int +clickfs_fill(PFS_FILL_ARGS) +{ + int len = 0; + + switch(uio->uio_rw) + { + case UIO_READ: + if(sb != NULL) + len = clickfs_fill_read_sbuf(pn, sb); + else + len = clickfs_fill_read_raw(pn, uio); + break; + case UIO_WRITE: + if(sb != NULL) + len = clickfs_fill_write_sbuf(pn, sb, uio); + else + len = clickfs_fill_write_raw(pn, uio); + break; + default: + len = EINVAL; + break; + } + return len; +} + +/* + * The rbuf must have the link arguments. Otherwise, return "unknown" + */ +static int +clickfs_fill_link(PFS_FILL_ARGS) +{ + struct clickfs_data *data = (struct clickfs_data *)pn->pn_data; + if(data == NULL || data->rbuf == NULL) + sbuf_printf(sb, "%s", "unknown"); + else + sbuf_printf(sb, "%s", data->rbuf->c_str()); + + return 0; +} + +static int +clickfs_attr(PFS_ATTR_ARGS) +{ + struct clickfs_data *d = (struct clickfs_data *)pn->pn_data; + if(d == NULL) + return 0; + + vap->va_mode = d->perm; + + return 0; +} + + +static void +clickfs_destroy_node(PFS_DESTROY_ARGS) +{ + struct clickfs_data *data = (struct clickfs_data *)pn->pn_data; + if(data->rbuf != NULL) + delete data->rbuf; + if(data->wbuf != NULL) + delete data->wbuf; + + free(data, M_CLICKFS); +} + + +static int +clickfs_close(PFS_CLOSE_ARGS) +{ + int retval = 0; + struct clickfs_data *data = (struct clickfs_data *)pn->pn_data; + + /* + * In the write function, the string is allocated even if + * the write is of 0 bytes. + * + * Therefore, just go ahead and return success. Otherwise, + * call the appropriate write handler. + */ + if(data->wbuf == NULL && data->rbuf == NULL) + return 0; + + if(data->wbuf != NULL) { + const Handler *h = Router::handler(click_router, data->handle); + if(h == NULL) + return ENOENT; + + Element *e = NULL; + if(data->eindex >= 0) + e = click_router->element(data->eindex); + + String context_string = "In write handler `" + h->name() + "'"; + if(e != NULL) { + context_string += String(" for `") + e->declaration() + "'"; + } + + ContextErrorHandler cerrh(click_logged_errh, context_string + ":"); + + HANDLER_LOCK_WRITE(); + retval = h->call_write(*data->wbuf, e, true, &cerrh); + HANDLER_UNLOCK_WRITE(); + retval = (retval >= 0 ? 0 : -retval); + } + + /* + * Dispose off the buffers if allocated. + */ + if(data->rbuf != NULL) { + delete data->rbuf; + data->rbuf = NULL; + } + if(data->wbuf != NULL) { + data->w_offset += data->wbuf->length(); + delete data->wbuf; + data->wbuf = NULL; + } + + return retval; +} +/******************** Clickfs tree manipulation **********************/ + +struct pfs_node *clickfs_tree_root = NULL; + +/* + * Initialize the elements in the root tree. + */ +int +clickfs_tree_init(void) +{ + int error = 0; + + HANDLER_LOCK_INIT(); + + /* + * Create global handlers in clickfs root directory. + */ + Vector handlers; + handlers.clear(); + Router::element_hindexes(0, handlers); + printf("NHandlers = %d\n", handlers.size()); + for(int i = 0; i < handlers.size(); i++) + { + struct pfs_node *pn; + const Handler *h = Router::handler(NULL, handlers[i]); + + pn = clickfs_tree_add_file(clickfs_tree_root, h, 0, Router::FIRST_GLOBAL_HANDLER + i); + if(pn == NULL) + { + /*XXX: Handle the error case? */ + error = EINVAL; + break; + } + } + + return error; +} + +struct pfs_node * +clickfs_tree_add_dir(struct pfs_node *parent, char *name, int perm) +{ + char *cp; + struct pfs_node *pn = NULL;; + struct clickfs_data *d = NULL; + + /* + * If name contains the '/' character, recursively add the whole path. + * XXX: What happens with '.' and '..' in the path? + */ + if(cp = rindex(name, '/')) { + *cp = '\0'; + parent = clickfs_tree_add_dir(parent, name, perm); + *cp = '/'; + name = cp + 1; + } + + pn = pfs_create_dir(parent, (const char *)name, NULL, NULL, + clickfs_destroy_node, 0); + if(pn == NULL) + return NULL; + + d = (struct clickfs_data *)malloc(sizeof(*d), M_CLICKFS, M_NOWAIT|M_ZERO); + if(d == NULL) { + pfs_destroy(pn); + return NULL; + } + + d->perm = perm; + + pn->pn_data = d; + return pn; +} + +struct pfs_node * +clickfs_tree_add_file(struct pfs_node *parent, const Handler *h, int eindex, + int handle) +{ + int flags = 0; + int perm = 0; + String name = h->name(); + struct pfs_node *pn = NULL; + + printf("Handler Name = %s Index = %d\n", name.c_str(), handle); + + if(!h->read_visible() && !h->write_visible()) { + printf("Handler not visible\n"); + return parent; + } + + /* + * Set the permissions to be returned from clickfs_attr + */ + if(h->readable()) + perm |= S_IRUSR | S_IRGRP | S_IROTH; + if(h->writable()) + perm |= S_IWUSR | S_IWGRP; + + if(h->raw()) { + if(h->readable() && h->writable()) + flags |= PFS_RAW; + else if(h->readable()) + flags |= PFS_RAWRD; + else if(h->writable()) + flags |= PFS_RAWWR; + else { + printf("RAW Handler not readable, not writable\n"); + return NULL; + } + } + else if(h->readable() && h->writable()) + flags |= PFS_RDWR; + else if(h->writable()) + flags |= PFS_WR; + else if(h->readable()) + flags |= PFS_RD; + else { + printf("Unknown handler type\n"); + return NULL; + } + + pn = pfs_create_file(parent, name.c_str(), + clickfs_fill, clickfs_attr, NULL, + clickfs_destroy_node, flags); + if(pn == NULL) { + printf("Cannot allocate pfs node\n"); + return NULL; + } + + if(perm & (S_IWUSR | S_IWGRP)) + pn->pn_close = clickfs_close; + + /* + * Allocate data and assign pn_data. Since it is WAITOK, it should not + * return NULL. + */ + struct clickfs_data *d = (struct clickfs_data *)malloc(sizeof(*d), + M_CLICKFS, M_NOWAIT|M_ZERO); + + if(d == NULL) { + printf("Cannot allocate clickfs data\n"); + pfs_destroy(pn); + return NULL; + } + + d->perm = perm; + d->eindex = eindex; + d->handle = handle; + + pn->pn_data = d; + + return pn; +} + +struct pfs_node * +clickfs_tree_add_link(struct pfs_node *parent, const char *to, const char *from) +{ + struct clickfs_data *d; + struct pfs_node *pn; + + if(pfs_find_node(parent, to) != NULL) + return NULL; + + pn = pfs_create_link(parent, from, clickfs_fill_link, clickfs_attr, + NULL, clickfs_destroy_node, 0); + if(pn == NULL) + return NULL; + + /* + * Put the to in the rbuf string. + */ + d = (struct clickfs_data *)malloc(sizeof(*d), M_CLICKFS, + M_NOWAIT|M_ZERO); + if(d == NULL) + { + pfs_destroy(pn); + return NULL; + } + d->perm = 0777; + d->rbuf = new String(to); + + pn->pn_data = d; + + return pn; +} + +/* + * Iterate over current router's elements and create element + * directories and symlinks for them + */ +void +clickfs_init_elements(void) +{ + int nelems = click_router->nelements(); + + for(int curelem = 0; curelem < nelems; curelem++) { + char *lbp; + char *nbp; + char namebuf[PFS_NAMELEN]; + char linkbuf[PFS_NAMELEN]; + struct pfs_node *pn; + struct pfs_node *elemnode; + + const Element *e = click_router->element(curelem); + const String &id = e->id(); + + snprintf(namebuf, sizeof(namebuf), "%d", curelem + 1); + + elemnode = clickfs_tree_add_dir(clickfs_tree_root, namebuf, 0755); + + /* XXX: What if elemnode is NULL! */ + + /* Create a symlink. */ + snprintf(linkbuf, sizeof(linkbuf), "%s", id.data()); + linkbuf[id.length()] = '\0'; + + nbp = namebuf; + if(lbp = rindex(linkbuf, '/')) { + char *c = linkbuf; + while(c = index(c, '/')) { + sprintf(nbp, "../"); + nbp += 3; + c++; + } + *lbp++ = '\0'; + + pn = clickfs_tree_add_dir(clickfs_tree_root, linkbuf, + 0755); + } + else { + lbp = linkbuf; + pn = clickfs_tree_root; + } + + sprintf(nbp, "%d", curelem + 1); + clickfs_tree_add_link(pn, lbp, namebuf); + + /* Now iterate over all handlers in this router element */ + Vector handlers; + handlers.clear(); + click_router->element_hindexes(e, handlers); + + for(int h_idx = 0; h_idx < handlers.size(); h_idx++) { + struct pfs_node *pn = clickfs_tree_add_file(elemnode, + Router::handler(e, handlers[h_idx]), + curelem, handlers[h_idx]); + if(pn == NULL) { + printf("Cannot add file -- should destroy!!\n"); + return; + } + } + } +} + +void +clickfs_cleanup_elements(void) +{ + struct pfs_node *pn = clickfs_tree_root->pn_nodes; + + /* + * Iterate through the root and destroy all entries + * except global handlers + */ + while(pn) { + struct pfs_node *pnext = pn->pn_next; + switch(pn->pn_type) { + case pfstype_none: + case pfstype_root: + case pfstype_this: + case pfstype_parent: + case pfstype_file: + /* Ignore these entries */ + break; + case pfstype_dir: + case pfstype_symlink: + case pfstype_procdir: + default: + { + /* Delete these entries */ + pfs_destroy(pn); + break; + } + } + pn = pnext; + } +} + +void +clickfs_tree_cleanup() +{ + /* + * pseudofs will cleanup after us. + * All we need to do is destroy our lock. + */ + HANDLER_LOCK_DESTROY(); +} diff --git a/bsdmodule/clickpfs.hh b/bsdmodule/clickpfs.hh new file mode 100644 index 0000000..beb170b --- /dev/null +++ b/bsdmodule/clickpfs.hh @@ -0,0 +1,27 @@ +#ifndef __CLICKFS_TREE_HH__ +#define __CLICKFS_TREE_HH__ + +MALLOC_DECLARE(M_CLICKFS); + +struct clickfs_data { + int perm; + int eindex; + int handle; + String *wbuf; + off_t w_offset; + String *rbuf; + off_t r_offset; +}; + +extern struct pfs_info *clickfs_info; +extern struct pfs_node *clickfs_tree_root; + +extern int clickfs_tree_init(void); +extern void clickfs_tree_cleanup(void); +extern struct pfs_node *clickfs_tree_add_dir(struct pfs_node *, char *, int); +extern struct pfs_node *clickfs_tree_add_file(struct pfs_node *, const Handler *, + int, int); +extern struct pfs_node *clickfs_tree_add_link(struct pfs_node *, const char *, + const char *); + +#endif diff --git a/bsdmodule/config.cc b/bsdmodule/config.cc index 699afa3..b844376 100644 --- a/bsdmodule/config.cc +++ b/bsdmodule/config.cc @@ -94,7 +94,7 @@ install_router(const String &config, Router *r) click_router = r; if (click_router) { click_router->use(); - init_router_element_procs(); + clickfs_init_elements(); } *current_config = config; click_config_generation++; @@ -105,7 +105,7 @@ kill_router() { if (click_router) { int s = splimp(); - cleanup_router_element_procs(); + clickfs_cleanup_elements(); click_router->unuse(); click_router = 0; splx(s); diff --git a/bsdmodule/module.cc b/bsdmodule/module.cc index 3201790..747014d 100644 --- a/bsdmodule/module.cc +++ b/bsdmodule/module.cc @@ -18,7 +18,7 @@ #include #include "modulepriv.hh" -#include "clickfs_tree.hh" +#include "clickpfs.hh" #include #include @@ -37,8 +37,8 @@ extern "C" int click_cleanup_packages(); KernelErrorHandler *click_logged_errh = 0; static KernelErrorHandler *syslog_errh = 0; -Router *click_router = 0; -Master *click_master = 0; +Router *click_router = NULL; +Master *click_master = NULL; /***************************** Global handlers *******************************/ @@ -206,13 +206,6 @@ click_clear_error_log() /******************** Module initialization and cleanup **********************/ -extern "C" void click_ether_input(struct ifnet *, struct mbuf **, struct ether_header *); -extern "C" void (*ng_ether_input_p)(struct ifnet *, struct mbuf **, struct ether_header *); - -extern "C" void click_ether_output(struct ifnet *, struct mbuf **); -extern "C" void (*ng_ether_output_p)(struct ifnet *, struct mbuf **); - - extern "C" int init_module() { @@ -248,8 +241,6 @@ init_module() Router::add_write_handler(0, "assert_stop", write_assert_stop, 0); #endif - // filesystem interface - clickfs_tree_init(); // set modes based on 'accessible' if (click_accessible) { click_mode_r = S_IRUSR | S_IRGRP | S_IROTH; @@ -261,9 +252,8 @@ init_module() click_mode_w = S_IWUSR | S_IWGRP; click_mode_dir = S_IFDIR | click_mode_r | click_mode_x; - // netgraph hooks - ng_ether_input_p = click_ether_input; - ng_ether_output_p = click_ether_output; + // filesystem interface + clickfs_tree_init(); return 0; } @@ -275,9 +265,8 @@ cleanup_module() { extern int click_dmalloc_curnew; /* glue.cc */ - // netgraph hooks - ng_ether_input_p = 0; - ng_ether_output_p = 0; + // filesystem interface + clickfs_tree_cleanup(); // extra packages, global handlers click_cleanup_packages(); @@ -287,9 +276,6 @@ cleanup_module() click_cleanup_config(); click_cleanup_sched(); - // filesystem interface - clickfs_tree_cleanup(); - cp_va_static_cleanup(); // error handlers @@ -315,68 +301,3 @@ cleanup_module() click_dmalloc_cleanup(); } } - -static int -click_load(struct module *mod, int cmd, void *arg) -{ - int ret = ENOTSUP; - - /* Load and unload the VFS part first */ - ret = vfs_modevent(mod, cmd, arg); - if (ret) - return ret; - - switch (cmd) { - case MOD_LOAD: - printf("Click module loading\n"); - if (init_module()) { - ret = EINVAL; - break; - } - - ret = 0; - break; - - case MOD_UNLOAD: - printf("Click module unloading\n"); - cleanup_module(); - ret = 0; - break; - - case MOD_SHUTDOWN: - /* - * MOD_SHUTDOWN is usually called when the machine is - * about to shut down and the module is loaded at the - * moment. Perhaps we should call cleanup_module() at - * this point, but since we're shutting down anyway, - * it doesn't really matter.. - */ - printf("Click module shutdown\n"); - ret = 0; - break; - - default: - printf("Click: unknown module command %d\n", cmd); - ret = EINVAL; - break; - } - - return ret; -} - -static struct vfsconf click_vfsconf = { - &clickfs_vfsops, - "click", - -1, - 0, - VFCF_SYNTHETIC -}; - -static moduledata_t mod_data = { - "click", - click_load, - &click_vfsconf -}; - -DECLARE_MODULE(click, mod_data, SI_SUB_VFS, SI_ORDER_MIDDLE); -VNODEOP_SET(clickfs_vnodeop_opv_desc); diff --git a/bsdmodule/module_pfs.c b/bsdmodule/module_pfs.c new file mode 100644 index 0000000..5d290d0 --- /dev/null +++ b/bsdmodule/module_pfs.c @@ -0,0 +1,87 @@ +/*- + * + * Copyright (c) 2007 Aniruddha Bohra. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int init_module(void); +extern void cleanup_module(void); + +extern struct pfs_node *clickfs_tree_root; + +static unsigned int is_click_greedy = 0; +static unsigned int click_nthreads = 2; + +SYSCTL_NODE(_net, OID_AUTO, click, CTLFLAG_RW, 0, + "Kernel Click parameters"); + +SYSCTL_UINT(_net_click, OID_AUTO, greedy, CTLFLAG_RDTUN, &is_click_greedy, 0, + "Click takes up all CPU"); + +SYSCTL_UINT(_net_click, OID_AUTO, nthreads, CTLFLAG_RDTUN, &click_nthreads, 1, + "Number of click threads"); + +int +click_threads(void) +{ + return click_nthreads; +} + +int +click_greedy(void) +{ + return is_click_greedy; +} + +/* + * Constructor + */ +static int +clickfs_init(PFS_INIT_ARGS) +{ + int error = 0; + + + clickfs_tree_root = pi->pi_root; + + error = init_module(); + if(error != 0) + clickfs_tree_root = NULL; + else + printf("CLICKFS Initialized\n"); + return error; +} + + +/* + * Destructor + */ +static int +clickfs_uninit(PFS_INIT_ARGS) +{ + printf("Uninitialize\n"); + cleanup_module(); + return 0; +} + +PSEUDOFS(clickfs, 1); diff --git a/bsdmodule/modulepriv.hh b/bsdmodule/modulepriv.hh index eef8677..215be40 100644 --- a/bsdmodule/modulepriv.hh +++ b/bsdmodule/modulepriv.hh @@ -12,8 +12,10 @@ CLICK_CXX_PROTECT #include #include #include +#if 0 #include #include +#endif CLICK_CXX_UNPROTECT #include @@ -59,6 +61,10 @@ int click_kill_router_threads(); void click_init_sched(ErrorHandler *); int click_cleanup_sched(); +void clickfs_init_elements(void); +void clickfs_cleanup_elements(void); + +#if 0 void init_router_element_procs(); void cleanup_router_element_procs(); @@ -66,5 +72,6 @@ extern struct vfsops clickfs_vfsops; extern struct vnodeopv_desc clickfs_vnodeop_opv_desc; int clickfs_rootvnode(struct mount *, struct vnode **); +#endif #endif diff --git a/bsdmodule/sched.cc b/bsdmodule/sched.cc index aaca6c0..86484a6 100644 --- a/bsdmodule/sched.cc +++ b/bsdmodule/sched.cc @@ -17,7 +17,6 @@ * notice is a summary of the Click LICENSE file; the license in that file is * legally binding. */ - #define CLICK_SCHED_DEBUG #include #include "modulepriv.hh" @@ -34,15 +33,40 @@ #include CLICK_CXX_PROTECT +#include +#include +#include +#include +#include +#include #include #include #include +#include +#include + +#include +#include +#include +#include +#include +#include +#include #include CLICK_CXX_UNPROTECT #include static Router *placeholder_router; +static struct mtx click_thread_lock; + +#define CLICK_THR_LOCK() mtx_lock(&click_thread_lock) +#define CLICK_THR_UNLOCK() mtx_unlock(&click_thread_lock) +#define _CLICK_THR_LOCK_INIT(a) mtx_init(&click_thread_lock, a, NULL, MTX_DEF) +#define CLICK_THR_LOCK_INIT() _CLICK_THR_LOCK_INIT("ClickThrLock") +#define CLICK_THR_LOCK_DESTROY() mtx_destroy(&click_thread_lock) + + #ifdef BSD_NETISRSCHED struct ifnet click_dummyifnet; struct callout_handle click_timer_h; @@ -58,56 +82,83 @@ static unsigned min_click_frac = 5, max_click_frac = 800; static void click_sched(void *thunk) { - curproc->p_nice = click_thread_priority; - RouterThread *rt = (RouterThread *)thunk; + curproc->p_nice = click_thread_priority; + RouterThread *rt = (RouterThread *)thunk; + #ifdef HAVE_ADAPTIVE_SCHEDULER - rt->set_cpu_share(min_click_frac, max_click_frac); + rt->set_cpu_share(min_click_frac, max_click_frac); #endif - // add pid to thread list - if (click_thread_pids) - click_thread_pids->push_back(curproc->p_pid); - - // driver loop; does not return for a while - rt->driver(); - - // release master (preserved in click_init_sched) - click_master->unuse(); + /* add pid to thread list */ + CLICK_THR_LOCK(); + if (click_thread_pids) + click_thread_pids->push_back(curproc->p_pid); + CLICK_THR_UNLOCK(); + + /* Migrate and bind thread to the appropriate CPU */ + int targetcpu = rt->cpuid(); + int cpu_id = PCPU_GET(cpuid); + if(cpu_id != targetcpu) { + mtx_lock_spin(&sched_lock); + sched_bind(curthread, targetcpu); + mtx_unlock_spin(&sched_lock); + } - // remove pid from thread list - if (click_thread_pids) - for (int i = 0; i < click_thread_pids->size(); i++) { - if ((*click_thread_pids)[i] == curproc->p_pid) { - (*click_thread_pids)[i] = click_thread_pids->back(); - click_thread_pids->pop_back(); - break; - } - } - kthread_exit(0); + click_chatter("Bound thread to CPU %d", targetcpu); + + /* driver loop; does not return for a while */ + rt->driver(); + + /* release master (preserved in click_init_sched) */ + click_master->unuse(); + + /* remove pid from thread list */ + CLICK_THR_LOCK(); + if (click_thread_pids) { + for (int i = 0; i < click_thread_pids->size(); i++) { + if ((*click_thread_pids)[i] == curproc->p_pid) { + (*click_thread_pids)[i] = click_thread_pids->back(); + click_thread_pids->pop_back(); + break; + } + } + } + CLICK_THR_UNLOCK(); + click_chatter("click: stopping router thread pid %d\n", curproc->p_pid); + kthread_exit(0); } static int kill_router_threads() { - if (click_router) - click_router->set_runcount(Router::STOP_RUNCOUNT); - delete placeholder_router; - - // wait up to 5 seconds for routers to exit - unsigned long out_ticks = ticks + 5 * hz; - int num_threads; - do { - num_threads = click_thread_pids->size(); - - if (num_threads > 0) - tsleep(curproc, PPAUSE, "unload", 1); - } while (num_threads > 0 && ticks < out_ticks); - - if (num_threads > 0) { - printf("click: current router threads refuse to die!\n"); - return -1; - } else - return 0; + int error = 0; + + if (click_router) + click_router->set_runcount(Router::STOP_RUNCOUNT); + + delete placeholder_router; + + /* wait up to 5 seconds for routers to exit */ + unsigned long out_ticks = ticks + 5 * hz; + int num_threads; + /* + * XXX: bohra + * Use cv_init(9) + */ + do { + CLICK_THR_LOCK(); + num_threads = click_thread_pids->size(); + CLICK_THR_UNLOCK(); + if (num_threads > 0) + tsleep(curproc, PPAUSE, "unload", 1); + } while (num_threads > 0 && ticks < out_ticks); + + if (num_threads > 0) { + printf("click: current router threads refuse to die!\n"); + error = -1; + } + + return error; } #endif //BSD_NETISRSCHED @@ -119,13 +170,17 @@ kill_router_threads() static String read_threads(Element *, void *) { - StringAccum sa; - simple_lock(&click_thread_lock); - if (click_thread_pids) - for (int i = 0; i < click_thread_pids->size(); i++) - sa << (*click_thread_pids)[i] << '\n'; - simple_unlock(&click_thread_lock); - return sa.take_string(); + StringAccum sa; + + CLICK_THR_LOCK(); + + if (click_thread_pids) + for (int i = 0; i < click_thread_pids->size(); i++) + sa << (*click_thread_pids)[i] << '\n'; + + CLICK_THR_UNLOCK(); + + return sa.take_string(); } static String @@ -137,27 +192,35 @@ read_priority(Element *, void *) static int write_priority(const String &conf, Element *, void *, ErrorHandler *errh) { - int priority; - if (!cp_integer(cp_uncomment(conf), &priority)) - return errh->error("priority must be an integer"); - - if (priority > PRIO_MAX) - priority = PRIO_MAX; - if (priority < PRIO_MIN) - priority = PRIO_MIN; - - // change current thread priorities - click_thread_priority = priority; - if (click_thread_pids) - for (int i = 0; i < click_thread_pids->size(); i++) { - struct proc *proc = pfind((*click_thread_pids)[i]); - if (proc) { - proc->p_nice = priority; - resetpriority(proc); - } - } - - return 0; + int priority; + if (!cp_integer(cp_uncomment(conf), &priority)) + return errh->error("priority must be an integer"); + + if (priority > PRIO_MAX) + priority = PRIO_MAX; + if (priority < PRIO_MIN) + priority = PRIO_MIN; + + /* + * XXX: bohra + * This does not perform all tests in the setpriority() system call. + * Should we change this? + */ + CLICK_THR_LOCK(); + click_thread_priority = priority; + if (click_thread_pids) { + for (int i = 0; i < click_thread_pids->size(); i++) { + struct proc *proc = pfind((*click_thread_pids)[i]); + if (proc) { + mtx_lock_spin(&sched_lock); + sched_nice(proc, priority); + mtx_unlock_spin(&sched_lock); + } + } + } + + CLICK_THR_UNLOCK(); + return 0; } @@ -165,7 +228,7 @@ write_priority(const String &conf, Element *, void *, ErrorHandler *errh) static String read_master_info(Element *, void *) { - return click_master->info(); + return click_master->info(); } #endif @@ -175,38 +238,40 @@ read_master_info(Element *, void *) static String read_cpu_share(Element *, void *thunk) { - int val = (thunk ? max_click_frac : min_click_frac); - return cp_unparse_real10(val, 3) + "\n"; + int val = (thunk ? max_click_frac : min_click_frac); + return cp_unparse_real10(val, 3) + "\n"; } static String read_cur_cpu_share(Element *, void *) { - if (click_router) { - String s; - for (int i = 0; i < click_master->nthreads(); i++) - s += cp_unparse_real10(click_master->thread(i)->cur_cpu_share(), 3) + "\n"; - return s; - } else - return "0\n"; + if (click_router) { + String s; + for (int i = 0; i < click_master->nthreads(); i++) + s += cp_unparse_real10(click_master->thread(i)->cur_cpu_share(), 3) + "\n"; + return s; + } + + return "0\n"; } static int write_cpu_share(const String &conf, Element *, void *thunk, ErrorHandler *errh) { - const char *name = (thunk ? "max_" : "min_"); - - int32_t frac; - if (!cp_real10(cp_uncomment(conf), 3, &frac) || frac < 1 || frac > 999) - return errh->error("%scpu_share must be a real number between 0.001 and 0.999", name); - - (thunk ? max_click_frac : min_click_frac) = frac; - - // change current thread priorities - for (int i = 0; i < click_master->nthreads(); i++) - click_master->thread(i)->set_cpu_share(min_click_frac, max_click_frac); - - return 0; + const char *name = (thunk ? "max_" : "min_"); + + int32_t frac; + if (!cp_real10(cp_uncomment(conf), 3, &frac) || frac < 1 || frac > 999) + return errh->error("%scpu_share must be a real number\ + between 0.001 and 0.999", name); + + (thunk ? max_click_frac : min_click_frac) = frac; + + /* change current thread priorities */ + for (int i = 0; i < click_master->nthreads(); i++) + click_master->thread(i)->set_cpu_share(min_click_frac, max_click_frac); + + return 0; } #endif @@ -294,34 +359,35 @@ write_sched_param(const String &conf, Element *e, void *thunk, ErrorHandler *err extern "C" int click_threads(); #endif +extern "C" int click_greedy(); + #ifdef BSD_NETISRSCHED static void click_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) { - schednetisr(NETISR_CLICK); + schednetisr(NETISR_CLICK); } static void click_timer(void *arg) { - if (polling && *polling) - ether_poll_register(click_poll, &click_dummyifnet); - else - schednetisr(NETISR_CLICK); - click_timer_h = timeout(click_timer, NULL, 1); +#ifdef DEVICE_POLLING + if (polling && *polling) + ether_poll_register(click_poll, &click_dummyifnet); + else + schednetisr(NETISR_CLICK); +#endif + click_timer_h = timeout(click_timer, NULL, 1); } void click_netisr(void) { - int s = splimp(); - RouterThread *rt = click_master->thread(0); - - rt->driver(); - splx(s); + RouterThread *rt = click_master->thread(0); + rt->driver(); } #endif //BSD_NETISRSCHED @@ -330,92 +396,123 @@ click_netisr(void) void click_init_sched(ErrorHandler *errh) { + CLICK_THR_LOCK_INIT(); + bool greedy = click_greedy(); + #ifndef BSD_NETISRSCHED - click_thread_pids = new Vector; + click_thread_pids = new Vector; #endif #if __MTCLICK__ - click_master = new Master(click_threads()); - if (smp_num_cpus != NUM_CLICK_CPUS) - click_chatter("warning: click compiled for %d cpus, machine allows %d", - NUM_CLICK_CPUS, smp_num_cpus); + click_chatter("Creating master\n"); + click_master = new Master(click_threads()); + if (mp_ncpus != click_threads()) + click_chatter("warning: click compiled for %d cpus, machine\ + allows %d", click_threads(), mp_ncpus); #else - click_master = new Master(1); + click_master = new Master(1); #endif + /* + * Add one reference to the master as ourselves. + * Remove this reference when cleaning up. + */ + click_master->use(); + #ifdef BSD_NETISRSCHED - register_netisr(NETISR_CLICK, click_netisr); - schednetisr(NETISR_CLICK); - click_timer_h = timeout(click_timer, NULL, 1); - click_dummyifnet.if_flags |= IFF_UP|IFF_RUNNING; + netisr_register(NETISR_CLICK, click_netisr, NULL, 0); + schednetisr(NETISR_CLICK); + click_timer_h = timeout(click_timer, NULL, 1); + click_dummyifnet.if_flags |= IFF_UP|IFF_DRV_RUNNING; #endif - placeholder_router = new Router("", click_master); - placeholder_router->initialize(errh); - placeholder_router->activate(errh); + click_chatter("Creating placeholder router"); + + placeholder_router = new Router("", click_master); + placeholder_router->initialize(errh); + placeholder_router->activate(errh); #ifndef BSD_NETISRSCHED - for (int i = 0; i < click_master->nthreads(); i++) { - struct proc *p; - click_master->use(); - int error = kthread_create - (click_sched, click_master->thread(i), &p, "kclick"); - if (error < 0) { - errh->error("cannot create kernel thread for Click thread %i!", i); - click_master->unuse(); - } - } + for (int i = 0; i < click_master->nthreads(); i++) { + struct proc *p; + click_master->use(); + RouterThread *rt = click_master->thread(i); + rt->set_greedy(greedy); + /* + * XXX: Are the CPUs listed from 0 to n ? + */ + rt->set_cpuid(i % mp_ncpus); + + int error = kthread_create(click_sched, (void *)rt, &p, 0, 0, + "kclick%d", i); + if (error < 0) { + errh->error("cannot create kernel thread for Click thread %i!", i); + click_master->unuse(); + } + } - Router::add_read_handler(0, "threads", read_threads, 0); - Router::add_read_handler(0, "priority", read_priority, 0); - Router::add_write_handler(0, "priority", write_priority, 0); + Router::add_read_handler(0, "threads", read_threads, 0); + Router::add_read_handler(0, "priority", read_priority, 0); + Router::add_write_handler(0, "priority", write_priority, 0); #endif //BSD_NETISRSCHED + #ifdef HAVE_ADAPTIVE_SCHEDULER - static_assert(Task::MAX_UTILIZATION == 1000); - Router::add_read_handler(0, "min_cpu_share", read_cpu_share, 0); - Router::add_write_handler(0, "min_cpu_share", write_cpu_share, 0); - Router::add_read_handler(0, "max_cpu_share", read_cpu_share, (void *)1); - Router::add_write_handler(0, "max_cpu_share", write_cpu_share, (void *)1); - Router::add_read_handler(0, "cpu_share", read_cur_cpu_share, 0); -#else - Router::add_read_handler(0, "tasks_per_iter", read_sched_param, - (void *)H_TASKS_PER_ITER); - Router::add_read_handler(0, "iters_per_timers", read_sched_param, - (void *)H_ITERS_PER_TIMERS); - Router::add_read_handler(0, "iters_per_os", read_sched_param, - (void *)H_ITERS_PER_OS); - - Router::add_write_handler(0, "tasks_per_iter", write_sched_param, - (void *)H_TASKS_PER_ITER); - Router::add_write_handler(0, "iters_per_timers", write_sched_param, - (void *)H_ITERS_PER_TIMERS); - Router::add_write_handler(0, "iters_per_os", write_sched_param, - (void *)H_ITERS_PER_OS); + static_assert(Task::MAX_UTILIZATION == 1000); + Router::add_read_handler(0, "min_cpu_share", read_cpu_share, 0); + Router::add_write_handler(0, "min_cpu_share", write_cpu_share, 0); + Router::add_read_handler(0, "max_cpu_share", read_cpu_share, (void *)1); + Router::add_write_handler(0, "max_cpu_share", write_cpu_share, (void *)1); + Router::add_read_handler(0, "cpu_share", read_cur_cpu_share, 0); +#else + Router::add_read_handler(0, "tasks_per_iter", read_sched_param, + (void *)H_TASKS_PER_ITER); + Router::add_read_handler(0, "iters_per_timers", read_sched_param, + (void *)H_ITERS_PER_TIMERS); + Router::add_read_handler(0, "iters_per_os", read_sched_param, + (void *)H_ITERS_PER_OS); + + Router::add_write_handler(0, "tasks_per_iter", write_sched_param, + (void *)H_TASKS_PER_ITER); + Router::add_write_handler(0, "iters_per_timers", write_sched_param, + (void *)H_ITERS_PER_TIMERS); + Router::add_write_handler(0, "iters_per_os", write_sched_param, + (void *)H_ITERS_PER_OS); #endif #if CLICK_DEBUG_MASTER - Router::add_read_handler(0, "master_info", read_master_info, 0); + Router::add_read_handler(0, "master_info", read_master_info, 0); #endif } int click_cleanup_sched() { + int error = 0; + #ifdef BSD_NETISRSCHED - untimeout(click_timer, NULL, click_timer_h); - unregister_netisr(NETISR_CLICK); - ether_poll_deregister(&click_dummyifnet); - delete placeholder_router; + untimeout(click_timer, NULL, click_timer_h); + netisr_unregister(NETISR_CLICK); +#ifdef DEVICE_POLLING + ether_poll_deregister(&click_dummyifnet); +#endif + delete placeholder_router; #else - if (kill_router_threads() < 0) { - printf("click: Following threads still active, expect a crash:\n"); - for (int i = 0; i < click_thread_pids->size(); i++) - printf("click: router thread pid %d\n", (*click_thread_pids)[i]); - return -1; - } else { - delete click_thread_pids; - click_thread_pids = 0; - return 0; - } + if (kill_router_threads() < 0) { + printf("click: Following threads still active, expect a crash:\n"); + CLICK_THR_LOCK(); + for (int i = 0; i < click_thread_pids->size(); i++) + printf("click: router thread pid %d\n", (*click_thread_pids)[i]); + CLICK_THR_UNLOCK(); + click_master->unuse(); + error = -1; + } + else { + delete click_thread_pids; + click_thread_pids = NULL; + click_master->unuse(); + click_master = NULL; + } #endif + CLICK_THR_LOCK_DESTROY(); + return error; } diff --git a/configure b/configure index 4a2a911..4306239 100755 --- a/configure +++ b/configure @@ -4449,14 +4449,15 @@ are relative. You must supply absolute paths starting with /. =========================================" >&2;} { (exit 1); exit 1; }; } -elif test -r $freebsd_includedir/net/if_var.h -a -r $freebsd_srcdir/kern/vnode_if.pl; then +elif test -r $freebsd_includedir/net/if_var.h -a -r $freebsd_srcdir/kern/vnode_if.src; then ac_have_bsd_kernel=y + KERNEL_CXX="$KERNEL_CXX -fpermissive" else { { echo "$as_me:$LINENO: error: ========================================= Can't find $freebsd_includedir/net/if_var.h and/or -$freebsd_srcdir/kern/vnode_if.pl. Are you sure $freebsd_srcdir +$freebsd_srcdir/kern/vnode_if.src. Are you sure $freebsd_srcdir and $freebsd_includedir contain FreeBSD kernel source? =========================================" >&5 @@ -4464,7 +4465,7 @@ echo "$as_me: error: ========================================= Can't find $freebsd_includedir/net/if_var.h and/or -$freebsd_srcdir/kern/vnode_if.pl. Are you sure $freebsd_srcdir +$freebsd_srcdir/kern/vnode_if.src. Are you sure $freebsd_srcdir and $freebsd_includedir contain FreeBSD kernel source? =========================================" >&2;} @@ -12279,7 +12280,7 @@ cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ #include -#if __FreeBSD_version < 440002 || __FreeBSD_version >= 500000 +#if __FreeBSD_version < 500000 #include #endif _ACEOF @@ -12330,13 +12331,13 @@ _ACEOF { echo "$as_me:$LINENO: WARNING: ========================================= -Your version of FreeBSD is old. Click works with FreeBSD 4.5 and later. +Your version of FreeBSD is old. Click works with FreeBSD 5.x and later. =========================================" >&5 echo "$as_me: WARNING: ========================================= -Your version of FreeBSD is old. Click works with FreeBSD 4.5 and later. +Your version of FreeBSD is old. Click works with FreeBSD 5.x and later. =========================================" >&2;} fi diff --git a/configure.in b/configure.in index 8431443..5940a5d 100644 --- a/configure.in +++ b/configure.in @@ -208,14 +208,14 @@ The --with-freebsd directories $freebsd_srcdir,$freebsd_includedir are relative. You must supply absolute paths starting with /. =========================================]) -elif test -r $freebsd_includedir/net/if_var.h -a -r $freebsd_srcdir/kern/vnode_if.pl; then +elif test -r $freebsd_includedir/net/if_var.h -a -r $freebsd_srcdir/kern/vnode_if.src; then ac_have_bsd_kernel=y else AC_MSG_ERROR([ ========================================= Can't find $freebsd_includedir/net/if_var.h and/or -$freebsd_srcdir/kern/vnode_if.pl. Are you sure $freebsd_srcdir +$freebsd_srcdir/kern/vnode_if.src. Are you sure $freebsd_srcdir and $freebsd_includedir contain FreeBSD kernel source? =========================================]) @@ -545,11 +545,12 @@ dnl stuff in the bsd kernel dnl if test $ac_have_bsd_kernel = y; then + KERNEL_CXX="$KERNEL_CXX -fpermissive" AC_CACHE_CHECK(FreeBSD version, ac_cv_freebsd_version, [ save_flags="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I$freebsd_includedir" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include -#if __FreeBSD_version < 440002 || __FreeBSD_version >= 500000 +#if __FreeBSD_version < 500000 #include #endif]])], ac_cv_freebsd_version=yes, ac_cv_freebsd_version=no) CPPFLAGS="$save_flags"]) @@ -560,7 +561,7 @@ if test $ac_have_bsd_kernel = y; then AC_MSG_WARN([ ========================================= -Your version of FreeBSD is old. Click works with FreeBSD 4.5 and later. +Your version of FreeBSD is old. Click works with FreeBSD 5.x and later. =========================================]) fi diff --git a/elements/analysis/fromdagdump.cc b/elements/analysis/fromdagdump.cc index 617c223..0eec2e0 100644 --- a/elements/analysis/fromdagdump.cc +++ b/elements/analysis/fromdagdump.cc @@ -446,7 +446,7 @@ FromDAGDump::write_handler(const String &s_in, Element *e, void *thunk, ErrorHan return 0; case H_EXTEND_INTERVAL: { Timestamp tv; - if (cp_time(s, &tv)) { + if (cp_timeval(s, &tv)) { fd->_last_time += tv; if (fd->_end_h) fd->_have_last_time = true, fd->set_active(true); diff --git a/elements/analysis/fromnlanrdump.cc b/elements/analysis/fromnlanrdump.cc index 9e163e5..e956cde 100644 --- a/elements/analysis/fromnlanrdump.cc +++ b/elements/analysis/fromnlanrdump.cc @@ -383,7 +383,7 @@ FromNLANRDump::write_handler(const String &s_in, Element *e, void *thunk, ErrorH return 0; case H_EXTEND_INTERVAL: { Timestamp tv; - if (cp_time(s, &tv)) { + if (cp_timeval(s, &tv)) { fd->_last_time += tv; if (fd->_end_h) fd->_have_last_time = true, fd->set_active(true); diff --git a/elements/analysis/fromtcpdump.cc b/elements/analysis/fromtcpdump.cc index 811c113..d59f55e 100644 --- a/elements/analysis/fromtcpdump.cc +++ b/elements/analysis/fromtcpdump.cc @@ -393,7 +393,7 @@ FromTcpdump::read_packet(ErrorHandler *errh) // first, read timestamp const char *s2 = find(s, end, ' '); - if (!cp_time(line.substring(s, s2), &q->timestamp_anno())) + if (!cp_timeval(line.substring(s, s2), &q->timestamp_anno())) break; s = s2 + 1; diff --git a/elements/analysis/timefilter.cc b/elements/analysis/timefilter.cc index c3aae11..4856c45 100644 --- a/elements/analysis/timefilter.cc +++ b/elements/analysis/timefilter.cc @@ -152,7 +152,7 @@ TimeFilter::write_handler(const String &s_in, Element *e, void *thunk, ErrorHand switch ((intptr_t)thunk) { case H_EXTEND_INTERVAL: { Timestamp t; - if (cp_time(s, &t)) { + if (cp_timeval(s, &t)) { tf->_last += t; if (tf->_last_h) tf->_last_h_ready = true; diff --git a/elements/bsdmodule/anydevice.cc b/elements/bsdmodule/anydevice.cc index 56f043c..fe6b5f7 100644 --- a/elements/bsdmodule/anydevice.cc +++ b/elements/bsdmodule/anydevice.cc @@ -25,160 +25,332 @@ #include CLICK_CXX_PROTECT #include +#include CLICK_CXX_UNPROTECT #include +/* + * Usage model which will lead to the locking strategy. + * + * i) AnyDeviceMap is read/write, with very few writers. + * ii) AnyDevice calls into the if interface. + * + * Since the calls to these routines are not from the user side, we may need + * to use NET_(UN)LOCK_GIANT(). If we are going to do that at a higher layer, + * we must use NET_ASSERT_GIANT(). + * + */ + +struct ifnet * +etheraddr_to_dev(const String &name, Element *elem) +{ + unsigned char en[6]; + + if(!cp_ethernet_address(name, en, elem)) + return NULL; + + NET_ASSERT_GIANT(); + + struct ifnet *ifp = NULL; + struct ifnet *ifpret = NULL; + struct sockaddr *sdl = NULL; + + IFNET_RLOCK(); + TAILQ_FOREACH(ifp, &ifnet, if_link) { + if(ifp->if_type == IFT_ETHER || ifp->if_type == IFT_L2VLAN) { + struct ifaddr *ifa = ifaddr_byindex(ifp->if_index); + if((sdl = ifa->ifa_addr) != NULL) { + if(memcmp(sdl->sa_data, en, 6) == 0) { + ifpret = ifp; + break; + } + } + } + } + IFNET_RUNLOCK(); + + + return ifpret; +} + AnyDevice::AnyDevice() - : _dev(0), _task(this), _idles(0), _next(0) + : _dev(NULL), _promisc(false), _in_map(false), _next(NULL) { } AnyDevice::~AnyDevice() { + if(_in_map || _dev) + click_chatter("%s: bad device destructor!", name().c_str()); } -static void -lock_kernel() +int +AnyDevice::find_device(bool allow_nonexistent, AnyDeviceMap *adm, + ErrorHandler *errh) { - // XXX not yet in BSD + NET_LOCK_GIANT(); + _dev = ifunit(_devname.c_str()); + + if(_dev == NULL) + _dev = etheraddr_to_dev(_devname, this); + + if(_dev == NULL) { + if(!allow_nonexistent) { + NET_UNLOCK_GIANT(); + return errh->error("unknown device '%s'", + _devname.c_str()); + } + else + errh->warning("unknown device '%s'", _devname.c_str()); + } + + if(_dev != NULL && (_dev->if_flags & IFF_UP)) { + errh->warning("device '%s' is down", _devname.c_str()); + _dev = NULL; + } + + if(_dev != NULL) { + IFF_LOCKGIANT(_dev); + if(_promisc) + ifpromisc(_dev, 1); + IFF_UNLOCKGIANT(_dev); + } + + if(adm != NULL) + adm->insert(this); + NET_UNLOCK_GIANT(); + + return 0; } -static void -unlock_kernel() +void +AnyDevice::set_device(struct ifnet *dev, AnyDeviceMap *adm) { - // XXX not yet in BSD -} + /* Changing to the same device is a nop. */ + if(_dev == dev) + return; -int -AnyDevice::find_device(bool allow_nonexistent, AnyDeviceMap *adm, - ErrorHandler *errh) -{ - _dev = ifunit((char *) _devname.c_str()); - if (!_dev) - _dev = find_device_by_ether_address(_devname, this); - if (!_dev) { - if (!allow_nonexistent) - return errh->error("unknown device `%s'", _devname.c_str()); - else - errh->warning("unknown device `%s'", _devname.c_str()); - } - if (_dev && !(_dev->if_flags & IFF_UP)) { - errh->warning("device `%s' is down", _devname.c_str()); - _dev = 0; - } + /* + * We are changing the device state of the current + * _dev such that we do not handle it anymore. + * + * At the same time, we take ownership of the new dev. + */ + if(_dev != NULL) + click_chatter("%s: device '%s' went down", + declaration().c_str(), _devname.c_str()); + + if(dev != NULL) + click_chatter("%s: device '%s' came up", + declaration().c_str(), _devname.c_str()); + + if(_dev != NULL && _promisc != 0) + ifpromisc(_dev, 0); + + if(adm != NULL && _in_map) + adm->remove(this); - return 0; + _dev = dev; + + if(adm != NULL) + adm->insert(this); + + if(_dev != NULL && _promisc) + ifpromisc(_dev, 1); } void AnyDevice::clear_device(AnyDeviceMap *adm) { -#if 0 /* MARKO XXX */ - if (_dev && _promisc) - dev_set_promiscuity(_dev, -1); -#endif + NET_ASSERT_GIANT(); + + if (_dev && _promisc) + ifpromisc(_dev, 0); - if (adm) - adm->remove(this); - _dev = 0; + if (adm) + adm->remove(this); + + _dev = NULL; } AnyTaskDevice::AnyTaskDevice() - : _task(this), _idles(0) + : _task(this), _idles(0) { } void -AnyDeviceMap::initialize() +AnyDeviceMap::initialize(const char *id, const char *cl) { - _unknown_map = 0; - for (int i = 0; i < MAP_SIZE; i++) - _map[i] = 0; + lock_init(id, cl, MTX_DEF); + _unknown_map = NULL; + for(int i = 0; i < MAP_SIZE; i++) + _map[i] = NULL; + } void AnyDeviceMap::insert(AnyDevice *d) { - // lock whole kernel when manipulating device map - lock_kernel(); - - int ifi = d->ifindex(); - AnyDevice **head; - if (ifi < 0) - head = &_unknown_map; - else - head = &_map[ifi % MAP_SIZE]; - // put new devices first on the list - d->set_next(*head); - *head = d; + AnyDevice **head; + + lock_acquire(); - unlock_kernel(); + int ifi = d->ifindex(); + if(ifi < 0) + head = &_unknown_map; + else + head = &_map[ifi % MAP_SIZE]; + + /* Put new devices last on the list */ + AnyDevice *trav = *head; + while(trav) { + head = &trav->_next; + trav = *head; + } + + d->_next = NULL; + *head = d; + + d->_in_map = true; + + lock_release(); +} + + +void +AnyDeviceMap::move_to_front(AnyDevice *d) +{ + AnyDevice **pprev; + + lock_acquire(); + int ifi = d->ifindex(); + if(ifi < 0) + pprev = &_unknown_map; + else + pprev = &_map[ifi % MAP_SIZE]; + + AnyDevice **head = pprev; + AnyDevice *trav = *head; + + while(trav != NULL && trav != d) { + pprev = &trav->_next; + trav = *pprev; + } + if(trav != NULL) + *pprev = d->_next; + + d->_next = *head; + *head = d; + + d->_in_map = true; + + lock_release(); } void AnyDeviceMap::remove(AnyDevice *d) { - lock_kernel(); - - int ifi = d->ifindex(); - AnyDevice **head = (ifi >= 0 ? &_map[ifi % MAP_SIZE] : &_unknown_map); - AnyDevice *prev = 0; - AnyDevice *trav = *head; - while (trav && trav != d) { - prev = trav; - trav = trav->next(); - } - if (trav) { - if (prev) - prev->set_next(trav->next()); + AnyDevice **head; + + lock_acquire(); + int ifi = d->ifindex(); + if(ifi < 0) + head = &_unknown_map; else - *head = trav->next(); - } + head = &_map[ifi % MAP_SIZE]; - unlock_kernel(); + /* Put new devices last on the list */ + AnyDevice *trav = *head; + while(trav != NULL && trav != d) { + head = &trav->_next; + trav = *head; + } + + if(trav != NULL) + *head = d->_next; + + d->_in_map = false; + + lock_release(); +} + +static void +lock_kernel() +{ + // XXX not yet in BSD +} + +static void +unlock_kernel() +{ + // XXX not yet in BSD } + AnyDevice * -AnyDeviceMap::lookup_unknown(struct ifnet *dev) +AnyDeviceMap::lookup_unknown(struct ifnet *dev, AnyDevice *last) { - // make sure device is valid - if (dev == NULL) - return NULL; + /* make sure device is valid */ + if (dev == NULL) + return NULL; - // look first by device names - String dev_name = dev->if_name; - for (AnyDevice *d = _unknown_map; d; d = d->next()) - if (d->devname() == dev_name) - return d; + NET_ASSERT_GIANT(); -#if 0 /* XXX this is slightly more complicated in BSD */ - // then by Ethernet addresses - if (dev->if_type == IFT_ETHER) { - unsigned char en[6]; - for (AnyDevice *d = _unknown_map; d; d = d->next()) - if (cp_ethernet_address(d->devname(), en, d)) - if (memcmp(en, dev->dev_addr, 6) == 0) - return d; - } -#endif + unsigned char en[6]; + String dev_name = dev->if_xname; + AnyDevice *d = NULL; + + lock_acquire(); + + if(last != NULL) + d = last->_next; + else + d = _unknown_map; + + for(; d != NULL; d = d->_next) { + + if(d->devname() == dev_name) { + lock_release(); + return d; + } + + /* Get the ethernet address of the device */ + if(cp_ethernet_address(d->devname(), en, d) != 0) { - return 0; + struct sockaddr *sdl; + struct ifaddr *ifa = ifaddr_byindex(dev->if_index); + + if((sdl = ifa->ifa_addr) != NULL) + if(memcmp(sdl->sa_data, en, 6) == 0) { + lock_release(); + return d; + } + } + } + + lock_release(); + + return NULL; } +void +AnyDeviceMap::lookup_all(struct ifnet *dev, bool known, Vector &v) +{ + if(known) + for(AnyDevice *d = NULL; d = lookup(dev, d); v.push_back(d)) + /* Do nothing */; + else + for(AnyDevice *d = NULL; d = lookup_unknown(dev, d); + v.push_back(d)) + /* Do nothing */; + +} -struct ifnet * -find_device_by_ether_address(const String &name, Element *context) -{ -#if 0 /* XXX slightly more difficult in BSD */ - unsigned char en[6]; - if (!cp_ethernet_address(name, en, context)) - return 0; - for (struct ifnet *dev = dev_base; dev; dev = dev->next) - if (dev->type == ARPHRD_ETHER && memcmp(en, dev->dev_addr, 6) == 0) - return dev; -#endif - return 0; +void +AnyDeviceMap::cleanup(void) +{ + lock_destroy(); } ELEMENT_REQUIRES(bsdmodule) diff --git a/elements/bsdmodule/anydevice.hh b/elements/bsdmodule/anydevice.hh index 4e7d1b7..364460d 100644 --- a/elements/bsdmodule/anydevice.hh +++ b/elements/bsdmodule/anydevice.hh @@ -50,28 +50,21 @@ class AnyDevice : public Element { public: AnyDevice *next() const { return _next; } void set_next(AnyDevice *d) { _next = d; } - void set_max_tickets(int t) { _max_tickets = t; } int find_device(bool, AnyDeviceMap *, ErrorHandler *); void set_device(net_device *, AnyDeviceMap *); void clear_device(AnyDeviceMap *); - void adjust_tickets(int work); - void intr_reschedule(); protected: String _devname; struct ifnet *_dev; - Task _task; bool _promisc : 1; + bool _in_map : 1; AnyDevice *_next; - private: - - int _max_tickets; - int _idles; - + friend class AnyDeviceMap; }; @@ -79,6 +72,9 @@ class AnyTaskDevice : public AnyDevice { public: AnyTaskDevice(); + void set_max_tickets(int t) { _max_tickets = t; } + + void intr_reschedule(); void adjust_tickets(int work); protected: @@ -91,7 +87,7 @@ class AnyTaskDevice : public AnyDevice { public: inline void -AnyDevice::intr_reschedule(void) +AnyTaskDevice::intr_reschedule(void) { #ifdef BSD_NETISRSCHED if (!_task.scheduled()) @@ -105,7 +101,7 @@ AnyDevice::intr_reschedule(void) inline void -AnyDevice::adjust_tickets(int work) +AnyTaskDevice::adjust_tickets(int work) { #if CLICK_DEVICE_ADJUST_TICKETS int tix = _task.tickets(); @@ -137,32 +133,83 @@ AnyDevice::adjust_tickets(int work) class AnyDeviceMap { public: - void initialize(); - AnyDevice *lookup(struct ifnet *); - AnyDevice *lookup_unknown(struct ifnet *); + void initialize(const char *, const char *); + void cleanup(); + AnyDevice *lookup(struct ifnet *, AnyDevice *); + AnyDevice *lookup_unknown(struct ifnet *, AnyDevice *); + void lookup_all(struct ifnet *, bool, Vector &); void insert(AnyDevice *); void remove(AnyDevice *); + void move_to_front(AnyDevice *); + + void lock_init(const char *, const char *, int); + void lock_destroy(); + void lock_acquire(); + void lock_release(); private: - static const int MAP_SIZE = 64; + enum { MAP_SIZE = 64 }; + struct mtx _adm_lock; + int _lock_opts; AnyDevice *_unknown_map; AnyDevice *_map[MAP_SIZE]; }; +inline void +AnyDeviceMap::lock_init(const char *id, const char *cl, int opts) +{ + _lock_opts = opts; + mtx_init(&_adm_lock, id, cl, opts); +} + +inline void +AnyDeviceMap::lock_destroy(void) +{ + mtx_destroy(&_adm_lock); +} + +inline void +AnyDeviceMap::lock_acquire(void) +{ + if(_lock_opts == MTX_SPIN) + mtx_lock_spin(&_adm_lock); + else + mtx_lock(&_adm_lock); +} + +inline void +AnyDeviceMap::lock_release(void) +{ + if(_lock_opts == MTX_SPIN) + mtx_unlock_spin(&_adm_lock); + else + mtx_unlock(&_adm_lock); +} + inline AnyDevice * -AnyDeviceMap::lookup(struct ifnet *dev) +AnyDeviceMap::lookup(struct ifnet *dev, AnyDevice *last) { - if (dev == NULL) - return NULL; + AnyDevice *d; + + if(dev == NULL) + return NULL; + + lock_acquire(); + + if(last != NULL) + d = last->_next; + else + d = _map[dev->if_index % MAP_SIZE]; + + while(d && d->device() != dev) + d = d->next(); - AnyDevice *d = _map[dev->if_index % MAP_SIZE]; - while (d && d->device() != dev) - d = d->next(); - return d; + lock_release(); + return d; } -struct ifnet *find_device_by_ether_address(const String &, Element *); +struct ifnet *etheraddr_to_dev(const String &, Element *); #endif diff --git a/elements/bsdmodule/click_ether.cc b/elements/bsdmodule/click_ether.cc new file mode 100644 index 0000000..c899f0a --- /dev/null +++ b/elements/bsdmodule/click_ether.cc @@ -0,0 +1,364 @@ +// -*- mode: c++; c-basic-offset: 4 -*- +/* + * fromdevice.{cc,hh} -- element steals packets from kernel devices + * + * Robert Morris + * Eddie Kohler: AnyDevice, other changes + * Benjie Chen: scheduling, internal queue + * Nickolai Zeldovich, Luigi Rizzo, Marko Zec: BSD + * + * Copyright (c) 1999-2000 Massachusetts Institute of Technology + * Copyright (c) 2000 Mazu Networks, Inc. + * Copyright (c) 2001-2004 International Computer Science Institute + * Copyright (c) 2004 University of Zagreb + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, subject to the conditions + * listed in the Click LICENSE file. These conditions include: you must + * preserve this copyright notice, and you cannot mention the copyright + * holders in advertising related to the Software without their permission. + * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This + * notice is a summary of the Click LICENSE file; the license in that file is + * legally binding. + */ + +#include +#include +#include "fromdevice.hh" +#include "fromhost.hh" +#include +#include +#include +#include + +#include +CLICK_CXX_PROTECT +#include +#include +#include +#include +#include + +CLICK_CXX_UNPROTECT +#include + +#include "click_ether.hh" + +extern "C" { + +static int _click_ether_registered = 0; + +static void *saved_ng_input = NULL; +static void *saved_ng_input_orphan = NULL; +static void *saved_ng_output = NULL; +static void *saved_ng_attach = NULL; +static void *saved_ng_detach = NULL; +static void *saved_ng_link_state = NULL; + + +/* + * Called from interrupt context. + */ +void +click_ether_input(struct ifnet *ifp, struct mbuf **mp) +{ + struct mbuf *m = *mp; + FromDevice *fd = NULL; + + /* + * XXX:bohra + * + * This was there before my rewrite, but I believe this will + * _never_ be true. + * + * This is true iff ToHost uses ether_input() to requeue packets. + * Instead, do the work of ether_input and directly call ether_demux. + * + * Leave this here for now, we can remove it later. + * + */ + if(m->m_pkthdr.rcvif == NULL) { + /* + * The packet is being sent from Click to FreeBSD. + * + * Check if the interface is ready for packets and + * the mbuf has the ethernet header pulled up. + * + * Set the interface address and return. + */ + if(!((ifp->if_flags & IFF_UP) && + (ifp->if_drv_flags & IFF_DRV_RUNNING))) { + + m_freem(m); + *mp = NULL; + return; + } + + /* + * Ensure that the header is fully pulled up. + */ + if(m->m_pkthdr.len < sizeof(struct ether_header)) { + m_freem(m); + *mp = NULL; + return; + } + + if(m->m_len < sizeof(struct ether_header)) { + m = m_pullup(m, sizeof(struct ether_header)); + + if(m == NULL) { + *mp = NULL; + return; + } + } + + m->m_pkthdr.rcvif = ifp; + return; + } + + /* + * Lookup click element which handles this packet. + * + * If the packet is handled, steal it from the ethernet handler. + */ + fd = (FromDevice *)IFP2AC(ifp)->ac_netgraph; + if(fd != NULL && fd->got_mbuf(ifp, mp)) + *mp = NULL; +} + +/* + * Called from interrupt context. + */ +int +click_ether_output(struct ifnet *ifp, struct mbuf **mp) +{ +#if 0 + /* + * We are called only when click is registered as ac_netgraph for + * the FromHost element. + * + * If the rcvif is already set, this is a problem... + * + * So, now, we must take the mbuf and put it in the appropriate + * device's input queue. + * + * Clear *mp to grab packet from FreeBSD. + */ + if(IFP2AC(ifp)->ac_netgraph == NULL) + return 0; + + FromHost *fh = (FromHost *)(IFP2AC(ifp)->ac_netgraph); + + if(fh->got_mbuf(mp) == 0) + *mp = NULL; +#endif + return 0; +} + +/* + * Called from taskqueue. + */ +int +click_ether_link_state(struct ifnet *ifp, int state) +{ + bool down; + Vector v; + + down = (state == LINK_STATE_DOWN) ? true : false; + + from_device_map.lookup_all(ifp, down, v); + for(int i = 0; i < v.size(); i++) + ((FromDevice *)(v[i]))->change_device(down ? NULL : ifp); + + return 0; +} + +/* + * Called from taskqueue + * + * This allows us to bring up unspecified interfaces as well. + * + * The entry for this interface would have been created earlier. + * + * So, what we do is do a find_device in from device map and try to + * associate the newly attached device to the FromDevice object. + */ +int +click_ether_attach(struct ifnet *ifp) +{ + /* + * First lookup in the known map and then in the + * unknown map. + * Known map would be an error. + * Unknown map means we need to start the device. + */ + Vector v; + v.clear(); + from_device_map.lookup_all(ifp, false, v); + + for(int i = 0; i < v.size(); i++) + ((FromDevice *)(v[i]))->change_device(ifp); + + return 0; +} + +void +click_ether_detach(struct ifnet *ifp) +{ + Vector v; + + v.clear(); + from_device_map.lookup_all(ifp, true, v); + + for(int i = 0; i < v.size(); i++) + ((FromDevice *)(v[i]))->change_device(NULL); + + return; +} + +void +click_ether_linkstate(struct ifnet *ifp, int state) +{ + click_chatter("Link event"); +} + +/* + * Save the old netgraph_ether_hook_p and use the click + * handlers for these. + * + * In all cases, the Click router takes over packets. + * + * If the netgraph ether is defined, it will not get any packets. + * + * XXX: We can also find the netgraph hook and attach to the lower + * and upper hooks as an interface. + */ +void +click_ether_register(void) +{ + if(_click_ether_registered != 0) + return; + + saved_ng_input = ng_ether_input_p; + saved_ng_input_orphan = ng_ether_input_orphan_p; + saved_ng_output = ng_ether_output_p; + saved_ng_attach = ng_ether_attach_p; + saved_ng_detach = ng_ether_detach_p; + saved_ng_link_state = ng_ether_link_state_p; + + /* + * XXX:bohra + * Netgraph attachment checks if ng_ether_attach_p != NULL + * before attaching. The following must prevent that. + * However, if that check changes, we must follow. + */ + ng_ether_input_p = click_ether_input; + ng_ether_input_orphan_p = click_ether_input_orphan; + ng_ether_output_p = click_ether_output; + ng_ether_attach_p = click_ether_attach; + ng_ether_detach_p = click_ether_detach; + ng_ether_link_state_p = click_ether_linkstate; + + _click_ether_registered++; +} + +void +click_ether_unregister(void) +{ + if(--_click_ether_registered == 0) { + ng_ether_input_p = saved_ng_input; + ng_ether_input_orphan_p = saved_ng_input_orphan; + ng_ether_output_p = saved_ng_output; + ng_ether_attach_p = saved_ng_attach; + ng_ether_detach_p = saved_ng_detach; + ng_ether_link_state_p = saved_ng_link_state; + } +} + +void +click_ether_input_orphan(struct ifnet *ifp, struct mbuf *m) +{ + click_chatter("Orphan packet"); +} + +} +#if 0 +/* + * Process incoming packets using the ng_ether_input_p hook. + * The if_spare2 (normally unused) field from the struct ifnet is + * used as a pointer to the fromdevice data structure. Call it "xp". + * + * If xp == NULL, no FromDevice element is registered on this + * interface, so return to pass the packet back to FreeBSD. + * Otherwise, xp points to the element, which in turn contains + * a queue. Append the packet there, clear *mp to grab the pkt from FreeBSD. + * call wakeup() to poentially wake up the element. + * + * Special case: m->m_pkthdr.rcvif == NULL means the packet is coming + * from click and directed to this interface on the host. + * We are already running at splimp() so we need no further protection. + */ +extern "C" +void +click_ether_input(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh) +{ + if (ifp->if_spare2 == NULL) // not for click. + return ; + + struct mbuf *m = *mp; + if (m->m_pkthdr.rcvif == NULL) { // Special case: from click to FreeBSD + m->m_pkthdr.rcvif = ifp; // Reset rcvif to correct value, and + return; // let FreeBSD continue processing. + } + + *mp = NULL; // tell ether_input no further processing needed. + + FromDevice *me = (FromDevice *)(ifp->if_spare2); + +#if 0 /* XXX: needs rewriting for polling(4) rewrite. -bms */ + /* + * If sysctl kern.polling.enable == 2 we should take care of polling + * this NIC from inside a Click thread, so steal the handler from BSD. + */ + if (!me->_polling && (ifp->if_ipending & IFF_POLLING) && + polling && *polling == 2) { + struct pollrec *prp = pr; + int i; + + for (i = 0; i < *poll_handlers; i++, prp++) + if (prp->ifp == ifp) { + me->_poll_handler = prp->handler; + me->_poll_status_tick = ticks; + prp->handler = NULL; + prp->ifp = NULL; + printf("Click FromDevice(%s) taking control over NIC driver polling\n", ifp->if_xname); + me->_polling = -1; // wakeup task thread only once more + break; + } + if (!me->_polling) { + printf("Strange, couldn't find polling handler for %s\n", + ifp->if_xname); + me->_polling = -2; // Do not bother trying to register again + } + } +#endif + + // put the ethernet header back into the mbuf. + M_PREPEND(m, sizeof(*eh), M_WAIT); + bcopy(eh, mtod(m, struct ether_header *), sizeof(*eh)); + + if (_IF_QFULL(me->_inq)) { + _IF_DROP(me->_inq); + m_freem(m); + } else + _IF_ENQUEUE(me->_inq, m); + if (me->_polling != 1) + me->intr_reschedule(); + if (me->_polling == -1) + me->_polling = 1; // no need to wakeup task thread any more +#ifdef FROMDEVICE_TSTAMP + me->_tstamp = rdtsc(); +#endif +} +#endif diff --git a/elements/bsdmodule/click_ether.hh b/elements/bsdmodule/click_ether.hh new file mode 100644 index 0000000..e37e668 --- /dev/null +++ b/elements/bsdmodule/click_ether.hh @@ -0,0 +1,22 @@ +#ifndef __CLICK_ETHER_HH__ +#define __CLICK_ETHER_HH__ + +extern "C" { +extern void (*ng_ether_input_p)(struct ifnet *, struct mbuf **); +extern void (*ng_ether_input_orphan_p)(struct ifnet *, struct mbuf *); +extern int (*ng_ether_output_p)(struct ifnet *, struct mbuf **); +extern void (*ng_ether_attach_p)(struct ifnet *); +extern void (*ng_ether_detach_p)(struct ifnet *); +extern void (*ng_ether_link_state_p)(struct ifnet *, int); + +void click_ether_input(struct ifnet *, struct mbuf **); +void click_ether_input_orphan(struct ifnet *, struct mbuf *); +int click_ether_output(struct ifnet *, struct mbuf **); +int click_ether_attach(struct ifnet *); +void click_ether_detach(struct ifnet *); +void click_ether_linkstate(struct ifnet *, int); +void click_ether_register(void); +void click_ether_unregister(void); + +} +#endif diff --git a/elements/bsdmodule/fastudpsrc.cc b/elements/bsdmodule/fastudpsrc.cc index 0fd384d..38fe440 100644 --- a/elements/bsdmodule/fastudpsrc.cc +++ b/elements/bsdmodule/fastudpsrc.cc @@ -96,12 +96,12 @@ FastUDPSource::initialize(ErrorHandler *) // Create an mbuf with an mbuf cluster so copies are quick // (we just add a reference to the cluster rather than doing // a memory copy). - MGETHDR(_m, M_WAIT, MT_DATA); + MGETHDR(_m, M_TRYWAIT, MT_DATA); if (_m == NULL) { click_chatter("unable to get mbuf for FastUDPSource"); return -1; } - MCLGET(_m, M_WAIT); + MCLGET(_m, M_TRYWAIT); if ((_m->m_flags & M_EXT) == 0) { m_freem(_m); click_chatter("unable to get mbuf cluster for FastUDPSource"); @@ -165,20 +165,8 @@ FastUDPSource::pull(int) return 0; } - if (mclrefcnt[mtocl(_m->m_ext.ext_buf)] >= SCHAR_MAX) { - caddr_t mcl, mcl0; - MCLALLOC(mcl, M_WAIT); - if (!mcl) { - click_chatter("failure to allocate new mbuf cluster\n"); - return 0; - } - - bcopy(_m->m_data, mcl, _m->m_len); - mcl0 = _m->m_ext.ext_buf; - _m->m_data = mcl; - _m->m_ext.ext_buf = mcl; - MCLFREE(mcl0); - } + // FreeBSD's mbuf cluster refcounting was completely rewritten, + // so we don't need to check for the lame 127 references limitation. -bms bool need_packet = false; if (_rate_limited) { @@ -193,7 +181,7 @@ FastUDPSource::pull(int) if (!need_packet) return 0; // nothing to pull - m = m_copypacket(_m, M_WAIT); + m = m_copypacket(_m, M_TRYWAIT); if (!m) { click_chatter("unable to m_copypacket\n"); return 0; // bad luck. diff --git a/elements/bsdmodule/fromdevice.cc b/elements/bsdmodule/fromdevice.cc index 30c06e8..a1c8141 100644 --- a/elements/bsdmodule/fromdevice.cc +++ b/elements/bsdmodule/fromdevice.cc @@ -26,7 +26,6 @@ #include #include #include "fromdevice.hh" -#include "fromhost.hh" #include #include #include @@ -34,434 +33,576 @@ #include CLICK_CXX_PROTECT -#include +#include +#include +#include +#include #include +#include #include #include CLICK_CXX_UNPROTECT #include +#include +#include +#include "click_ether.hh" -#define POLL_LIST_LEN 128 // XXX should refernce a proper #include +static AnyDeviceMap from_device_map; -struct pollrec { - poll_handler_t *handler; - struct ifnet *ifp; -}; +static eventhandler_tag attach_notifier = NULL; +static eventhandler_tag detach_notifier = NULL; -int *polling; // polling mode -static int *poll_handlers; // # of NICs registered for BSD kernel polling -static int *reg_frac; // How often we have to check status registers -static struct pollrec *pr; // BSD kernel polling handlers +extern "C" { -static AnyDeviceMap from_device_map; -static int registered_readers; -static int from_device_count; +int *polling = NULL; +static void +attach_notifier_hook(void *arg __unused, struct ifnet *ifp) +{ + Vector es; -/* - * Process incoming packets using the ng_ether_input_p hook. - * The if_poll_intren (normally unused) field from the struct ifnet is - * used as a pointer to the fromdevice data structure. Call it "xp". - * - * If xp == NULL, no FromDevice element is registered on this - * interface, so return to pass the packet back to FreeBSD. - * Otherwise, xp points to the element, which in turn contains - * a queue. Append the packet there, clear *mp to grab the pkt from FreeBSD. - * call wakeup() to poentially wake up the element. - * - * Special case: m->m_pkthdr.rcvif == NULL means the packet is coming - * from click and directed to this interface on the host. - * We are already running at splimp() so we need no further protection. - */ -extern "C" -void -click_ether_input(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh) + NET_LOCK_GIANT(); + IFF_LOCKGIANT(ifp); + + from_device_map.lookup_all(ifp, false, es); + for(int i = 0; i < es.size(); i++) + ((FromDevice *)(es[i]))->set_device(ifp, &from_device_map); + + IFF_UNLOCKGIANT(ifp); + NET_UNLOCK_GIANT(); + +} + +static void +detach_notifier_hook(void *arg __unused, struct ifnet *ifp) { - if (ifp->if_poll_intren == NULL) // not for click. - return ; + Vector es; - struct mbuf *m = *mp; - if (m->m_pkthdr.rcvif == NULL) { // Special case: from click to FreeBSD - m->m_pkthdr.rcvif = ifp; // Reset rcvif to correct value, and - return; // let FreeBSD continue processing. - } + NET_LOCK_GIANT(); + IFF_LOCKGIANT(ifp); - *mp = NULL; // tell ether_input no further processing needed. - - FromDevice *me = (FromDevice *)(ifp->if_poll_intren); - - /* - * If sysctl kern.polling.enable == 2 we should take care of polling - * this NIC from inside a Click thread, so steal the handler from BSD. - */ - if (!me->_polling && (ifp->if_ipending & IFF_POLLING) && - polling && *polling == 2) { - struct pollrec *prp = pr; - int i; - - for (i = 0; i < *poll_handlers; i++, prp++) - if (prp->ifp == ifp) { - me->_poll_handler = prp->handler; - me->_poll_status_tick = ticks; - prp->handler = NULL; - prp->ifp = NULL; - printf("Click FromDevice(%s%d) taking control over NIC driver polling\n", ifp->if_name, ifp->if_unit); - me->_polling = -1; // wakeup task thread only once more - break; - } - if (!me->_polling) { - printf("Strange, couldn't find polling handler for %s%d\n", - ifp->if_name, ifp->if_unit); - me->_polling = -2; // Do not bother trying to register again - } - } + from_device_map.lookup_all(ifp, true, es); + for(int i = 0; i < es.size(); i++) + ((FromDevice *)(es[i]))->set_device(NULL, &from_device_map); - // put the ethernet header back into the mbuf. - M_PREPEND(m, sizeof(*eh), M_WAIT); - bcopy(eh, mtod(m, struct ether_header *), sizeof(*eh)); - - if (IF_QFULL(me->_inq)) { - IF_DROP(me->_inq); - m_freem(m); - } else - IF_ENQUEUE(me->_inq, m); - if (me->_polling != 1) - me->intr_reschedule(); - if (me->_polling == -1) - me->_polling = 1; // no need to wakeup task thread any more -#ifdef FROMDEVICE_TSTAMP - me->_tstamp = rdtsc(); -#endif + IFF_UNLOCKGIANT(ifp); + NET_UNLOCK_GIANT(); } -/* - * Process outgoing packets using the ng_ether_output_p hook. - * If if_poll_xmit == NULL, no FromHost element is registered on this - * interface, so return 0 to pass the packet back to FreeBSD. - * Otherwise, if_poll_xmit points to the element, which in turn contains - * a queue. Append the packet there, clear *mp to grab the pkt from FreeBSD, - * and possibly wakeup the element. - * - * We _need_ splimp()/splx() to avoid races. - */ -extern "C" -int -click_ether_output(struct ifnet *ifp, struct mbuf **mp) +} + +/*************************** Initialization *******************************/ +void +FromDevice::static_initialize() { - int s = splimp(); - if (ifp->if_poll_xmit == NULL) { // not for click... - splx(s); - return 0; - } - struct mbuf *m = *mp; - *mp = NULL; // tell ether_output no further processing needed - - FromHost *me = (FromHost *)(ifp->if_poll_xmit); - if (IF_QFULL(me->_inq)) { - IF_DROP(me->_inq); - m_freem(m); - } else - IF_ENQUEUE(me->_inq, m); - me->intr_reschedule(); - splx(s); - return 0; + /* + * A lock that protects the from_device_map. + * This should not be called from the interrupt + * context or while holding a mutex. + */ + from_device_map.initialize("FromDeviceMapLock", NULL); + + attach_notifier = EVENTHANDLER_REGISTER(ifnet_arrival_event, + attach_notifier_hook, NULL, + EVENTHANDLER_PRI_ANY); + + detach_notifier = EVENTHANDLER_REGISTER(ifnet_departure_event, + detach_notifier_hook, NULL, + EVENTHANDLER_PRI_ANY); } -static void -fromdev_static_initialize() +void +FromDevice::static_cleanup() { - linker_file_t kernel_lf = linker_find_file_by_id(1); /* kernel file */ - - pr = (struct pollrec *) linker_file_lookup_symbol(kernel_lf, "pr", 0); - reg_frac = (int *) linker_file_lookup_symbol(kernel_lf, "reg_frac", 0); - poll_handlers = (int *) - linker_file_lookup_symbol(kernel_lf, "poll_handlers", 0); - if (pr && poll_handlers && reg_frac) - polling = (int *) linker_file_lookup_symbol(kernel_lf, "polling", 0); - else - polling = NULL; - - if (polling) - printf("Cool, we are running Click on a polling capable kernel!\n"); - - if (++from_device_count == 1) - from_device_map.initialize(); + if(attach_notifier != NULL) + EVENTHANDLER_DEREGISTER(ifnet_arrival_event, attach_notifier); + + if(detach_notifier != NULL) + EVENTHANDLER_DEREGISTER(ifnet_departure_event, detach_notifier); + + from_device_map.cleanup(); } -static void -fromdev_static_cleanup() + +void +FromDevice::lock_queue() { - if (--from_device_count <= 0) { - if (registered_readers) - printf("Warning: registered reader count mismatch!\n"); - } + _fd_queue_lock.acquire(); +} + +void +FromDevice::unlock_queue() +{ + _fd_queue_lock.release(); } -FromDevice::FromDevice() +FromDevice::FromDevice() : _fd_queue_lock("FromDeviceLock") { - _readers = 0; // noone registered so far - _polling = 0; // we do not poll until NIC driver registers itself - fromdev_static_initialize(); + /* + * XXX:bohra + * _readers specifies the number of active consumers of this element. + * Since FromDevice has exactly one output, this should never exceed + * 1. + * + * _polling specified if the kernel supports polling or not. + * In the new world order, this is a legacy option, which might actually + * go away one day. + * + * In any case, we should have a separate element PollDevice which + * does the polling. + */ + + /* + * XXX:bohra + * _head and _tail are in the class Storage, used in conjunction with arrays. + * These are currently not used in the bsdmodule. Why? + * + * bsdmodule uses the ifnet data structure for storing packets instead + * of the queue private to the FromDevice object. Change this. + */ + _head = _tail = 0; } FromDevice::~FromDevice() { - fromdev_static_cleanup(); } void * FromDevice::cast(const char *n) { - if (strcmp(n, "Storage") == 0) - return (Storage *)this; - else if (strcmp(n, "FromDevice") == 0) - return (Element *)this; - else - return 0; + if(strcmp(n, "Storage") == 0) + return (Storage *)this; + else if(strcmp(n, "FromDevice") == 0) + return (FromDevice *)this; + else + return NULL; } int FromDevice::configure(Vector &conf, ErrorHandler *errh) { - _promisc = false; - _inq = NULL; - bool allow_nonexistent = false; - _burst = 8; - if (cp_va_parse(conf, this, errh, - cpString, "interface name", &_devname, - cpOptional, - cpBool, "enter promiscuous mode?", &_promisc, - cpUnsigned, "burst size", &_burst, - cpKeywords, - "PROMISC", cpBool, "enter promiscuous mode?", &_promisc, - "PROMISCUOUS", cpBool, "enter promiscuous mode?", &_promisc, - "BURST", cpUnsigned, "burst size", &_burst, - "ALLOW_NONEXISTENT", cpBool, "allow nonexistent interface?", &allow_nonexistent, - cpEnd) < 0) - return -1; - - if (find_device(allow_nonexistent, &from_device_map, errh) < 0) - return -1; - return 0; + bool promisc = false; + bool allow_nonexistent = false; + + _burst = 8; + + if(cp_va_parse(conf, this, errh, + cpString, "device name", &_devname, + cpOptional, + cpBool, "enter promiscuous mode?", &promisc, + cpUnsigned, "burst size", &_burst, + cpKeywords, + "PROMISC", cpBool, "enter promiscuous mode?", &promisc, + "PROMISCUOUS", cpBool, "enter promiscuous mode?", &promisc, + "BURST", cpUnsigned, "burst size", &_burst, + "ALLOW_NONEXISTENT", cpBool, "allow nonexistent device?", &allow_nonexistent, + cpEnd) < 0) + return -1; + + + if(promisc) + set_promisc(); + + /* + * find_device tries to find the device corresponding to _devname + * and inserts it in the map. + */ + + return find_device(allow_nonexistent, &from_device_map, errh); } /* - * Use a Linux interface added by us, in net/core/dev.c, - * to register to grab incoming packets. + * Grab incoming packets from the network. */ int FromDevice::initialize(ErrorHandler *errh) { - // check for duplicates - if (ifindex() >= 0) - for (int fi = 0; fi < router()->nelements(); fi++) { - Element *e = router()->element(fi); - if (e == this) - continue; - if (FromDevice *fd = (FromDevice *)(e->cast("FromDevice"))) { - if (fd->ifindex() == ifindex()) - return errh->error("duplicate FromDevice for `%s'", - _devname.c_str()); - } + /* + * The force_attachment option ensures there is exactly one + * device attached to the interface. + */ + if(ifindex() >= 0) { + void *&used = router()->force_attachment( + "device_reader_" + String(ifindex())); + + if(used != NULL) { + return errh->error("duplicate reader for device '%s'", + _devname.c_str()); + } + + used = this; + } + + NET_LOCK_GIANT(); + IFF_LOCKGIANT(_dev); + + /* + * Replace ng_ether_* functions with our own. + */ + click_ether_register(); + + if(IFP2AC(_dev)->ac_netgraph != NULL) { + _saved_ng_node = IFP2AC(_dev)->ac_netgraph; + + /* + * Set the ac_netgraph value to ourselves. + * This accomplishes two things : + * + * 1. The ether_input() checks this value to be non-NULL + * to invoke the ng_ether hooks. + * 2. We do not need to lookup the device for handlers + * in click_ether_input(). + */ + IFP2AC(_dev)->ac_netgraph = this; } - from_device_map.insert(this); - if (_promisc && device()) - ifpromisc(device(), 1); - - assert(device()); - int s = splimp(); - if (_inq == NULL) { - if (_readers != 0) - printf("Warning, _readers mismatch (%d should be 0)\n", - _readers); - _inq = (struct ifqueue *) - malloc(sizeof (struct ifqueue), M_DEVBUF, M_NOWAIT|M_ZERO); - assert(_inq); - _inq->ifq_maxlen = QSIZE; - (FromDevice *)(device()->if_poll_intren) = this; - } else { - if (_readers == 0) - printf("Warning, _readers mismatch (should not be 0)\n"); - } - _readers++; - registered_readers++; - splx(s); +#ifdef DEVICE_POLLING + /* + * Now, we must deregister the polling handler if registered and + * enable interrupt based delivery of packets. + * + * XXX:bohra + * + * This may not be desirable. Even if we are using fromdevice, + * we may want to be called from the polling handler. + */ + if(_dev->if_capenable & IFCAP_POLLING) { + struct ifreq ifr; + + memset(&ifr, 0, sizeof(struct ifreq)); + ifr.ifr_reqcap = _dev->if_capenable & ~IFCAP_POLLING; + + int error = _dev->if_ioctl(_dev, SIOCSIFCAP, (caddr_t)&ifr); + + errh->warning("FromDevice used with polling interface '%s'", + _devname.c_str()); + + if(error != 0) + errh->warning("Cannot disable polling for '%s'!", + _devname.c_str()); + else { + _polling_disabled = true; + errh->warning("Polling disabled for '%s'!", + _devname.c_str()); + } + } +#endif + + IFF_UNLOCKGIANT(_dev); + NET_UNLOCK_GIANT(); - ScheduleInfo::initialize_task(this, &_task, device() != 0, errh); + ScheduleInfo::initialize_task(this, &_task, _dev != NULL, errh); #ifdef HAVE_STRIDE_SCHED - // start out with default number of tickets, inflate up to max - set_max_tickets( _task.tickets() ); - _task.set_tickets(Task::DEFAULT_TICKETS); + _max_tickets = _task.tickets(); + _task.set_tickets(Task::DEFAULT_TICKETS); #endif -#if CLICK_DEVICE_STATS - // Initialize performance stats - _time_read = 0; - _time_push = 0; - _perfcnt1_read = 0; - _perfcnt2_read = 0; - _perfcnt1_push = 0; - _perfcnt2_push = 0; -#endif - _npackets = 0; + from_device_map.move_to_front(this); - _capacity = QSIZE; - return 0; + _capacity = QSIZE; + _drops = 0; + + reset_counts(); + + return 0; } void -FromDevice::cleanup(CleanupStage) +FromDevice::cleanup(CleanupStage stage) { - if (!device()) - return; - - struct ifqueue *q = NULL; - int s = splimp(); - registered_readers--; - _readers--; - if (_readers == 0) { // flush queue - q = _inq ; - _inq = NULL ; - device()->if_poll_intren = NULL ; - } - if (_polling == 1) { // return polling handler to the kernel - struct pollrec *prp = pr; - int i; - for (i = 0; i < *poll_handlers; i++, prp++) - if (prp->ifp == NULL) - break; - prp->handler = _poll_handler; - prp->ifp = _dev; - if (*poll_handlers == 0 && (_dev->if_flags & IFF_RUNNING)) - *poll_handlers = 1; - } - splx(s); - if (q) { // we do not mutex for this. - int i, max = q->ifq_maxlen ; - for (i = 0; i < max; i++) { - struct mbuf *m; - IF_DEQUEUE(q, m); - if (!m) - break; - m_freem(m); + + NET_LOCK_GIANT(); + IFF_LOCKGIANT(_dev); + + if(_saved_ng_node != NULL) { + IFP2AC(_dev)->ac_netgraph = _saved_ng_node; + _saved_ng_node = NULL; } - free(q, M_DEVBUF); - } - - from_device_map.remove(this); - if (_promisc && device()) - ifpromisc(device(), 0); + + /* Restore the ng_ether_* functions */ + click_ether_unregister(); + +#ifdef DEVICE_POLLING + if(stage >= CLEANUP_INITIALIZED) { + if(_polling_disabled) { + struct ifreq ifr; + + memset(&ifr, 0, sizeof(struct ifreq)); + ifr.ifr_reqcap = _dev->if_capenable | IFCAP_POLLING; + _dev->if_ioctl(_dev, SIOCSIFCAP, (caddr_t)&ifr); + } + } +#endif + + IFF_UNLOCKGIANT(_dev); + NET_UNLOCK_GIANT(); + + clear_device(&from_device_map); + + lock_queue(); + for(unsigned i = _head; i != _tail; i = next_i(i)) + _queue[i]->kill(); + + _head = _tail = 0; + unlock_queue(); + } void FromDevice::take_state(Element *e, ErrorHandler *errh) { - FromDevice *fd = (FromDevice *)e->cast("FromDevice"); - if (!fd) return; + FromDevice *fd = (FromDevice *)e->cast("FromDevice"); + + if(fd == NULL) + return; + + /* + * Need to hold locks on both "to" device and + * "from" device + */ + + fd->lock_queue(); + lock_queue(); + + if(_head != _tail) { + errh->error("already have packets enqueued, can't take state"); + fd->unlock_queue(); + return; + } + memcpy(_queue, fd->_queue, sizeof(Packet *) * (QSIZE + 1)); + + _head = fd->_head; + _tail = fd->_tail; + + fd->_head = fd->_tail = 0; + + unlock_queue(); + fd->unlock_queue(); } -bool -FromDevice::run_task() +void +FromDevice::change_device(struct ifnet *dev) { - int npq = 0; - // click_chatter("FromDevice::run_task()."); - - if (_dev && _polling == 1) { - if (_dev->if_ipending & IFF_POLLING) { - enum poll_cmd cmd; - - if ( _poll_status_tick <= ticks ) { - _poll_status_tick = ticks + *reg_frac; - cmd = POLL_AND_CHECK_STATUS; - } else - cmd = POLL_ONLY; - - if ( *polling != 2 ) { // Need to return the handler to the kernel - struct pollrec *prp = pr; - int i; - _polling = 0; // No more polling in Click - for (i = 0; i < *poll_handlers; i++, prp++) - if (prp->ifp == NULL) - break; - prp->handler = _poll_handler; - prp->ifp = _dev; - if (*poll_handlers == 0 && (_dev->if_flags & IFF_RUNNING)) - *poll_handlers = 1; - } else - _poll_handler(_dev, cmd, _burst); - } else - _polling = 0; // No more polling - } + struct ifnet *ifp = _dev; + + /* + * Need to hold locks on both old and new devices. + */ + NET_LOCK_GIANT(); + + if(ifp != NULL) + IFF_LOCKGIANT(ifp); + if(dev != NULL) + IFF_LOCKGIANT(dev); + + set_device(dev, &from_device_map); + + if(dev != NULL) + IFF_UNLOCKGIANT(dev); + if(ifp != NULL) + IFF_UNLOCKGIANT(ifp); + + NET_UNLOCK_GIANT(); +} + +/* + * got_mbuf checks and updates the tail of the queue. + */ +int +FromDevice::got_mbuf(struct ifnet *ifp, struct mbuf **mp) +{ + NET_ASSERT_GIANT(); + + if(ifp != _dev) { + /* + * This is the only case when we do not steal the packet. + */ + click_chatter("Got packet for '%s' on device '%s'", + ifp->if_xname, _devname.c_str()); + + return 0; + } + + click_chatter("Got packet!!"); + + /* + * We got a packet from the network. + * Store it in the packet queue and drop it if it is full. + */ + struct mbuf *m = *mp; + + lock_queue(); + + unsigned next = next_i(_tail); + + if(next != _head) { + Packet *p = Packet::make(m); + + _queue[_tail] = p; + + +#if CLICK_DEBUG_SCHEDULING + click_gettimeofday(&_schinfo[_tail].enq_time); + RouterThread *rt = _task.thread(); + _schinfo[_tail].enq_state = rt->thread_state(); + int enq_proc_asleep = rt->sleeper() && rt->sleeper()->state != TASK_RUNNING; + _schinfo[_tail].task_scheduled = _task.scheduled(); + _schinfo[_tail].enq_epoch = rt->driver_epoch(); + _schinfo[_tail].enq_task_epoch = rt->driver_task_epoch(); +#endif + _tail = next; + + unlock_queue(); + + _task.reschedule(); + +#if CLICK_DEBUG_SCHEDULING + _schinfo[_tail].enq_woke_process = enq_process_asleep && rt->sleeper()->state == TASK_RUNNING; +#endif + + } + else { + unlock_queue(); + m_freem(m); + _drops++; + } + + return 1; +} -#ifdef FROMDEVICE_TSTAMP - if (_tstamp) { - uint64_t now = rdtsc(); - click_chatter("FromDevice::run_task() latency=%ld", now - _tstamp); - _tstamp = 0; +#if CLICK_DEBUG_SCHEDULING +void +FromDevice::emission_report(int idx) +{ + struct timeval now; + click_gettimeofday(&now); + RouterThread *rt = _task.thread(); + StringAccum sa; + sa << "dt " << (now - _schinfo[idx].enq_time); + if (_schinfo[idx].enq_state != RouterThread::S_RUNNING) { + struct timeval etime = rt->task_epoch_time(_schinfo[idx].enq_task_epoch + 1); + if (timerisset(&etime)) + sa << " dt_thread " << (etime - _schinfo[idx].enq_time); } + sa << " arrst " << RouterThread::thread_state_name(_schinfo[idx].enq_state) + << " depoch " << (rt->driver_epoch() - _schinfo[idx].enq_epoch) + << " dtepoch " << (rt->driver_task_epoch() - _schinfo[idx].enq_task_epoch); + if (_schinfo[idx].enq_woke_process) + sa << " woke"; + if (_schinfo[idx].enq_task_scheduled) + sa << " tasksched"; + + click_chatter("%s packet: %s", name().c_str(), sa.c_str()); +} #endif - while (npq <= _burst) { - struct mbuf *m = 0; +/* + * run_task updates the _head of the queue. + */ +bool +FromDevice::run_task(void) +{ + int npq = 0; + bool retval = false; - // Try to dequeue a packet from the interrupt input queue. - IF_DEQUEUE(_inq, m); - if (m == NULL) { -#if CLICK_DEVICE_ADJUST_TICKETS - adjust_tickets(npq); + _runs++; + + + lock_queue(); + while(npq < _burst && !(_head == _tail)) { + Packet *p = _queue[_head]; +#if CLICK_DEBUG_SCHEDULING + emission_report(_head); #endif - if (_polling) - _task.fast_reschedule(); - return npq > 0; + _head = next_i(_head); + + + unlock_queue(); + + output(0).push(p); + npq++; + _pushes++; + + lock_queue(); } + unlock_queue(); - // Got a packet, which includes the MAC header. Make it a real Packet. + if(npq == 0) + _empty_runs++; - Packet *p = Packet::make(m); - output(0).push(p); - npq++; - _npackets++; - } #if CLICK_DEVICE_ADJUST_TICKETS - adjust_tickets(npq); + adjust_tickets(npq); #endif -//printf("fromdevice couldn't handle all packets\n"); - _task.fast_reschedule(); - return true; + + if(npq > 0) { + retval = false; + _task.fast_reschedule(); + } + + return retval; } -int -FromDevice::get_inq_drops() +void +FromDevice::reset_counts(void) { - return (_inq ? _inq->ifq_drops : 0); + _runs = 0; + _empty_runs = 0; + _pushes = 0; +} + +static int +FromDevice_write_stats(const String &, Element *e, void *, ErrorHandler *errh) +{ + FromDevice *fd = (FromDevice *)e->cast("FromDevice"); + + if(fd == NULL) + return errh->error("invalid call to writestats handler"); + + fd->reset_counts(); } static String -FromDevice_read_stats(Element *f, void *) +FromDevice_read_stats(Element *e, void *thunk) { - FromDevice *fd = (FromDevice *)f; - return - String(fd->_npackets) + " packets received\n" + - String(fd->get_inq_drops()) + " input queue drops\n" + -#if CLICK_DEVICE_STATS - String(fd->_time_read) + " cycles read\n" + - String(fd->_time_push) + " cycles push\n" + - String(fd->_perfcnt1_read) + " perfcnt1 read\n" + - String(fd->_perfcnt2_read) + " perfcnt2 read\n" + - String(fd->_perfcnt1_push) + " perfcnt1 push\n" + - String(fd->_perfcnt2_push) + " perfcnt2 push\n" + -#endif - String(); + FromDevice *fd = (FromDevice *)e->cast("FromDevice"); + + String retstr = ""; + if(fd == NULL) + return retstr; + + switch(reinterpret_cast(thunk)) { + case 0: + return String(fd->drops()); + case 1: + { + StringAccum sa; + + sa << "ncalls run_task(): " << fd->runs() << "\n" + << "ncalls push(): " << fd->pushes() << "\n" + << "empty runs: " << fd->empty_runs() << "\n" + << "drops: " << fd->drops() << "\n"; + + retstr = sa.take_string(); + break; + } + default: + break; + } + + return retstr; } void -FromDevice::add_handlers() +FromDevice::add_handlers(void) { - add_read_handler("stats", FromDevice_read_stats, 0); - add_task_handlers(&_task); -} + add_task_handlers(&_task); + add_read_handler("drops", FromDevice_read_stats, (void *)0); + add_read_handler("calls", FromDevice_read_stats, (void *)1); + add_write_handler("reset_counts", FromDevice_write_stats, NULL); +}; + +#include "click_ether.cc" ELEMENT_REQUIRES(AnyDevice bsdmodule) EXPORT_ELEMENT(FromDevice) diff --git a/elements/bsdmodule/fromdevice.hh b/elements/bsdmodule/fromdevice.hh index c7ec9cd..fafbc05 100644 --- a/elements/bsdmodule/fromdevice.hh +++ b/elements/bsdmodule/fromdevice.hh @@ -68,29 +68,81 @@ which can be used for better performance and stability). #include #include "elements/bsdmodule/anydevice.hh" #include +#include +CLICK_CXX_PROTECT +#include +#include +#include +CLICK_CXX_UNPROTECT +#include -class FromDevice : public AnyDevice, public Storage { public: - - FromDevice(); - ~FromDevice(); - const char *class_name() const { return "FromDevice"; } - const char *port_count() const { return PORTS_0_1; } - const char *processing() const { return PUSH; } - void *cast(const char *); +class FromDevice : public AnyTaskDevice, public Storage { public: + + FromDevice(); + ~FromDevice(); - int configure(Vector &, ErrorHandler *); - int initialize(ErrorHandler *); - void cleanup(CleanupStage); - void add_handlers(); + static void static_initialize(); + static void static_cleanup(); - void change_device(struct if_net *); - - int get_inq_drops(); // get some performance stats + const char *class_name() const { return "FromDevice"; } + const char *port_count() const { return PORTS_0_1; } + const char *processing() const { return PUSH; } + void *cast(const char *); - bool run_task(); + int configure(Vector &, ErrorHandler *); + int initialize(ErrorHandler *); + void cleanup(CleanupStage); + void add_handlers(); + void take_state(Element *, ErrorHandler *); + int got_mbuf(struct ifnet *, struct mbuf **); + + void change_device(struct ifnet *); + bool run_task(); + + void reset_counts(); + + void lock_queue(); + void unlock_queue(); + + unsigned drops(void) { return _drops; } + unsigned runs(void) { return _runs; } + unsigned empty_runs(void) { return _empty_runs;} + unsigned pushes(void) { return _pushes; } + + private: + + unsigned _burst; + unsigned _drops; + + unsigned _runs; + unsigned _empty_runs; + unsigned _pushes; + + Mutex _fd_queue_lock; + + /* + * XXX:bohra + * Not the right way to go. + * For FromDevice(), if the interface is already polling, disable polling + * and re-enable it when cleaning up. + * + * Use PollDevice() for polling the interface from Click. + * Never use the FreeBSD polling handler. + */ + bool _polling_disabled; + bool _polling; + + void *_saved_ng_node; + + enum { QSIZE = 511 }; + Packet *_queue[QSIZE + 1]; +}; + +#if 0 + int get_inq_drops(); // get some performance stats poll_handler_t *_poll_handler; int _npackets; @@ -106,14 +158,5 @@ class FromDevice : public AnyDevice, public Storage { public: int _polling; int _poll_status_tick; uint64_t _tstamp; - - private: - - bool _promisc; - unsigned _burst; - - static const int QSIZE = 511; - -}; - +#endif #endif diff --git a/elements/bsdmodule/fromhost.cc b/elements/bsdmodule/fromhost.cc index eb70e65..c112545 100644 --- a/elements/bsdmodule/fromhost.cc +++ b/elements/bsdmodule/fromhost.cc @@ -17,20 +17,213 @@ */ #include +#include #include #include "fromhost.hh" -#include #include -#include +#include #include +#include +#include + +#include +CLICK_CXX_PROTECT +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include +CLICK_CXX_UNPROTECT +#include + +static int from_host_count; +static AnyDeviceMap fromhost_map; +static struct unrhdr *fh_unit; + +static void fh_eiface_init(void *); +static int fh_eiface_ioctl(struct ifnet *, u_long, caddr_t); +static void fh_eiface_start(struct ifnet *); +static void fh_wakeup(Timer *, void *); + + +#define FH_EIFACE_MTU_MAX 2312 +#define FH_EIFACE_MTU_MIN 72 + +void +FromHost::lock_acquire(void) +{ + _fh_lock.acquire(); +} + +void +FromHost::lock_release(void) +{ + _fh_lock.release(); +} + +/* + * From linuxmodule/fromhost.cc - Rationale for one packet buffer. + * + * 8.May.2003 - Doug and company had crashes with FromHost configurations. + * We eventually figured out this was because fl_tx was called at + * interrupt time -- at bottom-half time, to be exact -- and then pushed + * a packet through the configuration. Whoops: if Click was interrupted, + * and during the bottom-half FromHost emitted a packet into Click, + * DISASTER -- we assume that, when running single-threaded, at most one + * Click thread is active at a time; so there were race conditions, + * particularly with the task list. The solution is a single-packet-long + * queue in FromHost. fl_tx puts a packet onto the queue, a regular + * Click Task takes the packet off the queue. We could have implemented + * a larger queue, but why bother? Linux already maintains a queue for + * the device. + */ +void +FromHost::start(struct ifnet *ifp) +{ + lock_acquire(); + if(_queue == NULL) { + struct mbuf *m = NULL; + + IF_DEQUEUE(&ifp->if_snd, m); + + /* + * Assert that we are the only one executing. ??? + */ + if(m != NULL) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + Packet *p = Packet::make(m); + _queue = p; + _task.reschedule(); + } + } + lock_release(); +} + +/* + * We are called from if_start. If the interface needs giant, it is locked + * at the higher layer. So, we can assert it here. + * + * If we want to be the only task executing in this code, we must lock the + * queue. Since, we might be called from a non-blocking context, we should + * defer the task to a click thread which can block. + * + * We do this in our task and just queue it here for now. + * + * This approach is similar to the ng_eiface object. + */ +static void +fh_eiface_start(struct ifnet *ifp) +{ + NET_ASSERT_GIANT(); -static AnyDeviceMap fromhost_map; + /* Check interface flags */ + if(!((ifp->if_flags & IFF_UP) && + (ifp->if_drv_flags & IFF_DRV_RUNNING))) + return; + + /* Don't do anything if the output is active */ + if(ifp->if_drv_flags & IFF_DRV_OACTIVE) + return; + + + FromHost *fh = (FromHost *)ifp->if_softc; + if(fh != NULL) + fh->start(ifp); + +} + +static int +fh_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + int error = 0; + struct ifreq *const ifr = (struct ifreq *)data; + + NET_ASSERT_GIANT(); + + FromHost *fh = (FromHost *)ifp->if_softc; + + switch(command) { + /* These are usually handled at the higher layer */ + case SIOCSIFADDR: + error = ether_ioctl(ifp, command, data); + break; + case SIOCGIFADDR: + break; + /* Set flags */ + case SIOCSIFFLAGS: + fh->lock_acquire(); + /* + * If the interface is marked up and stopped, start it. + * If it is marked down and running, stop it. + */ + if(ifp->if_flags & IFF_UP) { + if(!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_RUNNING; + } + } + else { + if(ifp->if_drv_flags & IFF_DRV_RUNNING) + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | + IFF_DRV_OACTIVE); + } + fh->lock_release(); + break; + /* Set the interface MTU */ + case SIOCSIFMTU: + fh->lock_acquire(); + if(ifr->ifr_mtu > FH_EIFACE_MTU_MAX || + ifr->ifr_mtu < FH_EIFACE_MTU_MIN) + return EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + + fh->lock_release(); + break; + /* Not supported */ + case SIOCADDMULTI: + case SIOCDELMULTI: + error = 0; + break; + case SIOCSIFPHYS: + error = EOPNOTSUPP; + break; + default: + error = EINVAL; + break; + } + + return error; +} + +static void +fh_eiface_init(void *arg) +{ + FromHost *fh = (FromHost *)arg; + struct ifnet *ifp = fh->device(); + + fh->lock_acquire(); + ifp->if_drv_flags |= IFF_DRV_RUNNING; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + fh->lock_release(); +} FromHost::FromHost() - : _inq(0) + : _macaddr((const unsigned char *)"\000\001\002\003\004\005"), + _wakeup_timer(fh_wakeup, this), _queue(NULL), _fh_lock("FromHostLock") { } @@ -38,89 +231,334 @@ FromHost::~FromHost() { } +void +FromHost::static_initialize() +{ + fromhost_map.initialize("FromHostLock", NULL); + fh_unit = new_unrhdr(0, 0xffff, NULL); + click_chatter("Allocated UNR"); +} + +void +FromHost::static_cleanup() +{ + if(fh_unit != NULL) { + click_chatter("Cleanup UNR"); + delete_unrhdr(fh_unit); + } + + fromhost_map.cleanup(); +} + +static void +clear_sinaddr(struct sockaddr_in *sin) +{ + + bzero(sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = INADDR_ANY; /* XXX: htonl(INAADDR_ANY) ? */ + sin->sin_port = 0; +} + +int +FromHost::set_device_addresses(ErrorHandler *errh) +{ + int error; + struct ifreq ifr; + struct socket *so; + struct thread *td = curthread; + struct ifnet *ifp = _dev; + + NET_LOCK_GIANT(); + /* + * Set the interface up if required. + * XXX: Do we need this here? + */ + if(!(ifp->if_flags & IFF_UP)) { + memset(&ifr, 0, sizeof(struct ifreq)); + ifr.ifr_flags = ifp->if_flags; + ifr.ifr_flags |= IFF_UP; + fh_eiface_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); + } + + error = socreate(AF_INET, &so, SOCK_DGRAM, 0, td->td_ucred, td); + if(error != 0) { + NET_UNLOCK_GIANT(); + return -1; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + + /* + * Set the address on the interface. + */ + struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; + clear_sinaddr(sin); + sin->sin_family = AF_INET; + sin->sin_addr = _destmask.in_addr(); + error = ifioctl(so, SIOCSIFNETMASK, (caddr_t)&ifr, td); + if(error != 0) { + memset(&ifr, 0, sizeof(struct ifreq)); + ifr.ifr_flags = ifp->if_flags; + ifr.ifr_flags &= ~IFF_UP; + fh_eiface_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); + soclose(so); + NET_UNLOCK_GIANT(); + return -1; + } + + clear_sinaddr(sin); + sin->sin_family = AF_INET; + sin->sin_addr = _destaddr; + error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ifr, td); + if(error != 0) { + memset(&ifr, 0, sizeof(struct ifreq)); + ifr.ifr_flags = ifp->if_flags; + ifr.ifr_flags &= ~IFF_UP; + fh_eiface_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); + soclose(so); + NET_UNLOCK_GIANT(); + return -1; + } + + clear_sinaddr(sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = _destaddr.addr() | ~_destmask.addr(); + error = ifioctl(so, SIOCSIFBRDADDR, (caddr_t)&ifr, td); + if(error != 0) { + memset(&ifr, 0, sizeof(struct ifreq)); + ifr.ifr_flags = ifp->if_flags; + ifr.ifr_flags &= ~IFF_UP; + fh_eiface_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); + soclose(so); + NET_UNLOCK_GIANT(); + return -1; + } + + NET_UNLOCK_GIANT(); + return 0; +} + +struct ifnet * +FromHost::new_device(const char *name) +{ + struct ifnet *ifp; + u_char eaddr[6] = {0,0,0,0,0,0}; + + NET_ASSERT_GIANT(); + + ifp = if_alloc(IFT_ETHER); + if(ifp == NULL) + return NULL; + + /* + * Set the ethernet address to that specified in the arguments. + */ + bcopy(_macaddr.data(), eaddr, 6); + click_chatter("Allocating one unit"); + int unit = alloc_unr(fh_unit); + + if_initname(ifp, name, unit); + ifp->if_init = fh_eiface_init; + ifp->if_softc = this; + ifp->if_start = fh_eiface_start; + ifp->if_ioctl = fh_eiface_ioctl; + ifp->if_watchdog = NULL; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN - 1; + ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST); + + ether_ifattach(ifp, eaddr); + + _unit = unit; + + return ifp; +} + int FromHost::configure(Vector &conf, ErrorHandler *errh) { - _burst = 8; // same as in FromDevice + if(cp_va_parse(conf, this, errh, + cpString, "device name", &_devname, + cpIPPrefix, "dest. IP prefix", &_destaddr, &_destmask, + cpKeywords, + "ETHER", cpEthernetAddress, "fake device Ethernet address", &_macaddr, + cpEnd) < 0) { - if (cp_va_parse(conf, this, errh, - cpString, "interface name", &_devname, - cpEnd) < 0 ) { -printf ("FromHost 1\n"); - return -1; - } - if (find_device(false, &fromhost_map, errh) < 0) - return -1; - return 0; + return -1; + } + + if(_devname.length() > IFNAMSIZ - 1) + return errh->error("device name '%s' too long", _devname.c_str()); + + /* + * Check for duplicate attachment + */ + void *&used = router()->force_attachment("FromHost_" + _devname); + if(used) + return errh->error("duplicate FromHost for device '%s'", + _devname.c_str()); + + used = this; + + NET_LOCK_GIANT(); + /* + * Check for existing device. + */ + _dev = ifunit(_devname.c_str()); + if(_dev != NULL) { + if(_dev->if_init != fh_eiface_init) { + _dev = NULL; + NET_UNLOCK_GIANT(); + return errh->error("device '%s' already exists", + _devname.c_str()); + } + else { + fromhost_map.insert(this); + NET_UNLOCK_GIANT(); + return 0; + } + } + + _dev = new_device(_devname.c_str()); + if(_dev == NULL) + return errh->error("out of memory!"); + + NET_UNLOCK_GIANT(); + + fromhost_map.insert(this); + + return 0; } int FromHost::initialize(ErrorHandler *errh) { - // create queue - int s = splimp(); - if (device()->if_poll_xmit != NULL) { - splx(s); - click_chatter("FromHost: %s%d already in use", - device()->if_name, device()->if_unit); - return -1; - } - (FromHost *)(device()->if_poll_xmit) = this; - _inq = (struct ifqueue *) - malloc(sizeof (struct ifqueue), M_DEVBUF, M_NOWAIT|M_ZERO); - assert(_inq); - _inq->ifq_maxlen = QSIZE; - ScheduleInfo::initialize_task(this, &_task, true, errh); - splx(s); - return 0; + int error; + struct ifreq ifr; + + ScheduleInfo::initialize_task(this, &_task, _dev != NULL, errh); + _nonfull_signal = Notifier::downstream_full_signal(this, 0, &_task); + + NET_LOCK_GIANT(); + + if(_dev->if_flags & IFF_UP) { + _wakeup_timer.initialize(this); + _wakeup_timer.schedule_now(); + NET_UNLOCK_GIANT(); + return 0; + } + + /* + * We need to fake the interface up process. + * This code is derived from the nfsclient/bootp_subr.c + */ + error = set_device_addresses(errh); + if(error != 0) { + NET_UNLOCK_GIANT(); + return -1; + } + + /* + * Set the device up. + */ + ifr.ifr_flags = _dev->if_flags; + ifr.ifr_flags |= IFF_UP; + error = fh_eiface_ioctl(_dev, SIOCSIFFLAGS, (caddr_t)&ifr); + NET_UNLOCK_GIANT(); + return error; } + void FromHost::cleanup(CleanupStage) { - if (!_inq) - return; - - // Flush the receive queue. - int s = splimp(); - struct ifqueue *q = _inq ; - _inq = NULL; - device()->if_poll_xmit = NULL; - splx(s); - - int i, max = q->ifq_maxlen ; - for (i = 0; i < max; i++) { - struct mbuf *m; - IF_DEQUEUE(q, m); - if (!m) - break; - m_freem(m); - } - free(q, M_DEVBUF); + NET_LOCK_GIANT(); + fromhost_map.remove(this); + + lock_acquire(); + if(_queue != NULL) { + _queue->kill(); + _queue = NULL; + } + lock_release(); + + if(_dev) { + if(fromhost_map.lookup(_dev, 0) != NULL) { + _dev = NULL; + } + else { + if(_dev->if_flags & IFF_UP) { + struct ifreq ifr; + ifr.ifr_flags = _dev->if_flags; + ifr.ifr_flags &= ~IFF_UP; + fh_eiface_ioctl(_dev, SIOCSIFFLAGS, (caddr_t)&ifr); + } + + /* + * Unregister the device + */ + ether_ifdetach(_dev); + click_chatter("Freeing one unit"); + free_unr(fh_unit, _unit); + if_free(_dev); + _dev = NULL; + } + } + + NET_UNLOCK_GIANT(); +} + +static void +fh_wakeup(Timer *, void *thunk) +{ + FromHost *fh = (FromHost *)thunk; + struct ifreq ifr; + struct ifnet *ifp = fh->device(); + + PrefixErrorHandler errh(ErrorHandler::default_handler(), + fh->declaration() + ":"); + + NET_LOCK_GIANT(); + + if(ifp->if_flags & IFF_UP) { + ifr.ifr_flags = ifp->if_flags; + ifr.ifr_flags &= ~IFF_UP; + fh_eiface_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); + } + + fh->set_device_addresses(&errh); + + ifr.ifr_flags = ifp->if_flags; + ifr.ifr_flags |= IFF_UP; + fh_eiface_ioctl(ifp, SIOCSIFFLAGS, (caddr_t)&ifr); + + NET_UNLOCK_GIANT(); } bool FromHost::run_task() { - int npq = 0; - // click_chatter("FromHost::run_task()."); - while (npq < _burst) { - struct mbuf *m = 0; - IF_DEQUEUE(_inq, m); - if (m == NULL) { - return npq > 0; - } - - // Got an mbuf, including the MAC header. Make it a real Packet. - Packet *p = Packet::make(m); - output(0).push(p); - npq++; + int npq = 0; + bool retval = false; + + if(!_nonfull_signal) + return retval; + + NET_LOCK_GIANT(); + + lock_acquire(); + Packet *p = _queue; + if(p != NULL) { + _queue = NULL; + lock_release(); + output(0).push(p); + _dev->if_drv_flags &= ~IFF_DRV_OACTIVE; + retval = true; } -#if CLICK_DEVICE_ADJUST_TICKETS - adjust_tickets(npq); -#endif - _task.fast_reschedule(); - return true; + + NET_UNLOCK_GIANT(); + return retval; } ELEMENT_REQUIRES(AnyDevice bsdmodule) diff --git a/elements/bsdmodule/fromhost.hh b/elements/bsdmodule/fromhost.hh index d290631..42de8f4 100644 --- a/elements/bsdmodule/fromhost.hh +++ b/elements/bsdmodule/fromhost.hh @@ -5,12 +5,15 @@ #include #include "elements/bsdmodule/anydevice.hh" -class FromHost : public AnyDevice { +class FromHost : public AnyTaskDevice { public: FromHost(); ~FromHost(); + static void static_initialize(); + static void static_cleanup(); + const char *class_name() const { return "FromHost"; } const char *port_count() const { return PORTS_0_1; } const char *processing() const { return PUSH; } @@ -20,11 +23,30 @@ class FromHost : public AnyDevice { int initialize(ErrorHandler *); void cleanup(CleanupStage); bool run_task(); - struct ifqueue *_inq; + + //int got_mbuf(struct mbuf **); + int set_device_addresses(ErrorHandler *); + void start(struct ifnet *); + + void lock_acquire(void); + void lock_release(void); + + private: - static const int QSIZE = 511; - unsigned _burst; + int _unit; + EtherAddress _macaddr; + IPAddress _destaddr; + IPAddress _destmask; + Timer _wakeup_timer; + + NotifierSignal _nonfull_signal; + + Mutex _fh_lock; + Packet *_queue; + struct ifnet *new_device(const char *); + static const int QSIZE = 1; + unsigned _burst; }; #endif diff --git a/elements/bsdmodule/polldevice.cc b/elements/bsdmodule/polldevice.cc new file mode 100644 index 0000000..2f34ef6 --- /dev/null +++ b/elements/bsdmodule/polldevice.cc @@ -0,0 +1,371 @@ + +#include +#include +#include "polldevice.hh" +#include "fromdevice.hh" +#include "todevice.hh" +#include +#include +#include +#include + +#include +CLICK_CXX_PROTECT +#include + +CLICK_CXX_UNPROTECT +#include + +static AnyDeviceMap poll_device_map; +static eventhandler_tag attach_notifier = NULL; +static eventhandler_tag detach_notifier = NULL; + +extern "C" { + +static void +attach_notifier_hook(void *arg __unused, struct ifnet *ifp) +{ + Vector es; + + poll_device_map.lookup_all(ifp, false, es); + + for(int i = 0; i < es.size(); i++) + ((PollDevice *)(es[i]))->set_device(ifp, &poll_device_map); +} + +static void +detach_notifier_hook(void *arg __unused, struct ifnet *ifp) +{ + Vector es; + + poll_device_map.lookup_all(ifp, true, es); + + for(int i = 0; i < es.size(); i++) + ((PollDevice *)(es[i]))->set_device(NULL, &poll_device_map); +} + +} + +void +PollDevice::static_initialize(void) +{ + poll_device_map.initialize(); + + attach_notifier = EVENTHANDLER_REGISTER(ifnet_arrival_event, + attach_notifier_hook, NULL, + EVENTHANDLER_PRI_ANY); + + detach_notifier = EVENTHANDLER_REGISTER(ifnet_departure_event, + detach_notifier_hook, NULL, + EVENTHANDLER_PRI_ANY); +} + +void +PollDevice::static_cleanup(void) +{ + if(attach_notifier != NULL) + EVENTHANDLER_DEREGISTER(ifnet_arrival_event, attach_notifier); + + if(detach_notifier != NULL) + EVENTHANDLER_DEREGISTER(ifnet_departure_event, detach_notifier); +} + +PollDevice::PollDevice() +{ +} + +PollDevice::~PollDevice() +{ +} + +/* + * We can poll this interface. The following sets up polling for + * the interface: + * + * 1. Check from the kern_poll interface if the device is already + * registered and get the poll_handler_t *. + * 2. Save the poll_handler_t in our device. + * 3. Deregister polling from the device. + * 4. Register polling with our poll handler. + * 5. In our polling handler, call the original (saved) handler. + * + * Why do we need to do this? + * Basically, our goal is to control when the polling happens on the + * device. Ideally, we will always be scheduled so we will call the + * task all the time. Unlike the polling handler though, we will schedule + * according to the Click algorithm, with just the trigger being from the + * OS. + * + * If the OS allows an interface + * ether_replace_handler(ifp, pollhandler_t *, pollhandler_t **); + * + * Our hack will easily be replaced. + * + */ +int +PollDevice::update_pollhandler(ErrorHandler *errh) +{ + struct ifnet *ifp = device(); + + NET_LOCK_GIANT(); + /* + * Check if we are already polling. + * Otherwise, we need to call the ioctl for the device to + * disable polling. + */ + if(!(ifp->if_capenable & IFF_POLLING)) { + struct ifreq ifr; + + ifr.ifr_reqcap = ifp->if_capenable; + ifr.ifr_reqcap |= IFF_POLLING; + + IFF_LOCKGIANT(ifp); + error = *ifp->if_ioctl(ifp, SIOCSIFCAP, (caddr_t)&ifr); + if(error != 0) { + IFF_UNLOCKGIANT(ifp); + NET_UNLOCK_GIANT(); + + errh->error("device '%s' cannot enable polling", + _devname.c_str()); + return error; + } + } + + IFF_LOCK_GIANT(ifp); + /* + * At this point we are polling. + * So, go through the polling handlers and get the one for us. + * + * Since we are not going to touch any driver specific state, + * we do not need to lock the device and therefore do not need + * to go through an ioctl. + */ + error = ether_poll_switch(ifp, click_poll_handler, &_ifp_poll_handler); + if(error != 0) { + IFF_UNLOCK_GIANT(ifp); + NET_UNLOCK_GIANT(); + errh->error("Could not switch handlers for '%s'", + _devname.c_str()); + return error; + } + IFF_UNLOCK_GIANT(ifp); + NET_UNLOCK_GIANT(); + return 0; +} + +int +PollDevice::configure(Vector &conf, ErrorHandler *errh) +{ + bool promisc = false; + bool allow_nonexistent = false; + + _burst = 8; + + if(cp_va_parse(conf, this, errh, + cpString, "device name", &_devname, + cpOptional, + cpBool, "enter promiscuous mode?", &promisc, + cpUnsigned, "burst size", &_burst, + cpKeywords, + "PROMISC", cpBool, "enter promiscuous mode?", &promisc, + "PROMISCUOUS", cpBool, "enter promiscuous mode?", &promisc, + "BURST", cpUnsigned, "burst size", &_burst, + "ALLOW_NONEXISTENT", cpBool, "allow nonexistent device?", &allow_nonexistent, + cpEnd) < 0) { + return -1; + } + + if(promisc) + set_promisc(); + + if(find_device(allow_nonexistent, &poll_device_map, errh) < 0) + return -1; + +#ifdef DEVICE_POLLING + if(_dev && (!(_dev->if_capabilities & IFCAP_POLLING))) + return errh->error("dev '%s' not pollable, use FromDevice", + _devname.c_str()); + +#endif + return 0; +} + +int +PollDevice::initialize(ErrorHandler *errh) +{ +#ifdef DEVICE_POLLING + if(ifindex() >= 0) { + void *&used = router()->force_attachment("device_reader_" + + String(ifindex())); + + if(used != NULL) + return errh->error("duplicate reader for device '%s'", + _devname.c_str()); + + /* + * XXX: This is not true for FreeBSD I think. + * + * Since the tx and rx are combined in the FreeBSD poll routine, + * there is no difference if the ToDevice is added or not. + * + * The if_snd queue will be checked in the driver's poll handler + * and drained if there are packets there. + * + */ + if(!router->attachment("device_writer_" + String(ifindex()))) + errh->warning("no ToDevice(%s) in configuration\n(\ + Generally, you will get bad performance\ + from PollDevice unless\n\ + you include a ToDevice for the same device.\ + Try adding\n\ + 'Idle -> ToDevice(%s)'\ + to your configuration.)", + _devname.c_str(), + _devname.c_str()); + + } + + if(update_pollhandler() < 0) + return -1; + + ScheduleInfo::initialize_task(this, &_task, _dev != NULL, errh); +#ifdef HAVE_STRIDE_SCHED + _max_tickets = _task.tickets(); + _task.set_tickets(Task::DEFAULT_TICKETS); +#endif + + reset_counts(); +#else + errh->warning("can't get packets: not compiled with DEVICE_POLLING"); +#endif + + return 0; +} + +/* + * XXX: Where are we called from? + */ +void +PollDevice::run_task() +{ + +} + +#if 0 +#define POLL_LIST_LEN 128 // XXX should refernce a proper #include + +struct pollrec { + poll_handler_t *handler; + struct ifnet *ifp; +}; + +int *polling; // polling mode +static int *poll_handlers; // # of NICs registered for BSD kernel polling +static int *reg_frac; // How often we have to check status registers +static struct pollrec *pr; // BSD kernel polling handlers + +static void +fromdev_static_initialize() +{ + linker_file_t kernel_lf = linker_find_file_by_id(1); /* kernel file */ + + pr = (struct pollrec *) linker_file_lookup_symbol(kernel_lf, "pr", 0); + reg_frac = (int *) linker_file_lookup_symbol(kernel_lf, "reg_frac", 0); + poll_handlers = (int *) + linker_file_lookup_symbol(kernel_lf, "poll_handlers", 0); + if (pr && poll_handlers && reg_frac) + polling = (int *) linker_file_lookup_symbol(kernel_lf, "polling", 0); + else + polling = NULL; + + if (polling) + printf("Cool, we are running Click on a polling capable kernel!\n"); + + if (++from_device_count == 1) + from_device_map.initialize(); +} + + +static void +fromdev_static_cleanup() +{ + if (--from_device_count <= 0) { + if (registered_readers) + printf("Warning: registered reader count mismatch!\n"); + } +} + +bool +FromDevice::run_task() +{ + int npq = 0; + // click_chatter("FromDevice::run_task()."); + +#if 0 /* XXX: See polling(4). -bms */ + if (_dev && _polling == 1) { + if (_dev->if_ipending & IFF_POLLING) { + enum poll_cmd cmd; + + if ( _poll_status_tick <= ticks ) { + _poll_status_tick = ticks + *reg_frac; + cmd = POLL_AND_CHECK_STATUS; + } else + cmd = POLL_ONLY; + + if ( *polling != 2 ) { // Need to return the handler to the kernel + struct pollrec *prp = pr; + int i; + _polling = 0; // No more polling in Click + for (i = 0; i < *poll_handlers; i++, prp++) + if (prp->ifp == NULL) + break; + prp->handler = _poll_handler; + prp->ifp = _dev; + if (*poll_handlers == 0 && + (_dev->if_drv_flags & IFF_DRV_RUNNING)) + *poll_handlers = 1; + } else + _poll_handler(_dev, cmd, _burst); + } else + _polling = 0; // No more polling + } +#endif + +#ifdef FROMDEVICE_TSTAMP + if (_tstamp) { + uint64_t now = rdtsc(); + click_chatter("FromDevice::run_task() latency=%ld", now - _tstamp); + _tstamp = 0; + } +#endif + + while (npq <= _burst) { + struct mbuf *m = 0; + + // Try to dequeue a packet from the interrupt input queue. + IF_DEQUEUE(_inq, m); + if (m == NULL) { +#if CLICK_DEVICE_ADJUST_TICKETS + adjust_tickets(npq); +#endif + if (_polling) + _task.fast_reschedule(); + return npq > 0; + } + + // Got a packet, which includes the MAC header. Make it a real Packet. + + Packet *p = Packet::make(m); + output(0).push(p); + npq++; + _npackets++; + } +#if CLICK_DEVICE_ADJUST_TICKETS + adjust_tickets(npq); +#endif +//printf("fromdevice couldn't handle all packets\n"); + _task.fast_reschedule(); + return true; +} + +#endif diff --git a/elements/bsdmodule/todevice.cc b/elements/bsdmodule/todevice.cc index 2778acb..2be35d5 100644 --- a/elements/bsdmodule/todevice.cc +++ b/elements/bsdmodule/todevice.cc @@ -28,49 +28,109 @@ #include #include +#include +CLICK_CXX_PROTECT +#include +#include +#include +#include +#include +#include +#include +#include +CLICK_CXX_UNPROTECT +#include + +#include +#include + +#include "click_ether.hh" /* for watching when devices go offline */ static AnyDeviceMap to_device_map; + static int to_device_count; -#if 0 /* Not yet in BSD XXX */ -static struct notifier_block device_notifier; + extern "C" { -static int device_notifier_hook(struct notifier_block *nb, unsigned long val, void *v); +static eventhandler_tag attach_notifier = NULL; +static eventhandler_tag detach_notifier = NULL; + +static void +attach_notifier_hook(void *arg __unused, struct ifnet *ifp) +{ + Vector es; + + NET_LOCK_GIANT(); + IFF_LOCKGIANT(ifp); + + to_device_map.lookup_all(ifp, false, es); + for(int i = 0; i < es.size(); i++) + ((ToDevice *)(es[i]))->set_device(ifp, &to_device_map); + + IFF_UNLOCKGIANT(ifp); + NET_UNLOCK_GIANT(); + } -#endif static void -todev_static_initialize() -{ - if (++to_device_count == 1) { - to_device_map.initialize(); -#if 0 /* XXX not yet in BSD XXX */ - device_notifier.notifier_call = device_notifier_hook; - device_notifier.priority = 1; - device_notifier.next = 0; - register_netdevice_notifier(&device_notifier); -#endif - } +detach_notifier_hook(void *arg __unused, struct ifnet *ifp) +{ + Vector es; + + NET_LOCK_GIANT(); + IFF_LOCKGIANT(ifp); + + to_device_map.lookup_all(ifp, true, es); + for(int i = 0; i < es.size(); i++) + ((ToDevice *)(es[i]))->set_device(NULL, &to_device_map); + + IFF_UNLOCKGIANT(ifp); + NET_UNLOCK_GIANT(); + +} + } +/*************************** Initialization *******************************/ static void -todev_static_cleanup() +ToDevice::static_initialize() { - if (--to_device_count <= 0) { -#if 0 /* XXX not yet in BSD */ - unregister_netdevice_notifier(&device_notifier); -#endif - } + to_device_map.initialize("ToDeviceLock", NULL); + + attach_notifier = EVENTHANDLER_REGISTER(ifnet_arrival_event, + attach_notifier_hook, NULL, + EVENTHANDLER_PRI_ANY); + + detach_notifier = EVENTHANDLER_REGISTER(ifnet_departure_event, + detach_notifier_hook, NULL, + EVENTHANDLER_PRI_ANY); +} + +static void +ToDevice::static_cleanup() +{ + if(attach_notifier != NULL) + EVENTHANDLER_DEREGISTER(ifnet_arrival_event, attach_notifier); + + if(detach_notifier != NULL) + EVENTHANDLER_DEREGISTER(ifnet_departure_event, detach_notifier); + + to_device_map.cleanup(); } ToDevice::ToDevice() + : _dev_idle(0), _rejected(0), _hard_start(0), _no_pad(false) { - todev_static_initialize(); } ToDevice::~ToDevice() { - todev_static_cleanup(); +} + +void +ToDevice::tx_wake_queue(struct ifnet *dev) +{ + _task.reschedule(); } @@ -86,96 +146,239 @@ ToDevice::configure(Vector &conf, ErrorHandler *errh) cpKeywords, "BURST", cpUnsigned, "burst size", &_burst, "ALLOW_NONEXISTENT", cpBool, "allow nonexistent interface?", &allow_nonexistent, + "NO_PAD", cpBool, "don't pad packets to 60 bytes?", &_no_pad, cpEnd) < 0) return -1; - if (find_device(allow_nonexistent, &to_device_map, errh) < 0) - return -1; - return 0; + return find_device(allow_nonexistent, &to_device_map, errh); + } int ToDevice::initialize(ErrorHandler *errh) { - to_device_map.insert(this); - - ScheduleInfo::initialize_task(this, &_task, device() != 0, errh); - _signal = Notifier::upstream_empty_signal(this, 0, &_task); + if(ifindex() >= 0) { + void *&used = router()->force_attachment("device_writer_" + + String(ifindex())); + + if(used != NULL) + return errh->error("duplicate writer for device '%s'", + _devname.c_str()); + + used = this; + } + + ScheduleInfo::initialize_task(this, &_task, device() != 0, errh); + _signal = Notifier::upstream_empty_signal(this, 0, &_task); #ifdef HAVE_STRIDE_SCHED - // start out with max number of tickets - set_max_tickets( _task.tickets() ); - _task.set_tickets(Task::DEFAULT_TICKETS); + /* User specified maximum tickets; we start with default. */ + _max_tickets = _task.tickets(); + _task.set_tickets(Task::DEFAULT_TICKETS); #endif - reset_counts(); - return 0; + reset_counts(); + return 0; } void ToDevice::reset_counts() { - _npackets = 0; - _busy_returns = 0; + _npackets = 0; + _busy_returns = 0; + _too_short = 0; + _runs = 0; + _pulls = 0; + +#if CLICK_DEVICE_STATS + _activations = 0; + _time_clean = 0; + _time_freembuf = 0; + _time_queue = 0; + _perfcnt1_pull = 0; + _perfcnt1_clean = 0; + _perfcnt1_freembuf = 0; + _perfcnt1_queue = 0; + _perfcnt2_pull = 0; + _perfcnt2_clean = 0; + _perfcnt2_freembuf = 0; + _perfcnt2_queue = 0; + _pull_cycles = 0; +#endif } void ToDevice::cleanup(CleanupStage) { - to_device_map.remove(this); + clear_device(&to_device_map); } +/* + * XXX:bohra + * + * This needs to be carefully rewritten. + * + * Difference between Linux Click polling and FreeBSD polling. + * + * In Linux click polling, the click threads take over the polling responsibility. + * The transmit and receive part of the polling routine is separate and the + * skbs are managed by Click itself. + * + * On the other hand, in FreeBSD, all polling is done by the OS polling + * mechanism. The Click threads do not handle polling and do not need + * to clean up the tx ring. + */ bool -ToDevice::run_task() +ToDevice::run_task(void) { - int busy; - int sent = 0; - // click_chatter("ToDevice::run_task()."); + int busy = 0; + int sent = 0; + struct ifnet *ifp = device(); - while (sent < _burst && (busy = IF_QFULL(&device()->if_snd)) == 0) { + /* + * When we are checking for busy for the first time, + * we need to lock the send queue. + * + * After the initial check, the ether_output_frame() + * frees the mbuf and returns ENOBUFS if the queue is + * full. + * + * We account for drops in both cases. + */ - Packet *p = input(0).pull(); - if (!p) - break; - _npackets++; - struct mbuf *m = p->steal_m(); - if (ether_output_frame(device(), m) != 0) - break; - sent++; - } + NET_LOCK_GIANT(); + IFF_LOCKGIANT(ifp); - if (busy) - _busy_returns++; + IF_LOCK(&ifp->if_snd); + busy = _IF_QFULL(&ifp->if_snd); + IF_UNLOCK(&ifp->if_snd); + + while(sent < _burst && busy == 0) { -#if 0 - adjust_tickets(sent); -#endif - if (sent > 0 || busy || _signal) - _task.fast_reschedule(); - return sent > 0; + _pulls++; + + Packet *p = input(0).pull(); + if(p == NULL) + break; + + struct mbuf *m = p->steal_m(); + busy = ether_output_frame(ifp, m); + if(busy != 0) { + _ndrops++; + break; + } + + _npackets++; + sent++; + } + + IFF_UNLOCKGIANT(ifp); + NET_UNLOCK_GIANT(); + + if(busy != 0) + _busy_returns++; + + adjust_tickets(sent); + + if(busy || sent > 0 || _signal.active()) + _task.fast_reschedule(); + + return sent > 0; } + +void +ToDevice::change_device(struct ifnet *dev) +{ + struct ifnet *ifp = device(); + + _task.strong_unschedule(); + + NET_LOCK_GIANT(); + + if(dev != NULL) + IFF_LOCKGIANT(dev); + + if(ifp != NULL) + IFF_LOCKGIANT(ifp); + + set_device(dev, &to_device_map); + + if(ifp != NULL) + IFF_UNLOCKGIANT(ifp); + + if(dev != NULL) + IFF_UNLOCKGIANT(dev); + + NET_UNLOCK_GIANT(); + + if(_dev != NULL) + _task.strong_reschedule(); +} + + static String -ToDevice_read_calls(Element *f, void *) +ToDevice_read_calls(Element *e, void *) { - ToDevice *td = (ToDevice *)f; - return - String(td->_busy_returns) + " device busy returns\n" + - String(td->_npackets) + " packets sent\n" + - String(); + ToDevice *td = (ToDevice *)e; + StringAccum sa; + + sa << td->_rejected << " packets rejected\n" + << td->_hard_start << " hard start xmit\n" + << td->_busy_returns << " device busy returns\n" + << td->_npackets << " packets sent\n" + << td->_runs << " calls to run_task()\n" +#if CLICK_DEVICE_STATS + << td->_pull_cycles << " cycles pull\n" + << td->_time_clean << " cycles clean\n" + << td->_time_freembuf << " cycles freembuf\n" + << td->_time_queue << " cycles queue\n" + << td->_perfcnt1_pull << " perfctr1 pull\n" + << td->_perfcnt1_clean << " perfctr1 clean\n" + << td->_perfcnt1_freembuf << " perfctr1 freembuf\n" + << td->_perfcnt1_queue << " perfctr1 queue\n" + << td->_perfcnt2_pull << " perfctr2 pull\n" + << td->_perfcnt2_clean << " perfctr2 clean\n" + << td->_perfcnt2_freembuf << " perfctr2 freembuf\n" + << td->_perfcnt2_queue << " perfctr2 queue\n" +#endif + << "\n"; + + return sa.take_string(); } +enum { H_COUNT, H_DROPS, H_PULL_CYCLES, H_TIME_QUEUE, H_TIME_CLEAN }; + static String ToDevice_read_stats(Element *e, void *thunk) { - ToDevice *td = (ToDevice *)e; - int which = reinterpret_cast(thunk); - switch (which) { - case 0: - return String(td->_npackets); - default: - return String(); - } + ToDevice *td = (ToDevice *)e; + String retstr = String(); + + uintptr_t which = reinterpret_cast(thunk); + switch(which) { + case H_COUNT: + retstr = String(td->_npackets); + break; + case H_DROPS: + retstr = String(td->_rejected + td->_too_short); + break; +#if CLICK_DEVICE_STATS + case H_PULL_CYCLES: + retstr = String(td->_pull_cycles); + break; + case H_TIME_QUEUE: + retstr = String(td->_time_queue); + break; + case H_TIME_CLEAN: + retstr = String(td->_time_clean); + break; +#endif + default: + break; + } + + return retstr; } static int @@ -189,10 +392,18 @@ ToDevice_write_stats(const String &, Element *e, void *, ErrorHandler *) void ToDevice::add_handlers() { - add_read_handler("calls", ToDevice_read_calls, 0); - add_read_handler("packets", ToDevice_read_stats, 0); - add_write_handler("reset_counts", ToDevice_write_stats, 0); - add_task_handlers(&_task); + add_read_handler("calls", ToDevice_read_calls, 0); + add_read_handler("count", ToDevice_read_stats, (void *)H_COUNT); + add_read_handler("drops", ToDevice_read_stats, (void *)H_DROPS); + // XXX deprecated + add_read_handler("packets", ToDevice_read_stats, (void *)H_COUNT); +#if CLICK_DEVICE_STATS + add_read_handler("pull_cycles", ToDevice_read_stats, (void *)H_PULL_CYCLES); + add_read_handler("enqueue_cycles", ToDevice_read_stats, (void *)H_TIME_QUEUE); + add_read_handler("clean_dma_cycles", ToDevice_read_stats, (void *)H_TIME_CLEAN); +#endif + add_write_handler("reset_counts", ToDevice_write_stats, 0); + add_task_handlers(&_task); } ELEMENT_REQUIRES(AnyDevice bsdmodule) diff --git a/elements/bsdmodule/todevice.hh b/elements/bsdmodule/todevice.hh index 7523061..85cf77c 100644 --- a/elements/bsdmodule/todevice.hh +++ b/elements/bsdmodule/todevice.hh @@ -67,17 +67,21 @@ Resets C counter to zero when written. #include "elements/bsdmodule/anydevice.hh" #include -class ToDevice : public AnyDevice { +class ToDevice : public AnyTaskDevice { public: ToDevice(); ~ToDevice(); + static void static_initialize(); + static void static_cleanup(); + const char *class_name() const { return "ToDevice"; } const char *port_count() const { return PORTS_1_0; } const char *processing() const { return PULL; } + int configure_phase() const { return CONFIGURE_PHASE_TODEVICE; } int configure(Vector &, ErrorHandler *); int initialize(ErrorHandler *); @@ -87,15 +91,44 @@ class ToDevice : public AnyDevice { bool run_task(); void reset_counts(); + void change_device(struct ifnet *); + void tx_wake_queue(struct ifnet *); + bool tx_intr(void); + +#if CLICK_DEVICE_STATS + uint64_t _time_clean; + uint64_t _time_freembuf; + uint64_t _time_queue; + uint64_t _perfcnt1_pull; + uint64_t _perfcnt1_clean; + uint64_t _perfcnt1_freembuf; + uint64_t _perfcnt1_queue; + uint64_t _perfcnt2_pull; + uint64_t _perfcnt2_clean; + uint64_t _perfcnt2_freembuf; + uint64_t _perfcnt2_queue; + uint64_t _pull_cycles; +#endif - unsigned _npackets; - unsigned long _busy_returns; + uint32_t _runs; + uint32_t _ndrops; + uint32_t _pulls; + uint32_t _npackets; + uint32_t _pull_cycles; + uint32_t _rejected; + uint32_t _hard_start; + uint32_t _busy_returns; + uint32_t _too_short; + + bool polling() const { return _dev && _dev->if_capenable & IFCAP_POLLING; } private: - unsigned _burst; - NotifierSignal _signal; - + unsigned _burst; + int _dev_idle; + NotifierSignal _signal; + bool _no_pad; + int queue_packet(Packet *p); }; #endif diff --git a/elements/bsdmodule/tohost.cc b/elements/bsdmodule/tohost.cc index 817a718..385e99b 100644 --- a/elements/bsdmodule/tohost.cc +++ b/elements/bsdmodule/tohost.cc @@ -23,34 +23,88 @@ #include #include +#include +CLICK_CXX_PROTECT +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +CLICK_CXX_UNPROTECT +#include + // for watching when devices go offline -static AnyDeviceMap to_host_map; -#if 0 /* MARKO XXX not yet in BSD */ -static struct notifier_block device_notifier; +static struct mtx tohost_map_lock; +static AnyDeviceMap to_host_map; + + extern "C" { -static int device_notifier_hook(struct notifier_block *nb, unsigned long val, vo -id *v); + +static eventhandler_tag attach_notifier = NULL; +static eventhandler_tag detach_notifier = NULL; + +static void +attach_notifier_hook(void *arg __unused, struct ifnet *ifp) +{ + Vector es; + NET_LOCK_GIANT(); + + to_host_map.lookup_all(ifp, false, es); + for(int i = 0; i < es.size(); i++) + ((ToHost *)(es[i]))->set_device(ifp, &to_host_map); + + NET_UNLOCK_GIANT(); +} + +static void +detach_notifier_hook(void *arg __unused, struct ifnet *ifp) +{ + Vector es; + + NET_LOCK_GIANT(); + + to_host_map.lookup_all(ifp, true, es); + for(int i = 0; i < es.size(); i++) + ((ToHost *)(es[i]))->set_device(NULL, &to_host_map); + + NET_UNLOCK_GIANT(); +} + } -#endif void ToHost::static_initialize() { - to_host_map.initialize(); -#if 0 /* MARKO XXX not yet ready */ - device_notifier.notifier_call = device_notifier_hook; - device_notifier.priority = 1; - device_notifier.next = 0; - register_netdevice_notifier(&device_notifier); -#endif + to_host_map.initialize("ToHostMapLock", NULL); + + attach_notifier = EVENTHANDLER_REGISTER(ifnet_arrival_event, + attach_notifier_hook, NULL, + EVENTHANDLER_PRI_ANY); + + detach_notifier = EVENTHANDLER_REGISTER(ifnet_departure_event, + detach_notifier_hook, NULL, + EVENTHANDLER_PRI_ANY); } void ToHost::static_cleanup() { -#if 0 /* MARKO XXX */ - unregister_netdevice_notifier(&device_notifier); -#endif + if(attach_notifier != NULL) + EVENTHANDLER_DEREGISTER(ifnet_arrival_event, attach_notifier); + + if(detach_notifier != NULL) + EVENTHANDLER_DEREGISTER(ifnet_departure_event, detach_notifier); + + to_host_map.cleanup(); } ToHost::ToHost() @@ -79,43 +133,103 @@ ToHost::configure(Vector &conf, ErrorHandler *errh) int ToHost::initialize(ErrorHandler *errh) { - // We find the device here, rather than in 'initialize', to avoid warnings - // about "device down" with FromHost devices -- FromHost brings up its - // device during initialize(). - return (_devname ? find_device(_allow_nonexistent, &to_host_map, errh) : 0); + /* + * We find the device here, rather than in 'initialize', to avoid + * warnings about "device down" with FromHost devices -- FromHost + * brings up its device during initialize(). + */ + + int error = 0; + + NET_LOCK_GIANT(); + + if(_devname) + error = find_device(_allow_nonexistent, &to_host_map, errh); + + NET_UNLOCK_GIANT(); + + return error; } void ToHost::cleanup(CleanupStage) { - clear_device(&to_host_map); + NET_LOCK_GIANT(); + clear_device(&to_host_map); + NET_UNLOCK_GIANT(); } void ToHost::push(int, Packet *p) { - struct ifnet *ifp = _dev; - struct mbuf *m = p->steal_m(); - struct ether_header *eh = mtod(m, struct ether_header *); - if (m == NULL) { - click_chatter("ToHost: steal_m failed"); - return ; - } - - if (!ifp) - ifp = m->m_pkthdr.rcvif; - - // check that device exists - if (!ifp) { - if (++_drops == 1) - click_chatter("%{element}: device not set and packet has rcvif=NULL, dropping", this); - m_freem(m); - return; - } + if(p == NULL) + return; + + struct ifnet *ifp = device(); + struct mbuf *m = p->steal_m(); + if(m == NULL) { + click_chatter("ToHost: steal_m failed"); + return; + } + /* Check length and pull off header */ + if(m->m_pkthdr.len < sizeof(struct ether_header)) { + m_freem(m); + return; + } + + if(m->m_len < sizeof(struct ether_header) && + (m = m_pullup(m, sizeof(struct ether_header))) == NULL) { + m_freem(m); + return; + } + + struct ether_header *eh = mtod(m, struct ether_header *); - m->m_pkthdr.rcvif = NULL; // tell click-ether-input to ignore this - m_adj(m, ETHER_HDR_LEN); - ether_input(ifp, eh, m); + if(ifp == NULL) + ifp = m->m_pkthdr.rcvif; + + if(ifp == NULL) { + if(++_drops == 1) + click_chatter("%{element}: device not set\ + and packet has rcvif == NULL,\ + dropping", this); + + m_freem(m); + return; + } + + m->m_pkthdr.rcvif = ifp; + + /* + * Lock the stack, and the interface + */ + NET_LOCK_GIANT(); + IFF_LOCKGIANT(ifp); + + if(BDG_ACTIVE(ifp)) + m = bridge_in_ptr(ifp, m); + if(m == NULL) { + IFF_UNLOCKGIANT(ifp); + NET_UNLOCK_GIANT(); + return; + } + + if(ifp->if_bridge) { + BRIDGE_INPUT(ifp, m); + if(m == NULL) { + IFF_UNLOCKGIANT(ifp); + NET_UNLOCK_GIANT(); + return; + } + } + + /* + * Packet is routed back in. + */ + ether_demux(ifp, m); + IFF_UNLOCKGIANT(ifp); + NET_UNLOCK_GIANT(); + return; } String diff --git a/elements/bsdmodule/tohost.hh b/elements/bsdmodule/tohost.hh index 082828b..7c5ad22 100644 --- a/elements/bsdmodule/tohost.hh +++ b/elements/bsdmodule/tohost.hh @@ -1,7 +1,13 @@ #ifndef CLICK_TOHOST_HH #define CLICK_TOHOST_HH #include +#include + +CLICK_CXX_PROTECT #include +CLICK_CXX_UNPROTECT +#include + #include "elements/bsdmodule/anydevice.hh" class ToHost : public AnyDevice { diff --git a/elements/grid/filterbyhops.cc b/elements/grid/filterbyhops.cc index ecedef8..859c86b 100644 --- a/elements/grid/filterbyhops.cc +++ b/elements/grid/filterbyhops.cc @@ -24,7 +24,7 @@ #include #include #include "grid.hh" -#include +//#include CLICK_DECLS FilterByGridHops::FilterByGridHops() diff --git a/elements/linuxmodule/anydevice.hh b/elements/linuxmodule/anydevice.hh index 1ee4f80..49a9d12 100644 --- a/elements/linuxmodule/anydevice.hh +++ b/elements/linuxmodule/anydevice.hh @@ -153,7 +153,6 @@ AnyTaskDevice::adjust_tickets(int work) class AnyDeviceMap { public: - void initialize(); AnyDevice *lookup(net_device *, AnyDevice *); AnyDevice *lookup_unknown(net_device *, AnyDevice *); void lookup_all(net_device *, bool known, Vector &); @@ -161,8 +160,10 @@ class AnyDeviceMap { public: void remove(AnyDevice *); void move_to_front(AnyDevice *); - private: + private: + int _lock_opts; + struct mtx _adm_lock; enum { MAP_SIZE = 64 }; AnyDevice *_unknown_map; AnyDevice *_map[MAP_SIZE]; diff --git a/elements/standard/delayshaper.cc b/elements/standard/delayshaper.cc index 4bf0619..9ad698a 100644 --- a/elements/standard/delayshaper.cc +++ b/elements/standard/delayshaper.cc @@ -122,7 +122,7 @@ int DelayShaper::write_param(const String &s, Element *e, void *, ErrorHandler *errh) { DelayShaper *u = (DelayShaper *)e; - if (!cp_time(cp_uncomment(s), &u->_delay)) + if (!cp_timeval(cp_uncomment(s), &u->_delay)) return errh->error("delay must be a timestamp"); return 0; } diff --git a/elements/standard/script.cc b/elements/standard/script.cc index 9afb7e9..dbde176 100644 --- a/elements/standard/script.cc +++ b/elements/standard/script.cc @@ -226,7 +226,7 @@ Script::configure(Vector &conf, ErrorHandler *errh) wait_time: case INSN_WAIT_TIME: { Timestamp ts; - if (cp_time(conf[i], &ts)) + if (cp_timeval(conf[i], &ts)) add_insn(INSN_WAIT_TIME, ts.sec(), ts.subsec()); else goto syntax_error; diff --git a/elements/userlevel/fromdump.cc b/elements/userlevel/fromdump.cc index 1d1a9b4..c95d083 100644 --- a/elements/userlevel/fromdump.cc +++ b/elements/userlevel/fromdump.cc @@ -519,7 +519,7 @@ FromDump::write_handler(const String &s_in, Element *e, void *thunk, ErrorHandle return 0; case H_EXTEND_INTERVAL: { Timestamp ts; - if (cp_time(s, &ts)) { + if (cp_timeval(s, &ts)) { fd->_last_time += ts; if (fd->_end_h) fd->_have_last_time = true, fd->set_active(true); diff --git a/elements/wifi/fromhandler.cc b/elements/wifi/fromhandler.cc index 2d25da4..c06ea9f 100644 --- a/elements/wifi/fromhandler.cc +++ b/elements/wifi/fromhandler.cc @@ -92,7 +92,7 @@ FromHandler::get_packet() int n = s.find_left('|', 0); if (n > 0) { String ts = s.substring(0, n-1); - if (!cp_time(ts, &t)) { + if (!cp_timeval(ts, &t)) { click_chatter("parsing time failed\n"); } s = s.substring(n+2, s.length() - n - 1); diff --git a/include/click/confparse.hh b/include/click/confparse.hh index 4a7de29..5917756 100644 --- a/include/click/confparse.hh +++ b/include/click/confparse.hh @@ -111,8 +111,8 @@ bool cp_double(const String&, double*); bool cp_seconds_as(int want_power, const String&, uint32_t*); bool cp_seconds_as_milli(const String&, uint32_t*); bool cp_seconds_as_micro(const String&, uint32_t*); -bool cp_time(const String&, struct timeval*); -bool cp_time(const String&, Timestamp*); +bool cp_timeval(const String&, struct timeval*); +bool cp_timeval(const String&, Timestamp*); bool cp_bandwidth(const String&, uint32_t*); diff --git a/include/click/master.hh b/include/click/master.hh index b60013e..6d03ee3 100644 --- a/include/click/master.hh +++ b/include/click/master.hh @@ -54,8 +54,9 @@ class Master { public: simclick_click clickinst() const { return _clickinst; } #endif - void acquire_lock() { _master_lock.acquire(); } - void release_lock() { _master_lock.release(); } + inline void acquire_lock(); + inline void release_lock(); + #if CLICK_DEBUG_MASTER String info() const; @@ -67,7 +68,12 @@ class Master { public: private: +#ifndef CLICK_BSDMODULE Spinlock _master_lock; +#else + Mutex _master_lock; +#endif + volatile int _master_paused; // ROUTERS @@ -88,12 +94,20 @@ class Master { public: // PENDING TASKS Task _task_list; +#ifndef CLICK_BSDMODULE SpinlockIRQ _task_lock; +#else + Mutex _task_lock; +#endif void process_pending(RouterThread*); // TIMERS Vector _timer_heap; - Spinlock _timer_lock; +#ifndef CLICK_BSDMODULE + Spinlock _timer_lock; +#else + Mutex _timer_lock; +#endif void timer_reheapify_from(int, Timer*); #if CLICK_USERLEVEL @@ -153,6 +167,17 @@ class Master { public: friend class Router; }; +inline void +Master::acquire_lock() +{ + _master_lock.acquire(); +} + +inline void +Master::release_lock() +{ + _master_lock.release(); +} inline int Master::nthreads() const diff --git a/include/click/packet.hh b/include/click/packet.hh index f25e891..57e12a0 100644 --- a/include/click/packet.hh +++ b/include/click/packet.hh @@ -271,7 +271,7 @@ class Packet { public: #endif Packet(); - Packet(const Packet &); + //Packet(const Packet &); ~Packet(); Packet &operator=(const Packet &); diff --git a/include/click/routerthread.hh b/include/click/routerthread.hh index 89aced3..11ab30e 100644 --- a/include/click/routerthread.hh +++ b/include/click/routerthread.hh @@ -85,6 +85,11 @@ class RouterThread unsigned _iters_per_timers; unsigned _iters_per_os; +#ifdef CLICK_BSDMODULE + int cpuid() const { return _cpuid; } + void set_cpuid(int id) { _cpuid = id; } +#endif + private: #ifdef HAVE_TASK_HEAP @@ -95,7 +100,12 @@ class RouterThread Master *_master; int _id; - Spinlock _lock; +#ifndef CLICK_BSDMODULE + Spinlock _lock; +#else + Mutex _lock; +#endif + atomic_uint32_t _task_lock_waiting; atomic_uint32_t _pending; @@ -105,11 +115,11 @@ class RouterThread #endif #if CLICK_BSDMODULE - // XXX FreeBSD - u_int64_t _old_tsc; /* MARKO - temp. */ - void *_sleep_ident; - int _oticks; - bool _greedy; + uint64_t _old_tsc; /* MARKO - temp. */ + void *_sleep_ident; + int _oticks; + bool _greedy; + int _cpuid; #endif #ifdef HAVE_ADAPTIVE_SCHEDULER diff --git a/include/click/sync.hh b/include/click/sync.hh index 7b932dd..af6092c 100644 --- a/include/click/sync.hh +++ b/include/click/sync.hh @@ -15,15 +15,315 @@ # define num_possible_cpus() smp_num_cpus # endif #endif +#ifdef CLICK_BSDMODULE +#include +CLICK_CXX_PROTECT +#include +#include +#include +#include +#include +#include +#if __FreeBSD_version >= 700000 +#include +#endif +CLICK_CXX_UNPROTECT +#include +#endif + +#include CLICK_DECLS +#if CLICK_BSDMODULE + +static int nspinlocks = 0; + +class Spinlock { + public: + inline Spinlock(); + inline ~Spinlock(); + inline void acquire(); + inline void release(); + inline bool attempt(); + inline bool nested(); + + private: + + struct mtx _click_spinlock; +}; + +inline +Spinlock::Spinlock() +{ + String s("ClickSpinLock"); + s += String(++nspinlocks); + + bzero(&_click_spinlock, sizeof(_click_spinlock)); + mtx_init(&_click_spinlock, s.c_str(), NULL, MTX_SPIN); +} + +inline +Spinlock::~Spinlock() +{ + mtx_destroy(&_click_spinlock); +} + +inline void +Spinlock::acquire() +{ + mtx_lock_spin(&_click_spinlock); +} + +inline void +Spinlock::release() +{ + mtx_unlock_spin(&_click_spinlock); +} + +inline bool +Spinlock::attempt() +{ + /* + * XXX: Trylock on spin mutexes is not supported. + * so, we just call the acquire to get it. + */ + acquire(); + return true; +} + +inline bool +Spinlock::nested() +{ + mtx_assert(&_click_spinlock, MA_OWNED); + if(mtx_recursed(&_click_spinlock) != 0) + return true; + + return false; +} + +class Mutex { + public: + inline Mutex(const char *); + inline Mutex(const char *, int); + inline Mutex(const char *, const char *); + inline ~Mutex(); + + inline void acquire(); + inline bool attempt(); + inline void release(); + inline bool owned(); + + private: + struct mtx _click_mtx; +}; + +inline +Mutex::Mutex(const char *id) +{ + int tid = curthread->td_tid; + String s(id); + + s += String(tid); + + printf("Initializing mutex %s\n", s.c_str()); + + bzero(&_click_mtx, sizeof(_click_mtx)); + mtx_init(&_click_mtx, s.c_str(), NULL, MTX_DEF); +} + +inline +Mutex::Mutex(const char *id, int instanceid) +{ + String s(id); + + s += String(instanceid); + + bzero(&_click_mtx, sizeof(_click_mtx)); + mtx_init(&_click_mtx, s.c_str(), NULL, MTX_DEF); +} + +inline +Mutex::Mutex(const char *id, const char *cl) +{ + int tid = curthread->td_tid; + String s(id); + + s += String(tid); + bzero(&_click_mtx, sizeof(_click_mtx)); + mtx_init(&_click_mtx, s.c_str(), cl, MTX_DEF); +} + +inline +Mutex::~Mutex(void) +{ + mtx_destroy(&_click_mtx); +} + +inline void +Mutex::acquire(void) +{ + mtx_lock(&_click_mtx); +} + +inline void +Mutex::release(void) +{ + mtx_unlock(&_click_mtx); +} + +inline bool +Mutex::attempt(void) +{ + if(mtx_trylock(&_click_mtx) != 0) + return true; + + return false; +} + +inline bool +Mutex::owned(void) +{ + return mtx_owned(&_click_mtx); +} + + +class ReadWriteLock { + public: + + inline ReadWriteLock(); + inline ~ReadWriteLock(); + + inline void acquire_read(); + inline bool attempt_read(); + inline void release_read(); + inline void acquire_write(); + inline bool attempt_write(); + inline void release_write(); + + private: +#if __FreeBSD_version >= 700000 + struct rwlock _click_rwlock; +#else + struct sx _click_rwlock; +#endif + +}; + +#if __FreeBSD_version >= 700000 +inline +ReadWriteLock::ReadWriteLock() +{ + rw_init(&_click_rwlock, "Anonymous Click RWLock"); +} + +inline +ReadWriteLock::~ReadWriteLock() +{ + rw_destroy(&_click_rwlock); +} + +inline void +ReadWriteLock::acquire_read() +{ + rw_rlock(&_click_rwlock); +} + +inline bool +ReadWriteLock::attempt_read() +{ + rw_rlock(&_click_rwlock); + return true; +} + +inline void +ReadWriteLock::release_read() +{ + rw_runlock(&_click_rwlock); +} + +inline void +ReadWriteLock::acquire_write() +{ + rw_wlock(&_click_rwlock); +} + +inline bool +ReadWriteLock::attempt_write() +{ + rw_wlock(&_click_rwlock); + return true; +} + +inline void +ReadWriteLock::release_write() +{ + rw_wunlock(&_click_rwlock); +} +#else +inline +ReadWriteLock::ReadWriteLock() +{ + bzero(&_click_rwlock, sizeof(_click_rwlock)); + sx_init(&_click_rwlock, "Anonymous Click RWLock"); +} + +inline +ReadWriteLock::~ReadWriteLock() +{ + sx_destroy(&_click_rwlock); +} + +inline void +ReadWriteLock::acquire_read() +{ + sx_slock(&_click_rwlock); +} + +inline bool +ReadWriteLock::attempt_read() +{ + sx_slock(&_click_rwlock); + return true; +} + +inline void +ReadWriteLock::release_read() +{ + sx_sunlock(&_click_rwlock); +} + +inline void +ReadWriteLock::acquire_write() +{ + sx_xlock(&_click_rwlock); +} + +inline bool +ReadWriteLock::attempt_write() +{ + sx_xlock(&_click_rwlock); + return true; +} + +inline void +ReadWriteLock::release_write() +{ + sx_xunlock(&_click_rwlock); +} + +#endif + + +#endif + +#if 0 // loop-in-cache spinlock implementation: 8 bytes. if the size of this class // changes, change size of padding in ReadWriteLock below. -#if CLICK_LINUXMODULE && defined(__SMP__) +//#if defined(CLICK_LINUXMODULE) && defined(__SMP__) class Spinlock { public: - + jfdpsfsdf; inline Spinlock(); inline ~Spinlock(); @@ -119,7 +419,7 @@ Spinlock::release() } } -#else /* CLICK_LINUXMODULE && defined(__SMP__) */ +#elif !defined(CLICK_BSDMODULE) class Spinlock { public: @@ -138,7 +438,8 @@ class Spinlock { public: // spinlock that blocks interrupts -#if CLICK_LINUXMODULE +//#if CLICK_LINUXMODULE +#if 0 class SpinlockIRQ { public: @@ -202,10 +503,10 @@ class SpinlockIRQ { public: // that because we'd like to avoid a cache miss for read acquires. this makes // reads very fast, and writes more expensive -#if CLICK_LINUXMODULE && defined(__SMP__) +//#if defined(CLICK_LINUXMODULE) && defined(__SMP__) +#if 0 class ReadWriteLock { public: - inline ReadWriteLock(); inline ~ReadWriteLock(); @@ -289,10 +590,9 @@ ReadWriteLock::release_write() _l[i]._lock.release(); } -#else /* CLICK_LINUXMODULE && defined(__SMP__) */ +#elif !defined(CLICK_BSDMODULE) class ReadWriteLock { public: - ReadWriteLock() { } void acquire_read() { } diff --git a/include/click/task.hh b/include/click/task.hh index 368497a..3c2f0e2 100644 --- a/include/click/task.hh +++ b/include/click/task.hh @@ -9,20 +9,13 @@ #endif CLICK_DECLS -#ifdef CLICK_BSDMODULE -# include -# include /* MARKO XXX */ -#endif - -#if CLICK_BSDMODULE && !BSD_NETISRSCHED -# define SPLCHECK \ - int s = splimp(); \ - if (s == 0) \ - panic("not spl'ed: %d\n", s); \ - splx(s) -#else -# define SPLCHECK -#endif +#include +CLICK_CXX_PROTECT +#include +#include +#include +CLICK_CXX_UNPROTECT +#include #define PASS_GT(a, b) ((int)(a - b) > 0) @@ -147,6 +140,9 @@ class Task { public: inline void fast_unschedule(); +#ifdef CLICK_BSDMODULE + inline void lock_assert(Mutex *); +#endif friend class RouterThread; friend class Master; @@ -318,10 +314,6 @@ Task::fast_unschedule() #if CLICK_LINUXMODULE assert(!in_interrupt()); #endif -#if CLICK_BSDMODULE - // assert(!intr_nesting_level); - SPLCHECK; -#endif if (scheduled()) { #ifdef HAVE_TASK_HEAP Task* back = _thread->_task_heap.back(); @@ -400,10 +392,6 @@ Task::fast_reschedule() // tasks never run at interrupt time in Linux assert(!in_interrupt()); #endif -#if CLICK_BSDMODULE - // assert(!intr_nesting_level); it happens all the time from fromdevice! - SPLCHECK; -#endif if (!scheduled()) { // increase pass @@ -443,7 +431,6 @@ Task::fast_reschedule() inline void Task::fast_schedule() { - SPLCHECK; assert(_tickets >= 1); #if HAVE_TASK_HEAP _pass = (_thread->active() ? _thread->_task_heap[0]->_pass : 0); @@ -463,10 +450,6 @@ Task::fast_reschedule() // tasks never run at interrupt time assert(!in_interrupt()); #endif -#if CLICK_BSDMODULE - // assert(!intr_nesting_level); - SPLCHECK; -#endif if (!scheduled()) { _prev = _thread->_prev; _next = _thread; @@ -494,7 +477,6 @@ Task::fast_schedule() inline void Task::reschedule() { - SPLCHECK; assert(_thread); if (!scheduled()) true_reschedule(); diff --git a/lib/confparse.cc b/lib/confparse.cc index e5bfce6..6baf305 100644 --- a/lib/confparse.cc +++ b/lib/confparse.cc @@ -1303,7 +1303,7 @@ bool cp_seconds_as_micro(const String &str_in, uint32_t *return_value) return cp_seconds_as(6, str_in, return_value); } -bool cp_time(const String &str, Timestamp* return_value) +bool cp_timeval(const String &str, Timestamp* return_value) { int power = 0, factor = 1; const char *after_unit = read_unit(str.begin(), str.end(), seconds_units, sizeof(seconds_units), seconds_prefixes, &power, &factor); @@ -1320,9 +1320,9 @@ bool cp_time(const String &str, Timestamp* return_value) return true; } -bool cp_time(const String &str, timeval *return_value) +bool cp_timeval(const String &str, timeval *return_value) { - return cp_time(str, (Timestamp*) return_value); + return cp_timeval(str, (Timestamp*) return_value); } @@ -2275,7 +2275,7 @@ default_parsefunc(cp_value *v, const String &arg, case cpiTimeval: case cpiInterval: { struct timeval tv; - if (!cp_time(arg, &tv)) { + if (!cp_timeval(arg, &tv)) { if (cp_errno == CPE_NEGATIVE) errh->error("%s (%s) must be >= 0", argname, desc); else diff --git a/lib/glue.cc b/lib/glue.cc index eef3c45..62c2f6b 100644 --- a/lib/glue.cc +++ b/lib/glue.cc @@ -142,7 +142,7 @@ size_t click_dmalloc_maxmem = 0; # define CLICK_ALLOC(size) kmalloc((size), GFP_ATOMIC) # define CLICK_FREE(ptr) kfree((ptr)) # else -# define CLICK_ALLOC(size) malloc((size), M_TEMP, M_WAITOK) +# define CLICK_ALLOC(size) malloc((size), M_TEMP, M_NOWAIT) # define CLICK_FREE(ptr) free(ptr, M_TEMP) # endif @@ -188,6 +188,10 @@ operator new(size_t sz) throw () click_dmalloc_totalnew++; # if CLICK_DMALLOC void *v = CLICK_ALLOC(sz + sizeof(Chunk)); +#ifdef CLICK_BSDMODULE + if(v == NULL) + return NULL; +#endif Chunk *c = (Chunk *)v; c->magic = CHUNK_MAGIC; c->size = sz; @@ -211,6 +215,11 @@ operator new[](size_t sz) throw () click_dmalloc_totalnew++; # if CLICK_DMALLOC void *v = CLICK_ALLOC(sz + sizeof(Chunk)); +#ifdef CLICK_BSDMODULE + if(v == NULL) + return NULL; +#endif + Chunk *c = (Chunk *)v; c->magic = CHUNK_MAGIC; c->size = sz; diff --git a/lib/master.cc b/lib/master.cc index 77c2cd7..2858537 100644 --- a/lib/master.cc +++ b/lib/master.cc @@ -44,6 +44,10 @@ atomic_uint32_t Master::signals_pending; Master::Master(int nthreads) : _master_paused(0), _routers(0), _task_list(0, 0) +#ifdef CLICK_BSDMODULE + , _master_lock("MasterLock"), _task_lock("MasterTaskLock"), + _timer_lock("MasterTimerLock") +#endif { _refcount = 0; _stopper = 0; @@ -198,7 +202,11 @@ Master::kill_router(Router *router) (*tp)->unschedule_router_tasks(router); { +#ifndef CLICK_BSDMODULE SpinlockIRQ::flags_t flags = _task_lock.acquire(); +#else + _task_lock.acquire(); +#endif // Remove pending tasks Task *prev = &_task_list; @@ -214,7 +222,11 @@ Master::kill_router(Router *router) } prev->_pending_next = &_task_list; +#ifndef CLICK_BSDMODULE _task_lock.release(flags); +#else + _task_lock.release(); +#endif } // Remove timers @@ -348,11 +360,19 @@ Master::process_pending(RouterThread *thread) if (_master_lock.attempt()) { if (_master_paused == 0) { // get a copy of the list +#ifndef CLICK_BSDMODULE SpinlockIRQ::flags_t flags = _task_lock.acquire(); +#else + _task_lock.acquire(); +#endif Task *t = _task_list._pending_next; _task_list._pending_next = &_task_list; thread->_pending = 0; +#ifndef CLICK_BSDMODULE _task_lock.release(flags); +#else + _task_lock.release(); +#endif // reverse list so pending tasks are processed in the order we // added them diff --git a/lib/packet.cc b/lib/packet.cc index 5e34522..5b07de1 100644 --- a/lib/packet.cc +++ b/lib/packet.cc @@ -58,7 +58,7 @@ Packet::make(uint32_t headroom, const unsigned char *data, #else /* User-space or BSD kernel module */ -inline +//inline Packet::Packet() { _use_count = 1; diff --git a/lib/router.cc b/lib/router.cc index e2237cd..bf437cf 100644 --- a/lib/router.cc +++ b/lib/router.cc @@ -1256,6 +1256,11 @@ Router::store_handler(const Element* e, const Handler& to_store) const Handler* Router::handler(const Router* r, int hi) { +#if 0 + click_chatter("router = %p, hidx = %d, minglobalh = %d, \ + nglobalh = %d\n", + r, hi, FIRST_GLOBAL_HANDLER, nglobalh); +#endif if (r && hi >= 0 && hi < r->_nhandlers_bufs) return r->xhandler(hi); else if (hi >= FIRST_GLOBAL_HANDLER && hi < FIRST_GLOBAL_HANDLER + nglobalh) diff --git a/lib/routerthread.cc b/lib/routerthread.cc index bdfb4ef..2565ca7 100644 --- a/lib/routerthread.cc +++ b/lib/routerthread.cc @@ -71,6 +71,9 @@ RouterThread::RouterThread(Master *m, int id) #else : Task(Task::error_hook, 0), _master(m), _id(id) #endif +#ifdef CLICK_BSDMODULE + , _lock("RouterThread", id) +#endif { #ifndef HAVE_TASK_HEAP _prev = _next = _thread = this; @@ -316,6 +319,7 @@ RouterThread::task_reheapify_from(int pos, Task* t) inline void RouterThread::run_tasks(int ntasks) { + #if CLICK_DEBUG_SCHEDULING _driver_task_epoch++; click_gettimeofday(&_task_epoch_time[_driver_task_epoch % TASK_EPOCH_BUFSIZ]); @@ -382,11 +386,17 @@ RouterThread::run_tasks(int ntasks) inline void RouterThread::run_os() { + +#ifdef CLICK_BSDMODULE + mtx_assert(&_click_task_lock, MA_OWNED); +#endif + #if CLICK_LINUXMODULE // set state to interruptible early to avoid race conditions _sleeper = current; current->state = TASK_INTERRUPTIBLE; #endif + unlock_tasks(); #if CLICK_USERLEVEL @@ -422,8 +432,12 @@ RouterThread::run_os() if (_greedy) /* do nothing */; else if (active()) { // just schedule others for a moment - yield(curproc, NULL); + yield(curthread, NULL); } else { + /* + * XXX:bohra + * Not a good idea to use tsleep. + */ _sleep_ident = &_sleep_ident; // arbitrary address, != NULL tsleep(&_sleep_ident, PPAUSE, "pause", 1); _sleep_ident = NULL; @@ -471,6 +485,8 @@ RouterThread::driver() // run occasional tasks: timers, select, etc. iter++; + //click_chatter("Running \n"); + #if CLICK_USERLEVEL _master->run_signals(); #endif @@ -503,14 +519,10 @@ RouterThread::driver() _master->process_pending(this); #ifndef HAVE_ADAPTIVE_SCHEDULER + // run a bunch of tasks -# if CLICK_BSDMODULE && !BSD_NETISRSCHED - int s = splimp(); -# endif run_tasks(_tasks_per_iter); -# if CLICK_BSDMODULE && !BSD_NETISRSCHED - splx(s); -# endif + #else /* HAVE_ADAPTIVE_SCHEDULER */ click_gettimeofday(&t_before); int client; @@ -529,11 +541,12 @@ RouterThread::driver() #ifndef BSD_NETISRSCHED // check to see if driver is stopped if (*stopper) { - unlock_tasks(); - bool b = _master->check_driver(); - nice_lock_tasks(); - if (!b) - goto finish_driver; + click_chatter("Stopping!"); + unlock_tasks(); + bool b = _master->check_driver(); + nice_lock_tasks(); + if (!b) + goto finish_driver; } #endif @@ -562,9 +575,6 @@ RouterThread::driver_once() if (!_master->check_driver()) return; -#ifdef CLICK_BSDMODULE /* XXX MARKO */ - int s = splimp(); -#endif lock_tasks(); Task *t = task_begin(); if (t != task_end()) { @@ -572,9 +582,6 @@ RouterThread::driver_once() t->call_hook(); } unlock_tasks(); -#ifdef CLICK_BSDMODULE /* XXX MARKO */ - splx(s); -#endif } void diff --git a/lib/task.cc b/lib/task.cc index 041833a..ca6f255 100644 --- a/lib/task.cc +++ b/lib/task.cc @@ -145,7 +145,11 @@ Task::cleanup() if (_pending) { Master *m = _router->master(); +#ifndef CLICK_BSDMODULE SpinlockIRQ::flags_t flags = m->_task_lock.acquire(); +#else + m->_task_lock.acquire(); +#endif Task *prev = &m->_task_list; for (Task *t = prev->_pending_next; t != &m->_task_list; prev = t, t = t->_pending_next) if (t == this) { @@ -154,7 +158,11 @@ Task::cleanup() } _pending = 0; _pending_next = 0; +#ifndef CLICK_BSDMODULE m->_task_lock.release(flags); +#else + m->_task_lock.release(); +#endif } _router = 0; @@ -165,13 +173,13 @@ Task::cleanup() inline void Task::lock_tasks() { - while (1) { - RouterThread *t = _thread; - t->lock_tasks(); - if (t == _thread) - return; - t->unlock_tasks(); - } + while (1) { + RouterThread *t = _thread; + t->lock_tasks(); + if (t == _thread) + return; + t->unlock_tasks(); + } } inline bool @@ -190,7 +198,11 @@ void Task::add_pending(int p) { Master *m = _router->master(); +#ifndef CLICK_BSDMODULE SpinlockIRQ::flags_t flags = m->_task_lock.acquire(); +#else + m->_task_lock.acquire(); +#endif if (_router->_running >= Router::RUNNING_PREPARING) { _pending |= p; if (!_pending_next && _pending) { @@ -200,7 +212,11 @@ Task::add_pending(int p) if (_pending) _thread->add_pending(); } +#ifndef CLICK_BSDMODULE m->_task_lock.release(flags); +#else + m->_task_lock.release(); +#endif } /** @brief Unschedules the task. @@ -219,10 +235,6 @@ Task::unschedule() #if CLICK_LINUXMODULE assert(!in_interrupt()); #endif -#if CLICK_BSDMODULE - assert(!intr_nesting_level); - SPLCHECK; -#endif if (_thread) { lock_tasks(); fast_unschedule(); @@ -241,11 +253,6 @@ Task::fast_reschedule() // tasks never run at interrupt time assert(!in_interrupt()); #endif -#if CLICK_BSDMODULE - // assert(!intr_nesting_level); it happens all the time from fromdevice! - SPLCHECK; -#endif - if (!scheduled()) { // increase pass _pass += _stride; @@ -271,9 +278,6 @@ Task::true_reschedule() if (in_interrupt()) goto skip_lock; #endif -#if CLICK_BSDMODULE - SPLCHECK; -#endif if (attempt_lock_tasks()) { if (_router->_running >= Router::RUNNING_BACKGROUND) { if (!scheduled()) { @@ -307,10 +311,6 @@ Task::strong_unschedule() #if CLICK_LINUXMODULE assert(!in_interrupt()); #endif -#if CLICK_BSDMODULE - assert(!intr_nesting_level); - SPLCHECK; -#endif // unschedule() and move to a quiescent thread, so that subsequent // reschedule()s won't have any effect if (_thread) { @@ -339,10 +339,6 @@ Task::strong_reschedule() #if CLICK_LINUXMODULE assert(!in_interrupt()); #endif -#if CLICK_BSDMODULE - assert(!intr_nesting_level); - SPLCHECK; -#endif assert(_thread); lock_tasks(); RouterThread *old_thread = _thread; @@ -368,10 +364,6 @@ Task::move_thread(int thread_id) #if CLICK_LINUXMODULE assert(!in_interrupt()); #endif -#if CLICK_BSDMODULE - assert(!intr_nesting_level); - SPLCHECK; -#endif if (thread_id < RouterThread::THREAD_QUIESCENT) thread_id = RouterThread::THREAD_QUIESCENT; _home_thread_id = thread_id; @@ -393,13 +385,20 @@ Task::move_thread(int thread_id) add_pending(CHANGE_THREAD); } +#ifdef CLICK_BSDMODULE +inline void +Task::lock_assert(Mutex *lk) +{ + mtx_assert(lk, MA_OWNED); +} +#endif void Task::process_pending(RouterThread *thread) { // must be called with thread->lock held #if CLICK_BSDMODULE - int s = splimp(); + //lock_assert(thread->lock()); #endif if (_thread == thread) { @@ -423,10 +422,6 @@ Task::process_pending(RouterThread *thread) if (_pending) add_pending(0); - -#if CLICK_BSDMODULE - splx(s); -#endif } CLICK_ENDDECLS diff --git a/tools/click-install/click-install.cc b/tools/click-install/click-install.cc index d7fa623..484d731 100644 --- a/tools/click-install/click-install.cc +++ b/tools/click-install/click-install.cc @@ -246,6 +246,39 @@ install_module(const String &filename, const String &options, #endif } +static int caughtsig = 0; + +static void +catchsig(int s) +{ + caughtsig = 1; +} + +static int +clickfs_mount(const char *type, const char *path, int flags, void *data) +{ + struct iovec iov[4]; + + iov[0].iov_base = (char *)"fstype"; + iov[0].iov_len = sizeof("fstype"); + iov[1].iov_base = (char *)type; + iov[1].iov_len = strlen((char *)iov[1].iov_base) + 1; + iov[2].iov_base = (char *)"fspath"; + iov[2].iov_len = sizeof("fspath"); + iov[3].iov_base = (char *)path; + iov[3].iov_len = strlen((char *)iov[3].iov_base) + 1; + + signal(SIGSYS, catchsig); + int error = nmount(iov, 4, flags); + signal(SIGSYS, SIG_DFL); + + if(error && (errno == EOPNOTSUPP || errno == ENOSYS || caughtsig)) + error = mount(type, path, flags, data); + + if(error) + return -1; +} + static void install_required_packages(RouterT *r, HashMap &packages, HashMap &active_modules, @@ -517,9 +550,9 @@ particular purpose.\n"); // mount Click file system if (verbose) - errh->message("Mounting Click module at %s", clickfs_prefix); + errh->message("Mounting Clickfs at %s", clickfs_prefix); # if FOR_BSDMODULE - int mount_retval = mount("click", clickfs_prefix, 0, 0); + int mount_retval = clickfs_mount("clickfs", clickfs_prefix, 0, NULL); # else int mount_retval = mount("none", clickfs_prefix, "click", 0, 0); # endif @@ -537,6 +570,9 @@ particular purpose.\n"); #endif } + errh->message("FS created -- installing packages"); + exit(0); + // find current packages HashMap active_modules(-1); HashMap packages(-1);