The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netgraph/ng_device.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * Copyright (c) 2002 Mark Santcroos <marks@ripe.net>
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  * 3. The name of the author may not be used to endorse or promote products
   13  *    derived from this software without specific prior written permission.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   25  *
   26  * Netgraph "device" node
   27  *
   28  * This node presents a /dev/ngd%d device that interfaces to an other 
   29  * netgraph node.
   30  *
   31  * $FreeBSD: releng/5.0/sys/netgraph/ng_device.c 98402 2002-06-18 21:32:33Z julian $
   32  *
   33  */
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/kernel.h>
   38 #include <sys/mbuf.h>
   39 #include <sys/uio.h>
   40 #include <sys/queue.h>
   41 #include <sys/malloc.h>
   42 #include <sys/conf.h>
   43 #include <sys/poll.h>
   44 #include <sys/ioccom.h>
   45 
   46 #include <netgraph/ng_message.h>
   47 #include <netgraph/netgraph.h>
   48 
   49 #include "ng_device.h"
   50 
   51 /* turn this on for verbose messages */
   52 #define NGD_DEBUG
   53 
   54 /* Netgraph methods */
   55 static ng_constructor_t ng_device_cons;
   56 static ng_rcvmsg_t      ng_device_rcvmsg;
   57 static ng_newhook_t     ng_device_newhook;
   58 static ng_connect_t     ng_device_connect;
   59 static ng_rcvdata_t     ng_device_rcvdata;
   60 static ng_disconnect_t  ng_device_disconnect;
   61 static int              ng_device_mod_event(module_t mod, int event, void *data);
   62 
   63 static int ng_device_init(void);
   64 static int get_free_unit(void);
   65 
   66 /* Netgraph type */
   67 static struct ng_type typestruct = {
   68         NG_ABI_VERSION,                 /* version */
   69         NG_DEVICE_NODE_TYPE,            /* name */
   70         ng_device_mod_event,            /* modevent */
   71         ng_device_cons,                 /* constructor */
   72         ng_device_rcvmsg,               /* receive msg */
   73         NULL,                           /* shutdown */
   74         ng_device_newhook,              /* newhook */
   75         NULL,                           /* findhook */
   76         ng_device_connect,              /* connect */
   77         ng_device_rcvdata,              /* receive data */
   78         ng_device_disconnect,           /* disconnect */
   79         NULL
   80 };
   81 NETGRAPH_INIT(device, &typestruct);
   82 
   83 /* per hook data */
   84 struct ngd_connection {
   85         SLIST_ENTRY(ngd_connection) links;
   86 
   87         dev_t   ngddev;
   88         struct  ng_hook *active_hook;
   89         char    *readq;
   90         int     loc;
   91         int     unit;
   92 };
   93 
   94 /* global data */
   95 struct ngd_softc {
   96         SLIST_HEAD(, ngd_connection) head;
   97 
   98         node_p node;
   99         char nodename[NG_NODELEN + 1];
  100 } ngd_softc;
  101 
  102 /* helper definition */
  103 #define         MIN(a, b)       ((a) < (b) ? (a) : (b))
  104 
  105 /* the per connection receiving queue maximum */
  106 #define NGD_QUEUE_SIZE (1024*10)
  107 
  108 /* Maximum number of NGD devices */
  109 #define MAX_NGD 25              /* should be more than enough for now */
  110 
  111 static d_close_t ngdclose;
  112 static d_open_t ngdopen;
  113 static d_read_t ngdread;
  114 static d_write_t ngdwrite;
  115 static d_ioctl_t ngdioctl;
  116 static d_poll_t ngdpoll;
  117 
  118 #define NGD_CDEV_MAJOR 20
  119 static struct cdevsw ngd_cdevsw = {
  120         /* open */      ngdopen,
  121         /* close */     ngdclose,
  122         /* read */      ngdread,
  123         /* write */     ngdwrite,
  124         /* ioctl */     ngdioctl,
  125         /* poll */      ngdpoll,
  126         /* mmap */      nommap,
  127         /* strategy */  nostrategy,
  128         /* name */      "ngd",
  129         /* maj */       NGD_CDEV_MAJOR,
  130         /* dump */      nodump,
  131         /* psize */     nopsize,
  132         /* flags */     0,
  133 };
  134 
  135 /* 
  136  * this holds all the stuff that should be done at load time 
  137  */
  138 static int
  139 ng_device_mod_event(module_t mod, int event, void *data)
  140 {
  141         int error = 0;
  142 
  143 #ifdef NGD_DEBUG
  144         printf("%s()\n",__func__);
  145 #endif /* NGD_DEBUG */
  146 
  147         switch (event) {
  148                 case MOD_LOAD:
  149 
  150                         ng_device_init();
  151                         break;
  152 
  153                 case MOD_UNLOAD:
  154                         /* XXX do we need to do something specific ? */
  155                         /* ng_device_breakdown */
  156                         break;
  157                 
  158                 default:
  159                         error = EOPNOTSUPP;
  160                         break;
  161         }
  162 
  163         return(error);
  164 }
  165 
  166 
  167 static int
  168 ng_device_init()
  169 {
  170         struct ngd_softc *sc = &ngd_softc;
  171         
  172 #ifdef NGD_DEBUG
  173         printf("%s()\n",__func__);
  174 #endif /* NGD_DEBUG */ 
  175 
  176         SLIST_INIT(&sc->head);
  177 
  178         if (ng_make_node_common(&typestruct, &sc->node) != 0) {
  179                 printf("%s(): ng_make_node_common failed\n",__func__);
  180                 return(ENXIO);
  181         }
  182         sprintf(sc->nodename, "%s", NG_DEVICE_NODE_TYPE);
  183         if (ng_name_node(sc->node, sc->nodename)) {
  184                 NG_NODE_UNREF(sc->node); /* make it go away again */
  185                 printf("%s(): ng_name_node failed\n",__func__);
  186                 return(ENXIO);
  187         }
  188         NG_NODE_SET_PRIVATE(sc->node, sc);
  189 
  190         return(0);
  191 }
  192 
  193 /* 
  194  * don't allow to be created, only the device can do that 
  195  */
  196 static int
  197 ng_device_cons(node_p node)
  198 {
  199 
  200 #ifdef NGD_DEBUG
  201         printf("%s()\n",__func__);
  202 #endif /* NGD_DEBUG */
  203         
  204         return(EINVAL);
  205 }
  206 
  207 /*
  208  * Receive control message. We just bounce it back as a reply.
  209  */
  210 static int
  211 ng_device_rcvmsg(node_p node, item_p item, hook_p lasthook)
  212 {
  213         struct ngd_softc *sc = &ngd_softc;
  214         struct ng_mesg *msg;
  215         int error = 0;
  216         struct ngd_connection * connection = NULL;
  217         struct ngd_connection *tmp = NULL;
  218 
  219 #ifdef NGD_DEBUG
  220         printf("%s()\n",__func__);
  221 #endif /* NGD_DEBUG */
  222 
  223         NGI_GET_MSG(item, msg); 
  224 
  225         SLIST_FOREACH(tmp,&sc->head,links) {
  226                 if(tmp->active_hook == lasthook) {
  227                         connection = tmp;
  228                 }
  229         }
  230         if(connection == NULL) {
  231                 printf("%s(): connection is still NULL, no hook found\n",__func__);
  232                 return(-1);
  233         }
  234 
  235         return(error);
  236 }
  237 
  238 static int
  239 get_free_unit()
  240 {
  241         struct ngd_connection *tmp = NULL;
  242         struct ngd_softc *sc = &ngd_softc;
  243         int n = 0;
  244         int unit = -1;
  245 
  246 #ifdef NGD_DEBUG
  247         printf("%s()\n",__func__);
  248 #endif /* NGD_DEBUG */
  249 
  250         /* When there is no list yet, the first device unit is always 0. */
  251         if SLIST_EMPTY(&sc->head) {
  252                 unit = 0;
  253                 return(unit);
  254         }
  255 
  256         /* Just do a brute force loop to find the first free unit that is
  257          * smaller than MAX_NGD.
  258          * Set MAX_NGD to a large value, doesn't impact performance.
  259          */
  260         for(n = 0;n<MAX_NGD && unit == -1;n++) {
  261                 SLIST_FOREACH(tmp,&sc->head,links) {
  262 
  263                         if(tmp->unit == n) {
  264                                 unit = -1;
  265                                 break;
  266                         }
  267                         unit = n;
  268                 }
  269         }
  270 
  271         return(unit);
  272 }
  273 
  274 /*
  275  * incoming hook
  276  */
  277 static int
  278 ng_device_newhook(node_p node, hook_p hook, const char *name)
  279 {
  280         struct ngd_softc *sc = &ngd_softc;
  281         struct ngd_connection * new_connection = NULL;
  282 
  283 #ifdef NGD_DEBUG
  284         printf("%s()\n",__func__);
  285 #endif /* NGD_DEBUG */
  286 
  287         new_connection = malloc(sizeof(struct ngd_connection), M_DEVBUF, M_NOWAIT);
  288         if(new_connection == NULL) {
  289                 printf("%s(): ERROR: new_connection == NULL\n",__func__);
  290                 return(-1);
  291         }
  292 
  293         new_connection->unit = get_free_unit();
  294         if(new_connection->unit<0) {
  295                 printf("%s: No free unit found by get_free_unit(), "
  296                                 "increas MAX_NGD\n",__func__);
  297                 return(-1);
  298         }
  299         new_connection->ngddev = make_dev(&ngd_cdevsw, new_connection->unit, 0, 0,0600,"ngd%d",new_connection->unit);
  300         if(new_connection->ngddev == NULL) {
  301                 printf("%s(): make_dev failed\n",__func__);
  302                 return(-1);
  303         }
  304 
  305         new_connection->readq = malloc(sizeof(char)*NGD_QUEUE_SIZE, M_DEVBUF, M_NOWAIT | M_ZERO);
  306         if(new_connection->readq == NULL) {
  307                 printf("%s(): readq malloc failed\n",__func__);
  308                 return(-1);
  309         }
  310 
  311         /* point to begin of buffer */
  312         new_connection->loc = 0;
  313         new_connection->active_hook = hook;
  314 
  315         SLIST_INSERT_HEAD(&sc->head, new_connection, links);
  316 
  317         return(0);
  318 }
  319 
  320 /*
  321  * we gave ok to a new hook
  322  * now connect
  323  */
  324 static int
  325 ng_device_connect(hook_p hook)
  326 {
  327 
  328 #ifdef NGD_DEBUG
  329         printf("%s()\n",__func__);
  330 #endif /* NGD_DEBUG */
  331 
  332         return(0);
  333 }
  334 
  335 
  336 /*
  337  * Receive data from hook
  338  */
  339 static int
  340 ng_device_rcvdata(hook_p hook, item_p item)
  341 {
  342         struct mbuf *m;
  343         struct ngd_softc *sc = &ngd_softc;
  344         struct ngd_connection * connection = NULL;
  345         struct ngd_connection * tmp;
  346         char *buffer;
  347 
  348 #ifdef NGD_DEBUG
  349         printf("%s()\n",__func__);
  350 #endif /* NGD_DEBUG */
  351 
  352         SLIST_FOREACH(tmp,&sc->head,links) {
  353                 if(tmp->active_hook == hook) {
  354                         connection = tmp;
  355                 }
  356         }
  357         if(connection == NULL) {
  358                 printf("%s(): connection is still NULL, no hook found\n",__func__);
  359                 return(-1);
  360         }
  361 
  362         NGI_GET_M(item, m);
  363         NG_FREE_ITEM(item);
  364 
  365         m = m_pullup(m,m->m_len);
  366         if(m == NULL) {
  367                 printf("%s(): ERROR: m_pullup failed\n",__func__);
  368                 return(-1);
  369         }
  370 
  371         buffer = malloc(sizeof(char)*m->m_len, M_DEVBUF, M_NOWAIT | M_ZERO);
  372         if(buffer == NULL) {
  373                 printf("%s(): ERROR: buffer malloc failed\n",__func__);
  374                 return(-1);
  375         }
  376 
  377         buffer = mtod(m,char *);
  378 
  379         if( (connection->loc+m->m_len) < NGD_QUEUE_SIZE) {
  380                 memcpy(connection->readq+connection->loc, buffer, m->m_len);
  381                 connection->loc += m->m_len;
  382         } else
  383                 printf("%s(): queue full, first read out a bit\n",__func__);
  384 
  385         free(buffer,M_DEVBUF);
  386 
  387         return(0);
  388 }
  389 
  390 /*
  391  * Removal of the last link destroys the node
  392  */
  393 static int
  394 ng_device_disconnect(hook_p hook)
  395 {
  396         struct ngd_softc *sc = &ngd_softc;
  397         struct ngd_connection * connection = NULL;
  398         struct ngd_connection * tmp;
  399 
  400 #ifdef NGD_DEBUG
  401         printf("%s()\n",__func__);
  402 #endif /* NGD_DEBUG */
  403 
  404         SLIST_FOREACH(tmp,&sc->head,links) {
  405                 if(tmp->active_hook == hook) {
  406                         connection = tmp;
  407                 }
  408         }
  409         if(connection == NULL) {
  410                 printf("%s(): connection is still NULL, no hook found\n",__func__);
  411                 return(-1);
  412         }
  413 
  414         free(connection->readq,M_DEVBUF);
  415 
  416         destroy_dev(connection->ngddev);
  417 
  418         SLIST_REMOVE(&sc->head,connection,ngd_connection,links);
  419 
  420         return(0);
  421 }
  422 /*
  423  * the device is opened 
  424  */
  425 static int
  426 ngdopen(dev_t dev, int flag, int mode, struct thread *td)
  427 {
  428 
  429 #ifdef NGD_DEBUG
  430         printf("%s()\n",__func__);
  431 #endif /* NGD_DEBUG */
  432 
  433         return(0);
  434 }
  435 
  436 /*
  437  * the device is closed 
  438  */
  439 static int
  440 ngdclose(dev_t dev, int flag, int mode, struct thread *td)
  441 {
  442 
  443 #ifdef NGD_DEBUG
  444         printf("%s()\n",__func__);
  445 #endif
  446 
  447         return(0);
  448 }
  449 
  450 
  451 /*
  452  * process ioctl
  453  *
  454  * they are translated into netgraph messages and passed on
  455  * 
  456  */
  457 static int
  458 ngdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
  459 {
  460         struct ngd_softc *sc = &ngd_softc;
  461         struct ngd_connection * connection = NULL;
  462         struct ngd_connection * tmp;
  463         int error = 0;
  464         struct ng_mesg *msg;
  465         struct ngd_param_s * datap;
  466 
  467 #ifdef NGD_DEBUG
  468         printf("%s()\n",__func__);
  469 #endif /* NGD_DEBUG */
  470 
  471         SLIST_FOREACH(tmp,&sc->head,links) {
  472                 if(tmp->ngddev == dev) {
  473                         connection = tmp;
  474                 }
  475         }
  476         if(connection == NULL) {
  477                 printf("%s(): connection is still NULL, no dev found\n",__func__);
  478                 return(-1);
  479         }
  480 
  481         /* NG_MKMESSAGE(msg, cookie, cmdid, len, how) */
  482         NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s), 
  483                         M_NOWAIT);
  484         if (msg == NULL) {
  485                 printf("%s(): msg == NULL\n",__func__);
  486                 goto nomsg;
  487         }
  488 
  489         /* pass the ioctl data into the ->data area */
  490         datap = (struct ngd_param_s *)msg->data;
  491         datap->p = addr;
  492 
  493         /* NG_SEND_MSG_HOOK(error, here, msg, hook, retaddr) */
  494         NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, NULL);
  495         if(error)
  496                 printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error);
  497 
  498 nomsg:
  499 
  500         return(0);
  501 }
  502 
  503 
  504 /*
  505  * This function is called when a read(2) is done to our device.
  506  * We pass the data available in kernelspace on into userland using
  507  * uiomove.
  508  */
  509 static int
  510 ngdread(dev_t dev, struct uio *uio, int flag)
  511 {
  512         int ret = 0, amnt;
  513         char buffer[uio->uio_resid+1];
  514         struct ngd_softc *sc = &ngd_softc;
  515         struct ngd_connection * connection = NULL;
  516         struct ngd_connection * tmp;
  517 
  518 #ifdef NGD_DEBUG
  519         printf("%s()\n",__func__);
  520 #endif /* NGD_DEBUG */
  521 
  522         SLIST_FOREACH(tmp,&sc->head,links) {
  523                 if(tmp->ngddev == dev) {
  524                         connection = tmp;
  525                 }
  526         }
  527         if(connection == NULL) {
  528                 printf("%s(): connection is still NULL, no dev found\n",__func__);
  529                 return(-1);
  530         }
  531 
  532         while ( ( uio->uio_resid > 0 ) && ( connection->loc > 0 ) ) {
  533                 amnt = MIN(uio->uio_resid,connection->loc);
  534 
  535                 memcpy(buffer,connection->readq, amnt);
  536                 memcpy(connection->readq, connection->readq+amnt,
  537                                 connection->loc-amnt);
  538                 connection->loc -= amnt;
  539 
  540                 ret = uiomove((caddr_t)buffer, amnt, uio);
  541                 if(ret != 0)
  542                         goto error;
  543 
  544         }
  545         return(0);
  546 
  547 error:
  548         printf("%s(): uiomove returns error %d\n",__func__,ret);
  549         /* do error cleanup here */
  550         return(ret);
  551 }
  552 
  553 
  554 /* 
  555  * This function is called when our device is written to.
  556  * We read the data from userland into our local buffer and pass it on
  557  * into the remote hook.
  558  *
  559  */
  560 static int
  561 ngdwrite(dev_t dev, struct uio *uio, int flag)
  562 {
  563         int ret;
  564         int error = 0;
  565         struct mbuf *m;
  566         char buffer[uio->uio_resid];
  567         int len = uio->uio_resid;
  568         struct ngd_softc *sc =& ngd_softc;
  569         struct ngd_connection * connection = NULL;
  570         struct ngd_connection * tmp;
  571 
  572 #ifdef NGD_DEBUG
  573         printf("%s()\n",__func__);
  574 #endif /* NGD_DEBUG */
  575 
  576         SLIST_FOREACH(tmp,&sc->head,links) {
  577                 if(tmp->ngddev == dev) {
  578                         connection = tmp;
  579                 }
  580         }
  581 
  582         if(connection == NULL) {
  583                 printf("%s(): connection is still NULL, no dev found\n",__func__);
  584                 return(-1);
  585         }
  586 
  587         if (len > 0) {
  588                 if ((ret = uiomove((caddr_t)buffer, len, uio)) != 0)
  589                         goto error;
  590         } else
  591                 printf("%s(): len <= 0 : is this supposed to happen?!\n",__func__);
  592 
  593         m = m_devget(buffer,len,0,NULL,NULL);
  594 
  595         NG_SEND_DATA_ONLY(error,connection->active_hook,m);
  596 
  597         return(0);
  598 
  599 error:
  600         /* do error cleanup here */
  601         printf("%s(): uiomove returned err: %d\n",__func__,ret);
  602 
  603         return(ret);
  604 }
  605 
  606 /*
  607  * we are being polled/selected
  608  * check if there is data available for read
  609  */
  610 static int
  611 ngdpoll(dev_t dev, int events, struct thread *td)
  612 {
  613         int revents = 0;
  614         struct ngd_softc *sc = &ngd_softc;
  615         struct ngd_connection * connection = NULL;
  616         struct ngd_connection * tmp;
  617 
  618 
  619         if (events & (POLLIN | POLLRDNORM)) {
  620                 /* get the connection we have to know the loc from */
  621                 SLIST_FOREACH(tmp,&sc->head,links) {
  622                         if(tmp->ngddev == dev) {
  623                                 connection = tmp;
  624                         }
  625                 }
  626                 if(connection == NULL) {
  627                         printf("%s(): ERROR: connection is still NULL,"
  628                                 "no dev found\n",__func__);
  629                         return(-1);
  630                 }
  631 
  632                 if (connection->loc > 0)
  633                         revents |= events & (POLLIN | POLLRDNORM);
  634         }
  635 
  636         return(revents);
  637 }

Cache object: 2c2f34423720c3816d67424c49f109d9


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.