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/fs/fuse/fuse_node.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-3-Clause
    3  *
    4  * Copyright (c) 2007-2009 Google Inc. and Amit Singh
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions are
    9  * met:
   10  *
   11  * * Redistributions of source code must retain the above copyright
   12  *   notice, this list of conditions and the following disclaimer.
   13  * * Redistributions in binary form must reproduce the above
   14  *   copyright notice, this list of conditions and the following disclaimer
   15  *   in the documentation and/or other materials provided with the
   16  *   distribution.
   17  * * Neither the name of Google Inc. nor the names of its
   18  *   contributors may be used to endorse or promote products derived from
   19  *   this software without specific prior written permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   32  *
   33  * Copyright (C) 2005 Csaba Henk.
   34  * All rights reserved.
   35  *
   36  * Copyright (c) 2019 The FreeBSD Foundation
   37  *
   38  * Portions of this software were developed by BFF Storage Systems, LLC under
   39  * sponsorship from the FreeBSD Foundation.
   40  *
   41  * Redistribution and use in source and binary forms, with or without
   42  * modification, are permitted provided that the following conditions
   43  * are met:
   44  * 1. Redistributions of source code must retain the above copyright
   45  *    notice, this list of conditions and the following disclaimer.
   46  * 2. Redistributions in binary form must reproduce the above copyright
   47  *    notice, this list of conditions and the following disclaimer in the
   48  *    documentation and/or other materials provided with the distribution.
   49  *
   50  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   53  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
   54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   60  * SUCH DAMAGE.
   61  */
   62 
   63 #include <sys/cdefs.h>
   64 __FBSDID("$FreeBSD$");
   65 
   66 #include <sys/types.h>
   67 #include <sys/systm.h>
   68 #include <sys/counter.h>
   69 #include <sys/module.h>
   70 #include <sys/errno.h>
   71 #include <sys/param.h>
   72 #include <sys/kernel.h>
   73 #include <sys/conf.h>
   74 #include <sys/uio.h>
   75 #include <sys/malloc.h>
   76 #include <sys/queue.h>
   77 #include <sys/lock.h>
   78 #include <sys/sx.h>
   79 #include <sys/mutex.h>
   80 #include <sys/proc.h>
   81 #include <sys/vnode.h>
   82 #include <sys/namei.h>
   83 #include <sys/mount.h>
   84 #include <sys/sysctl.h>
   85 #include <sys/fcntl.h>
   86 #include <sys/priv.h>
   87 #include <sys/buf.h>
   88 #include <security/mac/mac_framework.h>
   89 #include <vm/vm.h>
   90 #include <vm/vm_extern.h>
   91 
   92 #include "fuse.h"
   93 #include "fuse_node.h"
   94 #include "fuse_internal.h"
   95 #include "fuse_io.h"
   96 #include "fuse_ipc.h"
   97 
   98 SDT_PROVIDER_DECLARE(fusefs);
   99 /* 
  100  * Fuse trace probe:
  101  * arg0: verbosity.  Higher numbers give more verbose messages
  102  * arg1: Textual message
  103  */
  104 SDT_PROBE_DEFINE2(fusefs, , node, trace, "int", "char*");
  105 
  106 MALLOC_DEFINE(M_FUSEVN, "fuse_vnode", "fuse vnode private data");
  107 
  108 static int sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS);
  109 
  110 static counter_u64_t fuse_node_count;
  111 
  112 SYSCTL_COUNTER_U64(_vfs_fusefs_stats, OID_AUTO, node_count, CTLFLAG_RD,
  113     &fuse_node_count, "Count of FUSE vnodes");
  114 
  115 int     fuse_data_cache_mode = FUSE_CACHE_WT;
  116 
  117 /*
  118  * DEPRECATED
  119  * This sysctl is no longer needed as of fuse protocol 7.23.  Individual
  120  * servers can select the cache behavior they need for each mountpoint:
  121  * - writethrough: the default
  122  * - writeback: set FUSE_WRITEBACK_CACHE in fuse_init_out.flags
  123  * - uncached: set FOPEN_DIRECT_IO for every file
  124  * The sysctl is retained primarily for use by jails supporting older FUSE
  125  * protocols.  It may be removed entirely once FreeBSD 11.3 and 12.0 are EOL.
  126  */
  127 SYSCTL_PROC(_vfs_fusefs, OID_AUTO, data_cache_mode, CTLTYPE_INT|CTLFLAG_RW,
  128     &fuse_data_cache_mode, 0, sysctl_fuse_cache_mode, "I",
  129     "Zero: disable caching of FUSE file data; One: write-through caching "
  130     "(default); Two: write-back caching (generally unsafe)");
  131 
  132 static int
  133 sysctl_fuse_cache_mode(SYSCTL_HANDLER_ARGS)
  134 {
  135         int val, error;
  136 
  137         val = *(int *)arg1;
  138         error = sysctl_handle_int(oidp, &val, 0, req);
  139         if (error || !req->newptr)
  140                 return (error);
  141 
  142         switch (val) {
  143         case FUSE_CACHE_UC:
  144         case FUSE_CACHE_WT:
  145         case FUSE_CACHE_WB:
  146                 *(int *)arg1 = val;
  147                 break;
  148         default:
  149                 return (EDOM);
  150         }
  151         return (0);
  152 }
  153 
  154 static void
  155 fuse_vnode_init(struct vnode *vp, struct fuse_vnode_data *fvdat,
  156     uint64_t nodeid, enum vtype vtyp)
  157 {
  158         fvdat->nid = nodeid;
  159         LIST_INIT(&fvdat->handles);
  160         vattr_null(&fvdat->cached_attrs);
  161         if (nodeid == FUSE_ROOT_ID) {
  162                 vp->v_vflag |= VV_ROOT;
  163         }
  164         vp->v_type = vtyp;
  165         vp->v_data = fvdat;
  166         timespecclear(&fvdat->last_local_modify);
  167 
  168         counter_u64_add(fuse_node_count, 1);
  169 }
  170 
  171 void
  172 fuse_vnode_destroy(struct vnode *vp)
  173 {
  174         struct fuse_vnode_data *fvdat = vp->v_data;
  175 
  176         vp->v_data = NULL;
  177         KASSERT(LIST_EMPTY(&fvdat->handles),
  178                 ("Destroying fuse vnode with open files!"));
  179         free(fvdat, M_FUSEVN);
  180 
  181         counter_u64_add(fuse_node_count, -1);
  182 }
  183 
  184 int
  185 fuse_vnode_cmp(struct vnode *vp, void *nidp)
  186 {
  187         return (VTOI(vp) != *((uint64_t *)nidp));
  188 }
  189 
  190 SDT_PROBE_DEFINE3(fusefs, , node, stale_vnode, "struct vnode*", "enum vtype",
  191                 "uint64_t");
  192 static int
  193 fuse_vnode_alloc(struct mount *mp,
  194     struct thread *td,
  195     uint64_t nodeid,
  196     enum vtype vtyp,
  197     struct vnode **vpp)
  198 {
  199         struct fuse_data *data;
  200         struct fuse_vnode_data *fvdat;
  201         struct vnode *vp2;
  202         int err = 0;
  203 
  204         data = fuse_get_mpdata(mp);
  205         if (vtyp == VNON) {
  206                 return EINVAL;
  207         }
  208         *vpp = NULL;
  209         err = vfs_hash_get(mp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE, td, vpp,
  210             fuse_vnode_cmp, &nodeid);
  211         if (err)
  212                 return (err);
  213 
  214         if (*vpp) {
  215                 if ((*vpp)->v_type == vtyp) {
  216                         /* Reuse a vnode that hasn't yet been reclaimed */
  217                         MPASS((*vpp)->v_data != NULL);
  218                         MPASS(VTOFUD(*vpp)->nid == nodeid);
  219                         SDT_PROBE2(fusefs, , node, trace, 1,
  220                                 "vnode taken from hash");
  221                         return (0);
  222                 } else {
  223                         /*
  224                          * The inode changed types!  If we get here, we can't
  225                          * tell whether the inode's entry cache had expired
  226                          * yet.  So this could be the result of a buggy server,
  227                          * but more likely the server just reused an inode
  228                          * number following an entry cache expiration.
  229                          */
  230                         SDT_PROBE3(fusefs, , node, stale_vnode, *vpp, vtyp,
  231                                 nodeid);
  232                         fuse_internal_vnode_disappear(*vpp);
  233                         vgone(*vpp);
  234                         lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
  235                 }
  236         }
  237         fvdat = malloc(sizeof(*fvdat), M_FUSEVN, M_WAITOK | M_ZERO);
  238         switch (vtyp) {
  239         case VFIFO:
  240                 err = getnewvnode("fuse", mp, &fuse_fifoops, vpp);
  241                 break;
  242         default:
  243                 err = getnewvnode("fuse", mp, &fuse_vnops, vpp);
  244                 break;
  245         }
  246         if (err) {
  247                 free(fvdat, M_FUSEVN);
  248                 return (err);
  249         }
  250         lockmgr((*vpp)->v_vnlock, LK_EXCLUSIVE, NULL);
  251         fuse_vnode_init(*vpp, fvdat, nodeid, vtyp);
  252         err = insmntque(*vpp, mp);
  253         ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
  254         if (err) {
  255                 lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
  256                 free(fvdat, M_FUSEVN);
  257                 *vpp = NULL;
  258                 return (err);
  259         }
  260         /* Disallow async reads for fifos because UFS does.  I don't know why */
  261         if (data->dataflags & FSESS_ASYNC_READ && vtyp != VFIFO)
  262                 VN_LOCK_ASHARE(*vpp);
  263 
  264         err = vfs_hash_insert(*vpp, fuse_vnode_hash(nodeid), LK_EXCLUSIVE,
  265             td, &vp2, fuse_vnode_cmp, &nodeid);
  266         if (err) {
  267                 lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
  268                 free(fvdat, M_FUSEVN);
  269                 *vpp = NULL;
  270                 return (err);
  271         }
  272         if (vp2 != NULL) {
  273                 *vpp = vp2;
  274                 return (0);
  275         }
  276 
  277         ASSERT_VOP_ELOCKED(*vpp, "fuse_vnode_alloc");
  278 
  279         return (0);
  280 }
  281 
  282 int
  283 fuse_vnode_get(struct mount *mp,
  284     struct fuse_entry_out *feo,
  285     uint64_t nodeid,
  286     struct vnode *dvp,
  287     struct vnode **vpp,
  288     struct componentname *cnp,
  289     enum vtype vtyp)
  290 {
  291         struct thread *td = (cnp != NULL ? cnp->cn_thread : curthread);
  292         /* 
  293          * feo should only be NULL for the root directory, which (when libfuse
  294          * is used) always has generation 0
  295          */
  296         uint64_t generation = feo ? feo->generation : 0;
  297         int err = 0;
  298 
  299         if (dvp != NULL && VTOFUD(dvp)->nid == nodeid) {
  300                 fuse_warn(fuse_get_mpdata(mp), FSESS_WARN_ILLEGAL_INODE,
  301                         "Assigned same inode to both parent and child.");
  302                 return EIO;
  303         }
  304 
  305         err = fuse_vnode_alloc(mp, td, nodeid, vtyp, vpp);
  306         if (err) {
  307                 return err;
  308         }
  309         if (dvp != NULL) {
  310                 MPASS(cnp && (cnp->cn_flags & ISDOTDOT) == 0);
  311                 MPASS(cnp &&
  312                         !(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.'));
  313                 fuse_vnode_setparent(*vpp, dvp);
  314         }
  315         if (dvp != NULL && cnp != NULL && (cnp->cn_flags & MAKEENTRY) != 0 &&
  316             feo != NULL &&
  317             (feo->entry_valid != 0 || feo->entry_valid_nsec != 0)) {
  318                 struct timespec timeout;
  319 
  320                 ASSERT_VOP_LOCKED(*vpp, "fuse_vnode_get");
  321                 ASSERT_VOP_LOCKED(dvp, "fuse_vnode_get");
  322 
  323                 fuse_validity_2_timespec(feo, &timeout);
  324                 cache_enter_time(dvp, *vpp, cnp, &timeout, NULL);
  325         }
  326 
  327         VTOFUD(*vpp)->generation = generation;
  328         /*
  329          * In userland, libfuse uses cached lookups for dot and dotdot entries,
  330          * thus it does not really bump the nlookup counter for forget.
  331          * Follow the same semantic and avoid the bump in order to keep
  332          * nlookup counters consistent.
  333          */
  334         if (cnp == NULL || ((cnp->cn_flags & ISDOTDOT) == 0 &&
  335             (cnp->cn_namelen != 1 || cnp->cn_nameptr[0] != '.')))
  336                 VTOFUD(*vpp)->nlookup++;
  337 
  338         return 0;
  339 }
  340 
  341 /*
  342  * Called for every fusefs vnode open to initialize the vnode (not
  343  * fuse_filehandle) for use
  344  */
  345 void
  346 fuse_vnode_open(struct vnode *vp, int32_t fuse_open_flags, struct thread *td)
  347 {
  348         if (vnode_vtype(vp) == VREG)
  349                 vnode_create_vobject(vp, 0, td);
  350 }
  351 
  352 int
  353 fuse_vnode_savesize(struct vnode *vp, struct ucred *cred, pid_t pid)
  354 {
  355         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  356         struct thread *td = curthread;
  357         struct fuse_filehandle *fufh = NULL;
  358         struct fuse_dispatcher fdi;
  359         struct fuse_setattr_in *fsai;
  360         int err = 0;
  361 
  362         ASSERT_VOP_ELOCKED(vp, "fuse_io_extend");
  363 
  364         if (fuse_isdeadfs(vp)) {
  365                 return EBADF;
  366         }
  367         if (vnode_vtype(vp) == VDIR) {
  368                 return EISDIR;
  369         }
  370         if (vfs_isrdonly(vnode_mount(vp))) {
  371                 return EROFS;
  372         }
  373         if (cred == NULL) {
  374                 cred = td->td_ucred;
  375         }
  376         fdisp_init(&fdi, sizeof(*fsai));
  377         fdisp_make_vp(&fdi, FUSE_SETATTR, vp, td, cred);
  378         fsai = fdi.indata;
  379         fsai->valid = 0;
  380 
  381         /* Truncate to a new value. */
  382         MPASS((fvdat->flag & FN_SIZECHANGE) != 0);
  383         fsai->size = fvdat->cached_attrs.va_size;
  384         fsai->valid |= FATTR_SIZE;
  385 
  386         fuse_filehandle_getrw(vp, FWRITE, &fufh, cred, pid);
  387         if (fufh) {
  388                 fsai->fh = fufh->fh_id;
  389                 fsai->valid |= FATTR_FH;
  390         }
  391         err = fdisp_wait_answ(&fdi);
  392         fdisp_destroy(&fdi);
  393         if (err == 0) {
  394                 getnanouptime(&fvdat->last_local_modify);
  395                 fvdat->flag &= ~FN_SIZECHANGE;
  396         }
  397 
  398         return err;
  399 }
  400 
  401 /*
  402  * Adjust the vnode's size to a new value.
  403  *
  404  * If the new value came from the server, such as from a FUSE_GETATTR
  405  * operation, set `from_server` true.  But if it came from a local operation,
  406  * such as write(2) or truncate(2), set `from_server` false.
  407  */
  408 int
  409 fuse_vnode_setsize(struct vnode *vp, off_t newsize, bool from_server)
  410 {
  411         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  412         struct vattr *attrs;
  413         off_t oldsize;
  414         size_t iosize;
  415         struct buf *bp = NULL;
  416         int err = 0;
  417 
  418         ASSERT_VOP_ELOCKED(vp, "fuse_vnode_setsize");
  419 
  420         iosize = fuse_iosize(vp);
  421         oldsize = fvdat->cached_attrs.va_size;
  422         fvdat->cached_attrs.va_size = newsize;
  423         if ((attrs = VTOVA(vp)) != NULL)
  424                 attrs->va_size = newsize;
  425 
  426         if (newsize < oldsize) {
  427                 daddr_t lbn;
  428 
  429                 err = vtruncbuf(vp, newsize, fuse_iosize(vp));
  430                 if (err)
  431                         goto out;
  432                 if (newsize % iosize == 0)
  433                         goto out;
  434                 /* 
  435                  * Zero the contents of the last partial block.
  436                  * Sure seems like vtruncbuf should do this for us.
  437                  */
  438 
  439                 lbn = newsize / iosize;
  440                 bp = getblk(vp, lbn, iosize, PCATCH, 0, 0);
  441                 if (!bp) {
  442                         err = EINTR;
  443                         goto out;
  444                 }
  445                 if (!(bp->b_flags & B_CACHE))
  446                         goto out;       /* Nothing to do */
  447                 MPASS(bp->b_flags & B_VMIO);
  448                 vfs_bio_clrbuf(bp);
  449                 bp->b_dirtyend = MIN(bp->b_dirtyend, newsize - lbn * iosize);
  450         } else if (from_server && newsize > oldsize && oldsize != VNOVAL) {
  451                 /*
  452                  * The FUSE server changed the file size behind our back.  We
  453                  * should invalidate the entire cache.
  454                  */
  455                 daddr_t left_lbn, end_lbn;
  456 
  457                 left_lbn = oldsize / iosize;
  458                 end_lbn = howmany(newsize, iosize);
  459                 v_inval_buf_range(vp, 0, end_lbn, iosize);
  460         }
  461 out:
  462         if (bp)
  463                 brelse(bp);
  464         vnode_pager_setsize(vp, newsize);
  465         return err;
  466 }
  467         
  468 /* Get the current, possibly dirty, size of the file */
  469 int
  470 fuse_vnode_size(struct vnode *vp, off_t *filesize, struct ucred *cred,
  471         struct thread *td)
  472 {
  473         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  474         int error = 0;
  475 
  476         if (!(fvdat->flag & FN_SIZECHANGE) &&
  477                 (!fuse_vnode_attr_cache_valid(vp) ||
  478                   fvdat->cached_attrs.va_size == VNOVAL)) 
  479                 error = fuse_internal_do_getattr(vp, NULL, cred, td);
  480 
  481         if (!error)
  482                 *filesize = fvdat->cached_attrs.va_size;
  483 
  484         return error;
  485 }
  486 
  487 void
  488 fuse_vnode_undirty_cached_timestamps(struct vnode *vp, bool atime)
  489 {
  490         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  491 
  492         fvdat->flag &= ~(FN_MTIMECHANGE | FN_CTIMECHANGE);
  493         if (atime)
  494                 fvdat->flag &= ~FN_ATIMECHANGE;
  495 }
  496 
  497 /* Update a fuse file's cached timestamps */
  498 void
  499 fuse_vnode_update(struct vnode *vp, int flags)
  500 {
  501         struct fuse_vnode_data *fvdat = VTOFUD(vp);
  502         struct mount *mp = vnode_mount(vp);
  503         struct fuse_data *data = fuse_get_mpdata(mp);
  504         struct timespec ts;
  505 
  506         vfs_timestamp(&ts);
  507 
  508         if (data->time_gran > 1)
  509                 ts.tv_nsec = rounddown(ts.tv_nsec, data->time_gran);
  510 
  511         if (mp->mnt_flag & MNT_NOATIME)
  512                 flags &= ~FN_ATIMECHANGE;
  513 
  514         if (flags & FN_ATIMECHANGE)
  515                 fvdat->cached_attrs.va_atime = ts;
  516         if (flags & FN_MTIMECHANGE)
  517                 fvdat->cached_attrs.va_mtime = ts;
  518         if (flags & FN_CTIMECHANGE)
  519                 fvdat->cached_attrs.va_ctime = ts;
  520         
  521         fvdat->flag |= flags;
  522 }
  523 
  524 void
  525 fuse_node_init(void)
  526 {
  527         fuse_node_count = counter_u64_alloc(M_WAITOK);
  528 }
  529 
  530 void
  531 fuse_node_destroy(void)
  532 {
  533         counter_u64_free(fuse_node_count);
  534 }

Cache object: 4f24ffe9cf65c4e502b792b702cbb237


[ 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.