ng_device.c revision 141341
1139823Simp/*- 298402Sjulian * Copyright (c) 2002 Mark Santcroos <marks@ripe.net> 3137525Sglebius * Copyright (c) 2004 Gleb Smirnoff <glebius@FreeBSD.org> 498402Sjulian * 598402Sjulian * Redistribution and use in source and binary forms, with or without 698402Sjulian * modification, are permitted provided that the following conditions 798402Sjulian * are met: 898402Sjulian * 1. Redistributions of source code must retain the above copyright 998402Sjulian * notice, this list of conditions and the following disclaimer. 1098402Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1198402Sjulian * notice, this list of conditions and the following disclaimer in the 1298402Sjulian * documentation and/or other materials provided with the distribution. 1398402Sjulian * 1498402Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1598402Sjulian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1698402Sjulian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1798402Sjulian * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1898402Sjulian * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1998402Sjulian * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2098402Sjulian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2198402Sjulian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2298402Sjulian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2398402Sjulian * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2498402Sjulian * 2598402Sjulian * Netgraph "device" node 2698402Sjulian * 27136673Sglebius * This node presents a /dev/ngd%d device that interfaces to an other 2898402Sjulian * netgraph node. 2998402Sjulian * 3098402Sjulian * $FreeBSD: head/sys/netgraph/ng_device.c 141341 2005-02-05 08:28:36Z ru $ 3198402Sjulian * 3298402Sjulian */ 3398402Sjulian 34136673Sglebius#if 0 35137100Sglebius#define DBG do { printf("ng_device: %s\n", __func__ ); } while (0) 36136673Sglebius#else 37137100Sglebius#define DBG do {} while (0) 38136673Sglebius#endif 39136673Sglebius 4098402Sjulian#include <sys/param.h> 41132446Sglebius#include <sys/conf.h> 42132446Sglebius#include <sys/ioccom.h> 4398402Sjulian#include <sys/kernel.h> 44132446Sglebius#include <sys/malloc.h> 4598402Sjulian#include <sys/mbuf.h> 46132446Sglebius#include <sys/poll.h> 47132446Sglebius#include <sys/queue.h> 48136673Sglebius#include <sys/socket.h> 49132446Sglebius#include <sys/systm.h> 5098402Sjulian#include <sys/uio.h> 51136673Sglebius#include <sys/vnode.h> 5298402Sjulian 53136673Sglebius#include <net/if.h> 54136673Sglebius#include <net/if_var.h> 55136673Sglebius#include <netinet/in.h> 56136673Sglebius#include <netinet/in_systm.h> 57136673Sglebius#include <netinet/ip.h> 58136673Sglebius 5998402Sjulian#include <netgraph/ng_message.h> 6098402Sjulian#include <netgraph/netgraph.h> 61132446Sglebius#include <netgraph/ng_device.h> 6298402Sjulian 63132448Sglebius#define ERROUT(x) do { error = (x); goto done; } while (0) 64132448Sglebius 6598402Sjulian/* Netgraph methods */ 66141341Srustatic int ng_device_mod_event(module_t, int, void *); 67136673Sglebiusstatic ng_constructor_t ng_device_constructor; 6898402Sjulianstatic ng_rcvmsg_t ng_device_rcvmsg; 69136673Sglebiusstatic ng_shutdown_t ng_device_shutdown; 7098402Sjulianstatic ng_newhook_t ng_device_newhook; 7198402Sjulianstatic ng_rcvdata_t ng_device_rcvdata; 7298402Sjulianstatic ng_disconnect_t ng_device_disconnect; 7398402Sjulian 7498402Sjulian/* Netgraph type */ 75136673Sglebiusstatic struct ng_type ngd_typestruct = { 76129823Sjulian .version = NG_ABI_VERSION, 77129823Sjulian .name = NG_DEVICE_NODE_TYPE, 78141341Sru .mod_event = ng_device_mod_event, 79136673Sglebius .constructor = ng_device_constructor, 80136673Sglebius .rcvmsg = ng_device_rcvmsg, 81136673Sglebius .shutdown = ng_device_shutdown, 82129823Sjulian .newhook = ng_device_newhook, 83129823Sjulian .rcvdata = ng_device_rcvdata, 84129823Sjulian .disconnect = ng_device_disconnect, 8598402Sjulian}; 86136673SglebiusNETGRAPH_INIT(device, &ngd_typestruct); 8798402Sjulian 88136673Sglebius/* per node data */ 89136673Sglebiusstruct ngd_private { 90136673Sglebius struct ifqueue readq; 91136673Sglebius SLIST_ENTRY(ngd_private) links; 92136673Sglebius struct ng_node *node; 93136673Sglebius struct ng_hook *hook; 94136673Sglebius struct cdev *ngddev; 95136673Sglebius struct mtx ngd_mtx; 96136673Sglebius int unit; 97136673Sglebius uint16_t flags; 98136673Sglebius#define NGDF_OPEN 0x0001 99136673Sglebius#define NGDF_RWAIT 0x0002 10098402Sjulian}; 101136673Sglebiustypedef struct ngd_private *priv_p; 10298402Sjulian 103136673Sglebius/* List of all active nodes and mutex to protect it */ 104136673Sglebiusstatic SLIST_HEAD(, ngd_private) ngd_nodes = SLIST_HEAD_INITIALIZER(ngd_nodes); 105136673Sglebiusstatic struct mtx ng_device_mtx; 10698402Sjulian 10798402Sjulian/* Maximum number of NGD devices */ 108136673Sglebius#define MAX_NGD 25 /* should be more than enough for now */ 10998402Sjulian 11098402Sjulianstatic d_close_t ngdclose; 11198402Sjulianstatic d_open_t ngdopen; 11298402Sjulianstatic d_read_t ngdread; 11398402Sjulianstatic d_write_t ngdwrite; 114136673Sglebius#if 0 11598402Sjulianstatic d_ioctl_t ngdioctl; 116136673Sglebius#endif 11798402Sjulianstatic d_poll_t ngdpoll; 11898402Sjulian 11998402Sjulianstatic struct cdevsw ngd_cdevsw = { 120126080Sphk .d_version = D_VERSION, 121111815Sphk .d_open = ngdopen, 122111815Sphk .d_close = ngdclose, 123111815Sphk .d_read = ngdread, 124111815Sphk .d_write = ngdwrite, 125136673Sglebius#if 0 126111815Sphk .d_ioctl = ngdioctl, 127136673Sglebius#endif 128111815Sphk .d_poll = ngdpoll, 129136673Sglebius .d_name = NG_DEVICE_DEVNAME, 13098402Sjulian}; 13198402Sjulian 132136673Sglebius/* Helper functions */ 133136673Sglebiusstatic int get_free_unit(void); 134136673Sglebius 135136673Sglebius/****************************************************************************** 136136673Sglebius * Netgraph methods 137136673Sglebius ******************************************************************************/ 138136673Sglebius 139136673Sglebius/* 140136673Sglebius * create new node 14198402Sjulian */ 14298402Sjulianstatic int 143136673Sglebiusng_device_constructor(node_p node) 14498402Sjulian{ 145136673Sglebius priv_p priv; 14698402Sjulian 147137022Sglebius DBG; 14898402Sjulian 149136673Sglebius MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 150136673Sglebius if (priv == NULL) 151136673Sglebius return (ENOMEM); 15298402Sjulian 153136673Sglebius mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF); 154136673Sglebius mtx_lock(&priv->ngd_mtx); 15598402Sjulian 156136673Sglebius mtx_lock(&ng_device_mtx); 157136673Sglebius 158136673Sglebius priv->unit = get_free_unit(); 159136673Sglebius if(priv->unit < 0) { 160136673Sglebius printf("%s: No free unit found by get_free_unit(), " 161136673Sglebius "increase MAX_NGD\n",__func__); 162136673Sglebius mtx_unlock(&ng_device_mtx); 163136673Sglebius mtx_destroy(&priv->ngd_mtx); 164136673Sglebius FREE(priv, M_NETGRAPH); 165136673Sglebius return(EINVAL); 16698402Sjulian } 16798402Sjulian 168136673Sglebius priv->ngddev = make_dev(&ngd_cdevsw, unit2minor(priv->unit), UID_ROOT, 169136673Sglebius GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit); 170136673Sglebius if(priv->ngddev == NULL) { 171136673Sglebius printf("%s(): make_dev() failed\n",__func__); 172136673Sglebius mtx_unlock(&ng_device_mtx); 173136673Sglebius mtx_destroy(&priv->ngd_mtx); 174136673Sglebius FREE(priv, M_NETGRAPH); 175136673Sglebius return(EINVAL); 176136673Sglebius } 17798402Sjulian 178136673Sglebius SLIST_INSERT_HEAD(&ngd_nodes, priv, links); 17998402Sjulian 180136673Sglebius mtx_unlock(&ng_device_mtx); 18198402Sjulian 182136673Sglebius mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF); 183136673Sglebius IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen); 18498402Sjulian 185136673Sglebius /* Link everything together */ 186136673Sglebius NG_NODE_SET_PRIVATE(node, priv); 187136673Sglebius priv->node = node; 188136673Sglebius priv->ngddev->si_drv1 = priv; 189136673Sglebius 190136673Sglebius mtx_unlock(&priv->ngd_mtx); 19198402Sjulian 19298402Sjulian return(0); 19398402Sjulian} 19498402Sjulian 195136673Sglebius/* 196136673Sglebius * Process control message. 19798402Sjulian */ 19898402Sjulian 19998402Sjulianstatic int 20098402Sjulianng_device_rcvmsg(node_p node, item_p item, hook_p lasthook) 20198402Sjulian{ 202136673Sglebius const priv_p priv = NG_NODE_PRIVATE(node); 20398402Sjulian struct ng_mesg *msg; 204136673Sglebius struct ng_mesg *resp = NULL; 20598402Sjulian int error = 0; 20698402Sjulian 207136673Sglebius NGI_GET_MSG(item, msg); 20898402Sjulian 209136673Sglebius if (msg->header.typecookie == NGM_DEVICE_COOKIE) { 210136673Sglebius switch (msg->header.cmd) { 211136673Sglebius case NGM_DEVICE_GET_DEVNAME: 212136673Sglebius /* XXX: Fix when NGD_MAX us bigger */ 213136673Sglebius NG_MKRESPONSE(resp, msg, 214136673Sglebius strlen(NG_DEVICE_DEVNAME) + 3, M_NOWAIT); 21598402Sjulian 216136673Sglebius if (resp == NULL) 217136673Sglebius ERROUT(ENOMEM); 21898402Sjulian 219136673Sglebius strlcpy((char *)resp->data, priv->ngddev->si_name, 220136673Sglebius strlen(priv->ngddev->si_name) + 1); 221136673Sglebius break; 22298402Sjulian 223136673Sglebius default: 224136673Sglebius error = EINVAL; 225136673Sglebius break; 22698402Sjulian } 227136673Sglebius } else 228136673Sglebius error = EINVAL; 22998402Sjulian 230136673Sglebiusdone: 231136673Sglebius NG_RESPOND_MSG(error, node, item, resp); 232136673Sglebius NG_FREE_MSG(msg); 233136673Sglebius return (error); 23498402Sjulian} 23598402Sjulian 23698402Sjulian/* 237136673Sglebius * Accept incoming hook. We support only one hook per node. 23898402Sjulian */ 23998402Sjulianstatic int 24098402Sjulianng_device_newhook(node_p node, hook_p hook, const char *name) 24198402Sjulian{ 242136673Sglebius priv_p priv = NG_NODE_PRIVATE(node); 24398402Sjulian 244137022Sglebius DBG; 24598402Sjulian 246136673Sglebius /* We have only one hook per node */ 247136673Sglebius if (priv->hook != NULL) 248136673Sglebius return (EISCONN); 24998402Sjulian 250136673Sglebius priv->hook = hook; 25198402Sjulian 25298402Sjulian return(0); 25398402Sjulian} 25498402Sjulian 25598402Sjulian/* 256136673Sglebius * Receive data from hook, write it to device. 25798402Sjulian */ 25898402Sjulianstatic int 25998402Sjulianng_device_rcvdata(hook_p hook, item_p item) 26098402Sjulian{ 261136673Sglebius priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 26298402Sjulian struct mbuf *m; 26398402Sjulian 264137022Sglebius DBG; 26598402Sjulian 266132448Sglebius NGI_GET_M(item, m); 267132448Sglebius NG_FREE_ITEM(item); 268132448Sglebius 269136673Sglebius IF_LOCK(&priv->readq); 270136673Sglebius if (_IF_QFULL(&priv->readq)) { 271136673Sglebius _IF_DROP(&priv->readq); 272136673Sglebius IF_UNLOCK(&priv->readq); 273136673Sglebius NG_FREE_M(m); 274136673Sglebius return (ENOBUFS); 27598402Sjulian } 27698402Sjulian 277136673Sglebius _IF_ENQUEUE(&priv->readq, m); 278136673Sglebius IF_UNLOCK(&priv->readq); 279136673Sglebius mtx_lock(&priv->ngd_mtx); 280136673Sglebius if (priv->flags & NGDF_RWAIT) { 281136673Sglebius priv->flags &= ~NGDF_RWAIT; 282136673Sglebius wakeup(priv); 28398402Sjulian } 284136673Sglebius mtx_unlock(&priv->ngd_mtx); 28598402Sjulian 286136673Sglebius return(0); 28798402Sjulian} 28898402Sjulian 28998402Sjulian/* 290136673Sglebius * Removal of the hook destroys the node. 29198402Sjulian */ 29298402Sjulianstatic int 29398402Sjulianng_device_disconnect(hook_p hook) 29498402Sjulian{ 295136673Sglebius priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 29698402Sjulian 297137022Sglebius DBG; 29898402Sjulian 299136673Sglebius destroy_dev(priv->ngddev); 300136673Sglebius mtx_destroy(&priv->ngd_mtx); 301132448Sglebius 302136673Sglebius mtx_lock(&ng_device_mtx); 303136673Sglebius SLIST_REMOVE(&ngd_nodes, priv, ngd_private, links); 304136673Sglebius mtx_unlock(&ng_device_mtx); 30598402Sjulian 306136673Sglebius IF_DRAIN(&priv->readq); 307136673Sglebius mtx_destroy(&(priv)->readq.ifq_mtx); 30898402Sjulian 309136673Sglebius FREE(priv, M_NETGRAPH); 31098402Sjulian 311136673Sglebius ng_rmnode_self(NG_HOOK_NODE(hook)); 31298402Sjulian 31398402Sjulian return(0); 31498402Sjulian} 315136673Sglebius 31698402Sjulian/* 317136673Sglebius * Node shutdown. Everything is already done in disconnect method. 31898402Sjulian */ 31998402Sjulianstatic int 320136673Sglebiusng_device_shutdown(node_p node) 321136673Sglebius{ 322136673Sglebius NG_NODE_UNREF(node); 323136673Sglebius return (0); 324136673Sglebius} 325136673Sglebius 326136673Sglebius/****************************************************************************** 327136673Sglebius * Device methods 328136673Sglebius ******************************************************************************/ 329136673Sglebius 330136673Sglebius/* 331136673Sglebius * the device is opened 332136673Sglebius */ 333136673Sglebiusstatic int 334130585Sphkngdopen(struct cdev *dev, int flag, int mode, struct thread *td) 33598402Sjulian{ 336136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 33798402Sjulian 338137022Sglebius DBG; 339137022Sglebius 340136673Sglebius mtx_lock(&priv->ngd_mtx); 341136673Sglebius priv->flags |= NGDF_OPEN; 342136673Sglebius mtx_unlock(&priv->ngd_mtx); 34398402Sjulian 34498402Sjulian return(0); 34598402Sjulian} 34698402Sjulian 34798402Sjulian/* 348136673Sglebius * the device is closed 34998402Sjulian */ 35098402Sjulianstatic int 351130585Sphkngdclose(struct cdev *dev, int flag, int mode, struct thread *td) 35298402Sjulian{ 353136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 35498402Sjulian 355137022Sglebius DBG; 356136673Sglebius mtx_lock(&priv->ngd_mtx); 357136673Sglebius priv->flags &= ~NGDF_OPEN; 358136673Sglebius mtx_unlock(&priv->ngd_mtx); 35998402Sjulian 36098402Sjulian return(0); 36198402Sjulian} 36298402Sjulian 363136673Sglebius#if 0 /* 364136673Sglebius * The ioctl is transformed into netgraph control message. 365136673Sglebius * We do not process them, yet. 366136673Sglebius */ 36798402Sjulian/* 36898402Sjulian * process ioctl 36998402Sjulian * 37098402Sjulian * they are translated into netgraph messages and passed on 371136673Sglebius * 37298402Sjulian */ 37398402Sjulianstatic int 374130585Sphkngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 37598402Sjulian{ 37698402Sjulian struct ngd_softc *sc = &ngd_softc; 37798402Sjulian struct ngd_connection * connection = NULL; 37898402Sjulian struct ngd_connection * tmp; 37998402Sjulian int error = 0; 38098402Sjulian struct ng_mesg *msg; 381136673Sglebius struct ngd_param_s * datap; 38298402Sjulian 383137022Sglebius DBG; 38498402Sjulian 38598402Sjulian SLIST_FOREACH(tmp,&sc->head,links) { 38698402Sjulian if(tmp->ngddev == dev) { 38798402Sjulian connection = tmp; 38898402Sjulian } 38998402Sjulian } 39098402Sjulian if(connection == NULL) { 39198402Sjulian printf("%s(): connection is still NULL, no dev found\n",__func__); 39298402Sjulian return(-1); 39398402Sjulian } 39498402Sjulian 395136673Sglebius NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s), 39698402Sjulian M_NOWAIT); 39798402Sjulian if (msg == NULL) { 39898402Sjulian printf("%s(): msg == NULL\n",__func__); 39998402Sjulian goto nomsg; 40098402Sjulian } 40198402Sjulian 40298402Sjulian /* pass the ioctl data into the ->data area */ 40398402Sjulian datap = (struct ngd_param_s *)msg->data; 404136673Sglebius datap->p = addr; 40598402Sjulian 406132446Sglebius NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0); 40798402Sjulian if(error) 40898402Sjulian printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error); 40998402Sjulian 41098402Sjuliannomsg: 41198402Sjulian 41298402Sjulian return(0); 41398402Sjulian} 414136673Sglebius#endif /* if 0 */ 41598402Sjulian 41698402Sjulian/* 41798402Sjulian * This function is called when a read(2) is done to our device. 418136673Sglebius * We process one mbuf from queue. 41998402Sjulian */ 42098402Sjulianstatic int 421130585Sphkngdread(struct cdev *dev, struct uio *uio, int flag) 42298402Sjulian{ 423136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 424136673Sglebius struct mbuf *m; 425136673Sglebius int len, error = 0; 42698402Sjulian 427137022Sglebius DBG; 42898402Sjulian 429136673Sglebius /* get an mbuf */ 430136673Sglebius do { 431136673Sglebius IF_DEQUEUE(&priv->readq, m); 432136673Sglebius if (m == NULL) { 433136673Sglebius if (flag & IO_NDELAY) 434136673Sglebius return (EWOULDBLOCK); 435136673Sglebius mtx_lock(&priv->ngd_mtx); 436136673Sglebius priv->flags |= NGDF_RWAIT; 437139331Srik if ((error = msleep(priv, &priv->ngd_mtx, 438139331Srik PDROP | PCATCH | (PZERO + 1), 439136673Sglebius "ngdread", 0)) != 0) 440136673Sglebius return (error); 44198402Sjulian } 442136673Sglebius } while (m == NULL); 443136673Sglebius 444136673Sglebius while (m && uio->uio_resid > 0 && error == 0) { 445136673Sglebius len = MIN(uio->uio_resid, m->m_len); 446136673Sglebius if (len != 0) 447136673Sglebius error = uiomove(mtod(m, void *), len, uio); 448136673Sglebius m = m_free(m); 44998402Sjulian } 45098402Sjulian 451136673Sglebius if (m) 452136673Sglebius m_freem(m); 45398402Sjulian 454136673Sglebius return (error); 45598402Sjulian} 45698402Sjulian 45798402Sjulian 458136673Sglebius/* 45998402Sjulian * This function is called when our device is written to. 460136673Sglebius * We read the data from userland into mbuf chain and pass it to the remote hook. 46198402Sjulian * 46298402Sjulian */ 46398402Sjulianstatic int 464130585Sphkngdwrite(struct cdev *dev, struct uio *uio, int flag) 46598402Sjulian{ 466136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 467136673Sglebius struct mbuf *m; 46898402Sjulian int error = 0; 46998402Sjulian 470137022Sglebius DBG; 47198402Sjulian 472136673Sglebius if (uio->uio_resid == 0) 473136673Sglebius return (0); 47498402Sjulian 475136673Sglebius if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET) 476136673Sglebius return (EIO); 47798402Sjulian 478136673Sglebius if ((m = m_uiotombuf(uio, M_DONTWAIT, 0)) == NULL) 479136673Sglebius return (ENOBUFS); 48098402Sjulian 481136673Sglebius NG_SEND_DATA_ONLY(error, priv->hook, m); 48298402Sjulian 483136673Sglebius return (error); 48498402Sjulian} 48598402Sjulian 48698402Sjulian/* 48798402Sjulian * we are being polled/selected 48898402Sjulian * check if there is data available for read 48998402Sjulian */ 49098402Sjulianstatic int 491130585Sphkngdpoll(struct cdev *dev, int events, struct thread *td) 49298402Sjulian{ 493136673Sglebius priv_p priv = (priv_p )dev->si_drv1; 49498402Sjulian int revents = 0; 49598402Sjulian 496136673Sglebius if (events & (POLLIN | POLLRDNORM) && 497136673Sglebius !IFQ_IS_EMPTY(&priv->readq)) 498136673Sglebius revents |= events & (POLLIN | POLLRDNORM); 49998402Sjulian 500136673Sglebius return (revents); 501136673Sglebius} 502136673Sglebius 503136673Sglebius/****************************************************************************** 504136673Sglebius * Helper subroutines 505136673Sglebius ******************************************************************************/ 506136673Sglebius 507136673Sglebiusstatic int 508136673Sglebiusget_free_unit() 509136673Sglebius{ 510136673Sglebius struct ngd_private *priv = NULL; 511136673Sglebius int n = 0; 512136673Sglebius int unit = -1; 513136673Sglebius 514137022Sglebius DBG; 515136673Sglebius 516136673Sglebius mtx_assert(&ng_device_mtx, MA_OWNED); 517136673Sglebius 518136673Sglebius /* When there is no list yet, the first device unit is always 0. */ 519136673Sglebius if SLIST_EMPTY(&ngd_nodes) 520136673Sglebius return(0); 521136673Sglebius 522136673Sglebius /* Just do a brute force loop to find the first free unit that is 523136673Sglebius * smaller than MAX_NGD. 524136673Sglebius * Set MAX_NGD to a large value, doesn't impact performance. 525136673Sglebius */ 526136673Sglebius for(n = 0; n<MAX_NGD && unit == -1; n++) { 527136673Sglebius SLIST_FOREACH(priv, &ngd_nodes, links) { 528136673Sglebius 529136673Sglebius if(priv->unit == n) { 530136673Sglebius unit = -1; 531136673Sglebius break; 53298402Sjulian } 533136673Sglebius unit = n; 53498402Sjulian } 53598402Sjulian } 53698402Sjulian 537136673Sglebius return (unit); 53898402Sjulian} 539141341Sru 540141341Sru/* 541141341Sru * Handle loading and unloading for this node type. 542141341Sru */ 543141341Srustatic int 544141341Srung_device_mod_event(module_t mod, int event, void *data) 545141341Sru{ 546141341Sru int error = 0; 547141341Sru 548141341Sru switch (event) { 549141341Sru case MOD_LOAD: 550141341Sru mtx_init(&ng_device_mtx, "ng_device", NULL, MTX_DEF); 551141341Sru break; 552141341Sru case MOD_UNLOAD: 553141341Sru mtx_destroy(&ng_device_mtx); 554141341Sru break; 555141341Sru default: 556141341Sru error = EOPNOTSUPP; 557141341Sru break; 558141341Sru } 559141341Sru return (error); 560141341Sru} 561