devfs_vfsops.c revision da6c28aaf62fa55f0fdb8004aa40f88f23bf53f0
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This is the device filesystem.
*
* It is a combination of a namer to drive autoconfiguration,
* plus the access methods for the device drivers of the system.
*
* The prototype is fairly dependent on specfs for the latter part
* of its implementation, though a final version would integrate the two.
*/
#include <sys/sysmacros.h>
#include <sys/pathname.h>
#include <sys/vfs_opreg.h>
/*
* devfs vfs operations.
*/
struct cred *);
static int devfsinit(int, char *);
static vfsdef_t devfs_vfssw = {
"devfs", /* type name string */
devfsinit, /* init routine */
0, /* flags */
NULL /* mount options table prototype */
};
static int devfstype; /* fstype */
/*
* Module linkage information
*/
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int e;
if ((e = mod_install(&modlinkage)) != 0) {
return (e);
}
dcmn_err(("devfs loaded\n"));
return (0);
}
int
_fini(void)
{
return (EBUSY);
}
int
{
}
/*ARGSUSED1*/
static int
{
static const fs_operation_def_t devfs_vfsops_template[] = {
};
int error;
int dev;
/*
* Associate VFS ops vector with this fstype
*/
if (error != 0) {
return (error);
}
if (error != 0) {
(void) vfs_freevfsops_by_type(fstype);
return (error);
}
/*
* Invent a dev_t (sigh).
*/
dev = 0;
}
return (0);
}
/*
* The name of the mount point and the name of the attribute
* filesystem are passed down from userland for now.
*/
static int
{
struct devfs_data *devfs_data;
dcmn_err(("devfs_mount\n"));
return (EPERM);
/*
* check that the mount point is sane
*/
return (ENOTDIR);
/*
* Devfs can only be mounted from kernel during boot.
* avp is the existing /devices, the same as the mount point.
*/
/*
* Create and initialize the vfs-private data.
* This includes a hand-crafted root vnode (we build
* this here mostly so that traverse() doesn't sleep
* in VFS_ROOT()).
*/
/* We're there. */
return (0);
}
/*
* We never unmount devfs in a real production system.
*/
/*ARGSUSED*/
static int
{
return (EBUSY);
}
/*
* return root vnode for given vfs
*/
static int
{
dcmn_err(("devfs_root\n"));
return (0);
}
/*
* return 'generic superblock' information to userland.
*
* not much that we can usefully admit to here
*/
static int
{
extern kmem_cache_t *dv_node_cache;
dcmn_err(("devfs_statvfs\n"));
/*
* We could compute the number of devfsnodes here .. but since
* it's dynamic anyway, it's not clear how useful this is.
*/
/* no illusions that blocks are relevant to devfs */
return (0);
}
/*
* devfs always mount after root is mounted, so this should never
* be invoked.
*/
/*ARGSUSED*/
static int
{
dcmn_err(("devfs_mountroot\n"));
return (EINVAL);
}
struct dv_node *
{
char *dirpath;
/* no-op if devfs not mounted yet */
if (devfs_mntinfo == NULL)
return (NULL);
/*
* The lookupname below only looks up cached dv_nodes
* because devfs_clean_key is set in thread specific data.
*/
return (NULL);
}
}
/*
* If DV_CLEAN_FORCE devfs_clean is issued with a dip that is not the root
* and not a vHCI we also need to clean any vHCI branches because they
* may contain pHCI nodes. A detach_node() of a pHCI will fail if its
* mdi_devi_offline() fails, and the mdi_devi_offline() of the last
* pHCI will fail unless an ndi_devi_offline() of the Client nodes under
* the vHCI is successful - which requires a clean vHCI branch to removed
* the devi_refs associated with devfs vnodes.
*/
static int
{
if (dvp) {
}
return (DDI_WALK_CONTINUE);
}
/*
* devfs_clean()
*
* Destroy unreferenced dv_node's and detach devices.
*
* devfs_clean will try its best to clean up unused nodes. It is
* no longer valid to assume that just because devfs_clean fails,
* the device is not removable. This is because device contracts
* can result in userland processes releasing a device during the
* device offline process in the kernel. Thus it is no longer
* correct to fail an offline just because devfs_clean finds
* referenced dv_nodes. To enforce this, devfs_clean() always
* returns success i.e. 0.
*
* devfs_clean() may return before removing all possible nodes if
* we cannot acquire locks in areas of the code where potential for
* deadlock exists (see comments in dv_find() and dv_cleandir() for
* examples of this).
*
* devfs caches unreferenced dv_node to speed by the performance
* of ls, find, etc. devfs_clean() is invoked to cleanup cached
* dv_nodes to reclaim memory as well as to facilitate device
* removal (dv_node reference devinfo nodes, which prevents driver
* detach).
*
* If a shell parks in a /devices directory, the dv_node will be
* held, preventing the corresponding device to be detached.
* This would be a denial of service against DR. To prevent this,
* DR code calls devfs_clean() with the DV_CLEAN_FORCE flag.
* The dv_cleandir() implementation does the right thing to ensure
* successful DR.
*/
int
{
dcmn_err(("devfs_unconfigure: dip = 0x%p, flags = 0x%x",
/* avoid recursion back into the device tree */
return (0);
}
/*
* If we are doing a DV_CLEAN_FORCE, and we did not start at the
* root, and we did not start at a vHCI node then clean vHCI
* branches too. Failure to clean vHCI branch does not cause EBUSY.
*
* Also, to accommodate nexus callers that clean 'self' to DR 'child'
* (like pcihp) we clean vHCIs even when dv_cleandir() of dip branch
* above fails - this prevents a busy DR 'child' sibling from causing
* the DR of 'child' to fail because a vHCI branch was not cleaned.
*/
/*
* NOTE: for backport the following is recommended
* (void) devfs_clean_vhci(scsi_vhci_dip,
* (void *)(uintptr_t)flags);
*/
}
return (0);
}
/*
* lookup a devfs relative pathname, returning held vnodes for the final
* component and the containing directory (if requested).
*
* NOTE: We can't use lookupname because this would use the current
* processes credentials (CRED) in the call lookuppnvp instead
* of kcred. It also does not give you the flexibility so
* specify the directory to start the resolution in (devicesdir).
*/
int
char *pathname, /* user pathname */
{
int error;
return (error);
/* make the path relative to /devices. */
pn_skipslash(&pn);
if (pn_pathleft(&pn) == 0) {
/* all we had was "\0" or "/" (which skipslash skiped) */
if (dirvpp)
if (compvpp) {
*compvpp = devicesdir;
}
} else {
/*
* Use devfs lookup to resolve pathname to the vnode for
* the device via relative lookup in devfs. Extra holds for
* using devicesdir as directory we are searching and for
* being our root without being == rootdir.
*/
}
return (error);
}
/*
* Given a devfs path (without the /devices prefix), walk
* the dv_node sub-tree rooted at the path.
*/
int
char *path,
void *arg)
{
return (ENXIO);
*devnm++ = '\0';
return (ENXIO);
}
/*
* if path == "/", visit the root dv_node
*/
if (*devnm == '\0') {
}
return (0);
}
int
{
int rval = -1;
/* fail if devfs not mounted yet */
if (devfs_mntinfo == NULL)
return (rval);
rval = 0;
}
}
return (rval);
}