From owner-svn-soc-all@FreeBSD.ORG Mon Jun 1 08:02:44 2015 Return-Path: Delivered-To: svn-soc-all@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 5E83AAF6 for ; Mon, 1 Jun 2015 08:02:44 +0000 (UTC) (envelope-from btw@FreeBSD.org) Received: from socsvn.freebsd.org (socsvn.freebsd.org [IPv6:2001:1900:2254:206a::50:2]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 4B9071588 for ; Mon, 1 Jun 2015 08:02:44 +0000 (UTC) (envelope-from btw@FreeBSD.org) Received: from socsvn.freebsd.org ([127.0.1.124]) by socsvn.freebsd.org (8.14.9/8.14.9) with ESMTP id t5182iVI059948 for ; Mon, 1 Jun 2015 08:02:44 GMT (envelope-from btw@FreeBSD.org) Received: (from www@localhost) by socsvn.freebsd.org (8.14.9/8.14.9/Submit) id t5182hSH059944 for svn-soc-all@FreeBSD.org; Mon, 1 Jun 2015 08:02:43 GMT (envelope-from btw@FreeBSD.org) Date: Mon, 1 Jun 2015 08:02:43 GMT Message-Id: <201506010802.t5182hSH059944@socsvn.freebsd.org> X-Authentication-Warning: socsvn.freebsd.org: www set sender to btw@FreeBSD.org using -f From: btw@FreeBSD.org To: svn-soc-all@FreeBSD.org Subject: socsvn commit: r286515 - soc2015/btw/head/tools/tools/mq-testing/vme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-soc-all@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the entire Summer of Code repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 01 Jun 2015 08:02:44 -0000 Author: btw Date: Mon Jun 1 08:02:42 2015 New Revision: 286515 URL: http://svnweb.FreeBSD.org/socsvn/?view=rev&rev=286515 Log: A kernel module vme (Virtual Multiqueue Ethernet interface) is created based on tap(4). It is written for the purpose of providing the same multiqueue behaviours with the hardware multiqueue NICs. The following features have already been implemented: - Calculating the RSS hash for IPv4 packets; - Multiqueue input supports; PS. vme is created to avoid adding too much unnecessary things to tap(4). Added: soc2015/btw/head/tools/tools/mq-testing/vme/ soc2015/btw/head/tools/tools/mq-testing/vme/Makefile soc2015/btw/head/tools/tools/mq-testing/vme/if_vme.c soc2015/btw/head/tools/tools/mq-testing/vme/if_vme.h Added: soc2015/btw/head/tools/tools/mq-testing/vme/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2015/btw/head/tools/tools/mq-testing/vme/Makefile Mon Jun 1 08:02:42 2015 (r286515) @@ -0,0 +1,15 @@ +# Copyright (C) 1999-2000 by Maksim Yevmenkin +# +# $FreeBSD$ + +.PATH: ${.CURDIR} + +KMOD= if_vme +SRCS= if_vme.c opt_compat.h opt_inet.h opt_rss.h vnode_if.h + +.if !defined(KERNBUILDDIR) +opt_compat.h: + echo "#define COMPAT_FREEBSD6 1" > ${.TARGET} +.endif + +.include Added: soc2015/btw/head/tools/tools/mq-testing/vme/if_vme.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ soc2015/btw/head/tools/tools/mq-testing/vme/if_vme.c Mon Jun 1 08:02:42 2015 (r286515) @@ -0,0 +1,1168 @@ +/*- + * Copyright (C) 1999-2000 by Maksim Yevmenkin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BASED ON: + * ------------------------------------------------------------------------- + * + * Copyright (c) 1988, Julian Onions + * Nottingham University 1987. + */ + +/* + * $FreeBSD$ + * $Id: if_vme.c,v 0.21 2000/07/23 21:46:02 max Exp $ + */ + +#include "opt_compat.h" +#include "opt_rss.h" + +#ifndef RSS +#error "RSS must be enabled" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "if_vme.h" + +/* + * XXXGL: to avoid inclusion of if_var.h define if_purgeaddrs. + * This should be fixed by destroying interface clone on close(2). + */ +void if_purgeaddrs(if_t); + +#define CDEV_NAME "vme" +#define VMEDEBUG if (vmedebug) printf + +static const char vmename[] = "vme"; +#define VMEMAXUNIT 0x7fff +#define VMEMAXQUEUES 128 + +#define PRIV_NET_VME PRIV_NET_TAP + +/* module */ +static int vmemodevent(module_t, int, void *); + +/* device */ +static void vmeclone(void *, struct ucred *, char *, int, + struct cdev **); +static void vmecreate(struct cdev *); + +/* network interface */ +static int vmeifioctl(if_t, u_long, void *, struct thread *); +static int vmeiftransmit(if_t, struct mbuf *); + +static int vme_clone_create(struct if_clone *, int, caddr_t); +static void vme_clone_destroy(if_t); +static struct if_clone *vme_cloner; + +/* character device */ +static d_open_t vmeopen; +static d_close_t vmeclose; +static d_read_t vmeread; +static d_write_t vmewrite; +static d_ioctl_t vmeioctl; +static d_poll_t vmepoll; +static d_kqfilter_t vmekqfilter; + +/* kqueue(2) */ +static int vmekqread(struct knote *, long); +static int vmekqwrite(struct knote *, long); +static void vmekqdetach(struct knote *); + +/* multiqueue rss */ +static void vmersshash(struct mbuf *); + +static struct filterops vme_read_filterops = { + .f_isfd = 1, + .f_attach = NULL, + .f_detach = vmekqdetach, + .f_event = vmekqread, +}; + +static struct filterops vme_write_filterops = { + .f_isfd = 1, + .f_attach = NULL, + .f_detach = vmekqdetach, + .f_event = vmekqwrite, +}; + +static struct cdevsw vme_cdevsw = { + .d_version = D_VERSION, + .d_flags = D_NEEDMINOR, + .d_open = vmeopen, + .d_close = vmeclose, + .d_read = vmeread, + .d_write = vmewrite, + .d_ioctl = vmeioctl, + .d_poll = vmepoll, + .d_name = CDEV_NAME, + .d_kqfilter = vmekqfilter, +}; + +static struct ifdriver vme_ifdrv = { + .ifdrv_ops = { + .ifop_ioctl = vmeifioctl, + .ifop_transmit = vmeiftransmit, + }, + .ifdrv_name = vmename, + .ifdrv_type = IFT_ETHER, +}; + +/* + * vme_mtx locks vme_flags, vme_pid. vme_next locked with global vmemtx. + * Other fields locked by owning subsystems. + */ +struct vme_softc { + struct mtx vme_mtx; /* per-softc mutex */ + struct cdev *vme_dev; + struct ifnet *vme_ifp; + struct mbufq vme_queue; + uint32_t vme_ifflags; + uint32_t vme_mtu; + uint64_t vme_baudrate; + uint16_t vme_flags; /* misc flags */ +#define VME_OPEN (1 << 0) +#define VME_INITED (1 << 1) +#define VME_RWAIT (1 << 2) +#define VME_ASYNC (1 << 3) +#define VME_READY (VME_OPEN|VME_INITED) + + u_int8_t ether_addr[ETHER_ADDR_LEN]; /* remote address */ + pid_t vme_pid; /* PID of process to open */ + struct sigio *vme_sigio; /* information for async I/O */ + struct selinfo vme_rsel; /* read select */ + + SLIST_ENTRY(vme_softc) vme_next; /* next device in chain */ +}; + +/* + * All global variables in if_vme.c are locked with vmemtx, with the + * exception of vmedebug, which is accessed unlocked; vmeclones is + * static at runtime. + */ +static struct mtx vmemtx; +static int vmedebug = 0; /* debug flag */ +static int vmeuopen = 0; /* allow user open() */ +static int vmeuponopen = 0; /* IFF_UP on open() */ +static int vmedclone = 1; /* enable devfs cloning */ +static int vmenumqueues = 0; /* Number of queues */ +static SLIST_HEAD(, vme_softc) vmehead; /* first device */ +static struct clonedevs *vmeclones; + +MALLOC_DECLARE(M_VME); +MALLOC_DEFINE(M_VME, CDEV_NAME, "Ethernet tunnel interface"); +SYSCTL_INT(_debug, OID_AUTO, if_vme_debug, CTLFLAG_RW, &vmedebug, 0, ""); + +SYSCTL_DECL(_net_link); +static SYSCTL_NODE(_net_link, OID_AUTO, vme, CTLFLAG_RW, 0, + "Ethernet tunnel software network interface"); +SYSCTL_INT(_net_link_vme, OID_AUTO, user_open, CTLFLAG_RW, &vmeuopen, 0, + "Allow user to open /dev/vme (based on node permissions)"); +SYSCTL_INT(_net_link_vme, OID_AUTO, up_on_open, CTLFLAG_RW, &vmeuponopen, 0, + "Bring interface up when /dev/vme is opened"); +SYSCTL_INT(_net_link_vme, OID_AUTO, devfs_cloning, CTLFLAG_RWTUN, &vmedclone, 0, + "Enably legacy devfs interface creation"); +SYSCTL_INT(_net_link_vme, OID_AUTO, debug, CTLFLAG_RW, &vmedebug, 0, ""); +SYSCTL_INT(_net_link_vme, OID_AUTO, num_queues, CTLFLAG_RDTUN, &vmenumqueues, 0, + "Number of queues to configure, 0 indicates autoconfigure"); + +DEV_MODULE(if_vme, vmemodevent, NULL); + +static int +vme_clone_create(struct if_clone *ifc, int unit, caddr_t params) +{ + struct cdev *dev; + int i; + + /* Find any existing device, or allocate new unit number. */ + i = clone_create(&vmeclones, &vme_cdevsw, &unit, &dev, 0); + if (i) { + dev = make_dev(&vme_cdevsw, unit, UID_ROOT, GID_WHEEL, 0600, + "%s%d", vmename, unit); + } + + vmecreate(dev); + return (0); +} + +static void +vme_destroy(struct vme_softc *tp) +{ + if_t ifp = tp->vme_ifp; + + CURVNET_SET(ifp->if_vnet); + destroy_dev(tp->vme_dev); + seldrain(&tp->vme_rsel); + knlist_clear(&tp->vme_rsel.si_note, 0); + knlist_destroy(&tp->vme_rsel.si_note); + if_detach(ifp); + + mtx_destroy(&tp->vme_mtx); + free(tp, M_VME); + CURVNET_RESTORE(); +} + +static void +vme_clone_destroy(if_t ifp) +{ + struct vme_softc *tp; + + tp = if_getsoftc(ifp, IF_DRIVER_SOFTC); + + mtx_lock(&vmemtx); + SLIST_REMOVE(&vmehead, tp, vme_softc, vme_next); + mtx_unlock(&vmemtx); + vme_destroy(tp); +} + +/* + * vmemodevent + * + * module event handler + */ +static int +vmemodevent(module_t mod, int type, void *data) +{ + static eventhandler_tag eh_tag = NULL; + struct vme_softc *tp = NULL; + if_t ifp = NULL; + + switch (type) { + case MOD_LOAD: + + /* intitialize device */ + + mtx_init(&vmemtx, "vmemtx", NULL, MTX_DEF); + SLIST_INIT(&vmehead); + + clone_setup(&vmeclones); + eh_tag = EVENTHANDLER_REGISTER(dev_clone, vmeclone, 0, 1000); + if (eh_tag == NULL) { + clone_cleanup(&vmeclones); + mtx_destroy(&vmemtx); + return (ENOMEM); + } + vme_cloner = if_clone_simple(vmename, vme_clone_create, + vme_clone_destroy, 0); + + if (vmenumqueues <= 0 || vmenumqueues >= VMEMAXQUEUES) + vmenumqueues = mp_ncpus; + + return (0); + + case MOD_UNLOAD: + /* + * The EBUSY algorithm here can't quite atomically + * guarantee that this is race-free since we have to + * release the vme mtx to deregister the clone handler. + */ + mtx_lock(&vmemtx); + SLIST_FOREACH(tp, &vmehead, vme_next) { + mtx_lock(&tp->vme_mtx); + if (tp->vme_flags & VME_OPEN) { + mtx_unlock(&tp->vme_mtx); + mtx_unlock(&vmemtx); + return (EBUSY); + } + mtx_unlock(&tp->vme_mtx); + } + mtx_unlock(&vmemtx); + + EVENTHANDLER_DEREGISTER(dev_clone, eh_tag); + if_clone_detach(vme_cloner); + drain_dev_clone_events(); + + mtx_lock(&vmemtx); + while ((tp = SLIST_FIRST(&vmehead)) != NULL) { + SLIST_REMOVE_HEAD(&vmehead, vme_next); + mtx_unlock(&vmemtx); + + ifp = tp->vme_ifp; + + VMEDEBUG("detaching %s\n", if_name(ifp)); + + vme_destroy(tp); + mtx_lock(&vmemtx); + } + mtx_unlock(&vmemtx); + clone_cleanup(&vmeclones); + + mtx_destroy(&vmemtx); + + break; + + default: + return (EOPNOTSUPP); + } + + return (0); +} + +/* + * DEVFS handler + * + * We need to support a kind of devices - vme + */ +static void +vmeclone(void *arg, struct ucred *cred, char *name, int namelen, + struct cdev **dev) +{ + char devname[SPECNAMELEN + 1]; + int i, unit, append_unit; + + if (*dev != NULL) + return; + + if (!vmedclone || + (!vmeuopen && priv_check_cred(cred, PRIV_NET_IFCREATE, 0) != 0)) + return; + + unit = 0; + append_unit = 0; + + /* We're interested in only vme devices. */ + if (strcmp(name, vmename) == 0) { + unit = -1; + } else if (dev_stdclone(name, NULL, vmename, &unit) != 1) { + return; + } + + if (unit == -1) + append_unit = 1; + + CURVNET_SET(CRED_TO_VNET(cred)); + /* find any existing device, or allocate new unit number */ + i = clone_create(&vmeclones, &vme_cdevsw, &unit, dev, 0); + if (i) { + if (append_unit) { + /* + * We were passed 'tun' or 'vme', with no unit specified + * so we'll need to append it now. + */ + namelen = snprintf(devname, sizeof(devname), "%s%d", name, + unit); + name = devname; + } + + *dev = make_dev_credf(MAKEDEV_REF, &vme_cdevsw, unit, + cred, UID_ROOT, GID_WHEEL, 0600, "%s", name); + } + + if_clone_create(name, namelen, NULL); + CURVNET_RESTORE(); +} + + +/* + * vmecreate + * + * to create interface + */ +static void +vmecreate(struct cdev *dev) +{ + struct if_attach_args ifat = { + .ifat_version = IF_ATTACH_VERSION, + .ifat_mtu = ETHERMTU, + .ifat_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST, + .ifat_capabilities = IFCAP_LINKSTATE, + .ifat_capenable = IFCAP_LINKSTATE, + .ifat_nrings = vmenumqueues, + }; + if_t ifp = NULL; + struct vme_softc *tp = NULL; + unsigned short macaddr_hi; + uint32_t macaddr_mid; + int unit; + const char *name = NULL; + u_char eaddr[6]; + + /* allocate driver storage and create device */ + tp = malloc(sizeof(*tp), M_VME, M_WAITOK | M_ZERO); + mtx_init(&tp->vme_mtx, "vme_mtx", NULL, MTX_DEF); + mbufq_init(&tp->vme_queue, IFQ_MAXLEN); + mtx_lock(&vmemtx); + SLIST_INSERT_HEAD(&vmehead, tp, vme_next); + mtx_unlock(&vmemtx); + + unit = dev2unit(dev); + + name = vmename; + ifat.ifat_drv = &vme_ifdrv; + + unit &= VMEMAXUNIT; + + VMEDEBUG("vmecreate(%s%d). minor = %#x\n", name, unit, dev2unit(dev)); + + /* generate fake MAC address: 00 bd xx xx xx unit_no */ + macaddr_hi = htons(0x00bd); + macaddr_mid = (uint32_t) ticks; + bcopy(&macaddr_hi, eaddr, sizeof(short)); + bcopy(&macaddr_mid, &eaddr[2], sizeof(uint32_t)); + eaddr[5] = (u_char)unit; + + /* fill the rest and attach interface */ + ifat.ifat_softc = tp; + ifat.ifat_dunit = unit; + ifat.ifat_lla = eaddr; + ifp = tp->vme_ifp = if_attach(&ifat); + if (ifp == NULL) + panic("%s%d: can not if_attach()", name, unit); + + dev->si_drv1 = tp; + tp->vme_dev = dev; + + mtx_lock(&tp->vme_mtx); + tp->vme_flags |= VME_INITED; + mtx_unlock(&tp->vme_mtx); + + knlist_init_mtx(&tp->vme_rsel.si_note, &tp->vme_mtx); + + VMEDEBUG("interface %s is created. minor = %#x\n", + if_name(ifp), dev2unit(dev)); +} + +/* + * vmeopen + * + * to open tunnel. must be superuser + */ +static int +vmeopen(struct cdev *dev, int flag, int mode, struct thread *td) +{ + struct vme_softc *tp = NULL; + if_t ifp = NULL; + int error; + + if (vmeuopen == 0) { + error = priv_check(td, PRIV_NET_VME); + if (error) + return (error); + } + + if ((dev2unit(dev) & CLONE_UNITMASK) > VMEMAXUNIT) + return (ENXIO); + + tp = dev->si_drv1; + + mtx_lock(&tp->vme_mtx); + if (tp->vme_flags & VME_OPEN) { + mtx_unlock(&tp->vme_mtx); + return (EBUSY); + } + + bcopy(if_lladdr(tp->vme_ifp), tp->ether_addr, sizeof(tp->ether_addr)); + tp->vme_pid = td->td_proc->p_pid; + tp->vme_flags |= VME_OPEN; + ifp = tp->vme_ifp; + + if_link_state_change(ifp, LINK_STATE_UP); + mtx_unlock(&tp->vme_mtx); + + if (vmeuponopen) { + struct ifreq ifr; + + if_drvioctl(ifp, SIOCGIFFLAGS, &ifr, td); + ifr.ifr_flags |= IFF_UP; + if_drvioctl(ifp, SIOCSIFFLAGS, &ifr, td); + } + + VMEDEBUG("%s is open. minor = %#x\n", if_name(ifp), dev2unit(dev)); + + return (0); +} + +/* + * vmeclose + * + * close the device - mark i/f down & delete routing info + */ +static int +vmeclose(struct cdev *dev, int foo, int bar, struct thread *td) +{ + struct vme_softc *tp = dev->si_drv1; + if_t ifp = tp->vme_ifp; + + /* junk all pending output */ + mtx_lock(&tp->vme_mtx); + CURVNET_SET(ifp->if_vnet); + if_link_state_change(ifp, LINK_STATE_DOWN); + mbufq_drain(&tp->vme_queue); + + /* + * Bring the interface down. + */ + if ((tp->vme_ifflags & (IFF_UP | IFF_LINK0)) == IFF_UP) { + struct ifreq ifr; + + mtx_unlock(&tp->vme_mtx); + if_drvioctl(ifp, SIOCGIFFLAGS, &ifr, td); + ifr.ifr_flags &= ~IFF_UP; + if_drvioctl(ifp, SIOCSIFFLAGS, &ifr, td); + if_purgeaddrs(ifp); + mtx_lock(&tp->vme_mtx); + } + + CURVNET_RESTORE(); + + funsetown(&tp->vme_sigio); + selwakeuppri(&tp->vme_rsel, PZERO+1); + KNOTE_LOCKED(&tp->vme_rsel.si_note, 0); + + tp->vme_flags &= ~VME_OPEN; + tp->vme_pid = 0; + mtx_unlock(&tp->vme_mtx); + + VMEDEBUG("%s is closed. minor = %#x\n", if_name(ifp), dev2unit(dev)); + + return (0); +} + +/* + * vmeifioctl + * + * Process an ioctl request on network interface + */ +static int +vmeifioctl(if_t ifp, u_long cmd, void *data, struct thread *td) +{ + struct vme_softc *tp; + struct ifreq *ifr = (struct ifreq *)data; + struct ifstat *ifs = NULL; + struct ifmediareq *ifmr = NULL; + int dummy, error = 0; + + tp = if_getsoftc(ifp, IF_DRIVER_SOFTC); + + switch (cmd) { + case SIOCSIFFLAGS: + tp->vme_ifflags = ifr->ifr_flags; + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + break; + + case SIOCGIFMEDIA: + ifmr = (struct ifmediareq *)data; + dummy = ifmr->ifm_count; + ifmr->ifm_count = 1; + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + if (tp->vme_flags & VME_OPEN) + ifmr->ifm_status |= IFM_ACTIVE; + ifmr->ifm_current = ifmr->ifm_active; + if (dummy >= 1) { + int media = IFM_ETHER; + error = copyout(&media, ifmr->ifm_ulist, + sizeof(int)); + } + break; + + case SIOCSIFMTU: + tp->vme_mtu = ifr->ifr_mtu; + break; + + case SIOCGIFSTATUS: + ifs = (struct ifstat *)data; + mtx_lock(&tp->vme_mtx); + if (tp->vme_pid != 0) + snprintf(ifs->ascii, sizeof(ifs->ascii), + "\tOpened by PID %d\n", tp->vme_pid); + else + ifs->ascii[0] = '\0'; + mtx_unlock(&tp->vme_mtx); + break; + + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} + +/* + * vmeiftransmit + * + * queue packets from higher level ready to put out + */ +static int +vmeiftransmit(if_t ifp, struct mbuf *m) +{ + struct vme_softc *tp; + int error; + + VMEDEBUG("%s starting\n", if_name(ifp)); + + tp = if_getsoftc(ifp, IF_DRIVER_SOFTC); + + mtx_lock(&tp->vme_mtx); + if ((tp->vme_flags & VME_READY) != VME_READY) { + + /* Unlocked read. */ + VMEDEBUG("%s not ready, vme_flags = 0x%x\n", if_name(ifp), + tp->vme_flags); + m_freem(m); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + mtx_unlock(&tp->vme_mtx); + return (0); + } + + if ((error = mbufq_enqueue(&tp->vme_queue, m)) != 0) + return (error); + + if (tp->vme_flags & VME_RWAIT) { + tp->vme_flags &= ~VME_RWAIT; + wakeup(tp); + } + + if ((tp->vme_flags & VME_ASYNC) && (tp->vme_sigio != NULL)) { + mtx_unlock(&tp->vme_mtx); + pgsigio(&tp->vme_sigio, SIGIO, 0); + mtx_lock(&tp->vme_mtx); + } + + selwakeuppri(&tp->vme_rsel, PZERO+1); + KNOTE_LOCKED(&tp->vme_rsel.si_note, 0); + + mtx_unlock(&tp->vme_mtx); + + return (0); +} + +/* + * vmeioctl + * + * the cdevsw interface is now pretty minimal + */ +static int +vmeioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, + struct thread *td) +{ + struct vme_softc *tp = dev->si_drv1; + if_t ifp = tp->vme_ifp; + struct vmeinfo *vmep = NULL; + int error = 0; +#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ + defined(COMPAT_FREEBSD4) + int ival; +#endif + + switch (cmd) { + case VMESIFINFO: + { + struct ifreq ifr; + + vmep = (struct vmeinfo *)data; + ifr.ifr_mtu = vmep->mtu; + error = if_drvioctl(ifp, SIOCSIFMTU, &ifr, td); + if (error) + break; + tp->vme_baudrate = vmep->baudrate; + if_setbaudrate(ifp, vmep->baudrate); + break; + } + + case VMEGIFINFO: + vmep = (struct vmeinfo *)data; + mtx_lock(&tp->vme_mtx); + vmep->mtu = tp->vme_mtu; + vmep->type = IFT_ETHER; + vmep->baudrate = tp->vme_baudrate; + mtx_unlock(&tp->vme_mtx); + break; + + case VMESDEBUG: + vmedebug = *(int *)data; + break; + + case VMEGDEBUG: + *(int *)data = vmedebug; + break; + + case VMEGIFNAME: + { + struct ifreq *ifr = (struct ifreq *) data; + + strlcpy(ifr->ifr_name, if_name(ifp), IFNAMSIZ); + break; + } + + case FIONBIO: + break; + + case FIOASYNC: + mtx_lock(&tp->vme_mtx); + if (*(int *)data) + tp->vme_flags |= VME_ASYNC; + else + tp->vme_flags &= ~VME_ASYNC; + mtx_unlock(&tp->vme_mtx); + break; + + case FIONREAD: + { + struct mbuf *m; + + m = mbufq_first(&tp->vme_queue); + if (m != NULL) + *(int *)data = m->m_pkthdr.len; + else + *(int *)data = 0; + break; + } + + case FIOSETOWN: + return (fsetown(*(int *)data, &tp->vme_sigio)); + + case FIOGETOWN: + *(int *)data = fgetown(&tp->vme_sigio); + return (0); + + /* this is deprecated, FIOSETOWN should be used instead */ + case TIOCSPGRP: + return (fsetown(-(*(int *)data), &tp->vme_sigio)); + + /* this is deprecated, FIOGETOWN should be used instead */ + case TIOCGPGRP: + *(int *)data = -fgetown(&tp->vme_sigio); + return (0); + +#if defined(COMPAT_FREEBSD6) || defined(COMPAT_FREEBSD5) || \ + defined(COMPAT_FREEBSD4) + case _IO('V', 0): + ival = IOCPARM_IVAL(data); + data = (caddr_t)&ival; + /* FALLTHROUGH */ +#endif + case SIOCGIFADDR: /* get MAC address of the remote side */ + mtx_lock(&tp->vme_mtx); + bcopy(tp->ether_addr, data, sizeof(tp->ether_addr)); + mtx_unlock(&tp->vme_mtx); + break; + + case SIOCSIFADDR: /* set MAC address of the remote side */ + mtx_lock(&tp->vme_mtx); + bcopy(data, tp->ether_addr, sizeof(tp->ether_addr)); + mtx_unlock(&tp->vme_mtx); + break; + + default: + return (ENOTTY); + } + + return (error); +} + +/* + * vmeread + * + * the cdevsw read interface - reads a packet at a time, or at + * least as much of a packet as can be read + */ +static int +vmeread(struct cdev *dev, struct uio *uio, int flag) +{ + struct vme_softc *tp = dev->si_drv1; + if_t ifp = tp->vme_ifp; + struct mbuf *m = NULL; + int error = 0, len; + + VMEDEBUG("%s reading, minor = %#x\n", if_name(ifp), dev2unit(dev)); + + mtx_lock(&tp->vme_mtx); + if ((tp->vme_flags & VME_READY) != VME_READY) { + mtx_unlock(&tp->vme_mtx); + + /* Unlocked read. */ + VMEDEBUG("%s not ready. minor = %#x, vme_flags = 0x%x\n", + if_name(ifp), dev2unit(dev), tp->vme_flags); + + return (EHOSTDOWN); + } + + tp->vme_flags &= ~VME_RWAIT; + + /* sleep until we get a packet */ + while ((m = mbufq_dequeue(&tp->vme_queue)) == NULL) { + if (flag & O_NONBLOCK) { + mtx_unlock(&tp->vme_mtx); + return (EWOULDBLOCK); + } + tp->vme_flags |= VME_RWAIT; + error = mtx_sleep(tp, &tp->vme_mtx, PCATCH | (PZERO + 1), + "vmerd", 0); + if (error) { + mtx_unlock(&tp->vme_mtx); + return (error); + } + } + mtx_unlock(&tp->vme_mtx); + + /* feed packet to bpf */ + if_mtap(ifp, m, NULL, 0); + + /* xfer packet to user space */ + while ((m != NULL) && (uio->uio_resid > 0) && (error == 0)) { + len = min(uio->uio_resid, m->m_len); + if (len == 0) + break; + + error = uiomove(mtod(m, void *), len, uio); + if (error != 0) + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + else if ((m->m_flags & M_PKTHDR) != 0) + if_inc_txcounters(ifp, m); + m = m_free(m); + } + + if (m != NULL) { + VMEDEBUG("%s dropping mbuf, minor = %#x\n", if_name(ifp), + dev2unit(dev)); + m_freem(m); + } + + return (error); +} + +/* + * vmewrite + * + * the cdevsw write interface - an atomic write is a packet - or else! + */ +static int +vmewrite(struct cdev *dev, struct uio *uio, int flag) +{ + struct ether_header *eh; + struct vme_softc *tp = dev->si_drv1; + if_t ifp = tp->vme_ifp; + struct mbuf *m; + ifring_t *ifrs; + int rid; + + VMEDEBUG("%s writing, minor = %#x\n", if_name(ifp), dev2unit(dev)); + + if (uio->uio_resid == 0) + return (0); + + if ((uio->uio_resid < 0) || (uio->uio_resid > VMEMRU)) { + VMEDEBUG("%s invalid packet len = %zd, minor = %#x\n", + if_name(ifp), uio->uio_resid, dev2unit(dev)); + + return (EIO); + } + + if ((m = m_uiotombuf(uio, M_NOWAIT, 0, ETHER_ALIGN, + M_PKTHDR)) == NULL) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + return (ENOBUFS); + } + + m->m_pkthdr.rcvif = ifp; + + /* + * Only pass a unicast frame to ether_input(), if it would actually + * have been received by non-virtual hardware. + */ + if (m->m_len < sizeof(struct ether_header)) { + m_freem(m); + return (0); + } + eh = mtod(m, struct ether_header *); + + if (eh && (tp->vme_ifflags & IFF_PROMISC) == 0 && + !ETHER_IS_MULTICAST(eh->ether_dhost) && + bcmp(eh->ether_dhost, if_lladdr(ifp), ETHER_ADDR_LEN) != 0) { + m_freem(m); + return (0); + } + + vmersshash(m); + ifrs = if_getsoftc(ifp, IF_RING); + + rid = m->m_pkthdr.flowid % vmenumqueues; + m->m_pkthdr.ifring = ifrs[rid]; + + /* Pass packet up to parent. */ + CURVNET_SET(ifp->if_vnet); + if_input(ifp, m); + CURVNET_RESTORE(); + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); /* ibytes are counted in parent */ + + return (0); +} + +/* + * vmepoll + * + * the poll interface, this is only useful on reads + * really. the write detect always returns true, write never blocks + * anyway, it either accepts the packet or drops it + */ +static int +vmepoll(struct cdev *dev, int events, struct thread *td) +{ + struct vme_softc *tp = dev->si_drv1; + if_t ifp = tp->vme_ifp; + int revents = 0; + + VMEDEBUG("%s polling, minor = %#x\n", if_name(ifp), dev2unit(dev)); + + mtx_lock(&tp->vme_mtx); + if (events & (POLLIN | POLLRDNORM)) { + if (mbufq_len(&tp->vme_queue) > 0) { + VMEDEBUG("%s have data in queue. len = %d, " \ + "minor = %#x\n", if_name(ifp), + if_snd_len(ifp), dev2unit(dev)); + + revents |= (events & (POLLIN | POLLRDNORM)); + } else { + VMEDEBUG("%s waiting for data, minor = %#x\n", + if_name(ifp), dev2unit(dev)); + + selrecord(td, &tp->vme_rsel); + } *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***