/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
#include <sys/sysmacros.h>
#include <sys/vfs_opreg.h>
#include <sys/autoconf.h>
int spec_debug = 0;
struct vnode *
{
/* associate a dip hold with the common snode's s_dip pointer */
return (vp);
}
/*
* Return a shadow special vnode for the given dev.
* If no snode exists for this dev create one and put it
* in a table hashed by <dev, realvp>. If the snode for
* this dev is already in the table return it (ref count is
* incremented by sfind). The snode will be flushed from the
* table when spec_inactive calls sdelete.
*
* The fsid is inherited from the real vnode so that clones
* can be found.
*
*/
struct vnode *
{
int rc;
return (NULL);
/*
* Pre-allocate snodes before holding any locks in case we block
*/
/*
* Get the time attributes outside of the stable lock since
* this operation may block. Unfortunately, it may not have
* been required if the snode is in the cache.
*/
if (rc == 0) {
/*
* Set times in snode to those in the vnode.
*/
} else {
}
}
if (used_csp == 0) {
/* Didn't use pre-allocated snode so free it */
}
} else {
/* free unused snode memory */
}
}
/*
* Return a special vnode for the given dev; no vnode is supplied
* for it to shadow. Always create a new snode and put it in the
* table hashed by <dev, NULL>. The snode will be flushed from the
* table when spec_inactive() calls sdelete(). The association of
* this node with a attached instance of hardware is not made until
* spec_open time.
*
* N.B. Assumes caller takes on responsibility of making sure no one
* else is creating a snode for (dev, type) at this time.
*/
struct vnode *
{
now = gethrestime_sec();
return (svp);
}
/*
* This function is called from spec_assoc_vp_with_devi(). That function
* associates a "new" dip with a common snode, releasing (any) old dip
* in the process. This function (spec_assoc_fence()) looks at the "new dip"
* and determines whether the snode should be fenced of or not. As the table
* below indicates, the value of old-dip is a don't care for all cases.
*
* old-dip new-dip common-snode
* =========================================
* Don't care NULL unfence
* Don't care retired fence
* Don't care not-retired unfence
*
* Since old-dip value is a "don't care", it is not passed into this function.
*/
static void
{
int fence;
fence = 0;
fence = 1;
}
/* SFENCED flag only set on common snode */
if (fence)
else
}
/*
* Associate the common snode with a devinfo node. This is called from:
*
* 1) specvp_devfs to associate a specfs node with the dip attached
* by devfs.
*
* 2) spec_open after path reconstruction and attach.
*
* 3) From dacf processing to associate a makespecvp node with
* the dip that dacf postattach processing is being performed on.
* This association is made prior to open to avoid recursion issues.
*
* 4) From ddi_assoc_queue_with_devi to change vnode association as part of
* from ddi_assoc_queue_with_devi may specify a NULL dip.
*
* We put an extra hold on the devinfo node passed in as we establish it as
* the new s_dip pointer. Any hold associated with the prior s_dip pointer
* is released. The new hold will stay active until another call to
* spec_assoc_vp_with_devi or until the common snode is destroyed by
* spec_inactive after the last VN_RELE of the common node. This devinfo hold
* transfers across a clone open except in the clone_dev case, where the clone
* driver is no longer required after open.
*
* When SDIPSET is set and s_dip is NULL, the vnode has an association with
* the driver even though there is currently no association with a specific
* hardware instance.
*/
void
{
/*
* Don't establish a NULL association for a vnode associated with the
* clone driver. The qassociate(, -1) call from a streams driver's
* open implementation to indicate support for qassociate has the
* side-effect of this type of spec_assoc_vp_with_devi call. This
* call should not change the the association of the pre-clone
* vnode associated with the clone driver, the post-clone newdev
* association will be established later by spec_clone().
*/
return;
/* hold the new */
if (dip)
/* If association changes then invalidate cached size */
/* release the old */
if (olddip)
}
/*
* Return the held dip associated with the specified snode.
*/
{
if (dip)
return (dip);
}
/*
* Find a special vnode that refers to the given device
* of the given type. Never return a "common" vnode.
* Return NULL if a special vnode does not exist.
* HOLD the vnode before returning it.
*/
struct vnode *
{
return (nvp);
}
}
}
return (NULL);
}
/*
* Loop through the snode cache looking for snodes referencing dip.
*
* This function determines if a devinfo node is "BUSY" from the perspective
* of having an active vnode associated with the device, which represents a
* dependency on the device's services. This function is needed because a
* devinfo node can have a non-zero devi_ref and still NOT be "BUSY" when,
* for instance, the framework is manipulating the node (has an open
* ndi_hold_devi).
*
* Returns:
* DEVI_REFERENCED - if dip is referenced
* DEVI_NOT_REFERENCED - if dip is not referenced
*/
int
{
int i;
/* if no hold then there can't be an snode with s_dip == dip */
if (e_ddi_devi_holdcnt(dip) == 0)
return (DEVI_NOT_REFERENCED);
for (i = 0; i < STABLESIZE; i++) {
return (DEVI_REFERENCED);
}
}
}
return (DEVI_NOT_REFERENCED);
}
/*
* Given an snode, returns the open count and the dip
* associated with that snode
* Assumes the caller holds the appropriate locks
* Returns:
* -1 No associated dip
* >= 0 Number of opens.
*/
int
{
/*
* We are only interested in common snodes. Only common snodes
* get their s_count fields bumped up on opens.
*/
return (-1);
count++;
return (count);
}
/*
* Given a device vnode, return the common
* vnode associated with it.
*/
struct vnode *
{
return (vp);
return (sp->s_commonvp);
}
/*
* Returns a special vnode for the given dev. The vnode is the
* one which is "common" to all the snodes which represent the
* same device.
* Similar to commonvp() but doesn't acquire the stable_lock, and
* may use a pre-allocated snode provided by caller.
*/
static struct vnode *
int *used_nsp) /* flag indicating if we use nsp */
{
} else
*used_nsp = 0;
}
/*
* Returns a special vnode for the given dev. The vnode is the
* one which is "common" to all the snodes which represent the
* same device. For use ONLY by SPECFS.
*/
struct vnode *
{
/* Pre-allocate snode in case we might block */
} else {
/* Didn't need the pre-allocated snode */
}
}
/*
* Snode lookup stuff.
* These routines maintain a table of snodes hashed by dev so
* that the snode for an dev can be found if it already exists.
*/
/*
* Put a snode in the table.
*/
static void
{
}
/*
* Remove an snode from the hash table.
* The realvp is not released here because spec_inactive() still
* needs it to do a spec_fsync().
*/
void
{
else
break;
}
}
}
/*
* Lookup an snode by <dev, type, vp>.
* ONLY looks for snodes with non-NULL s_realvp members and
* common snodes (with s_commonvp pointing to its vnode).
*
* If vp is NULL, only return commonvp. Otherwise return
* shadow vp with both shadow and common vp's VN_HELD.
*/
static struct snode *
{
return (st);
}
}
return (NULL);
}
/*
* Mark the accessed, updated, or changed times in an snode
* with the current time.
*/
void
{
/* check for change to avoid unnecessary locking */
/* lock and update */
}
}
/*
* Return the maximum file offset permitted for this device.
* -1 means unrestricted. SLOFFSET is associated with D_64BIT.
*
* On a 32-bit kernel this will limit:
* o D_64BIT devices to SPEC_MAXOFFSET_T.
* o non-D_64BIT character drivers to a 32-bit offset (MAXOFF_T).
*/
{
return ((offset_t)-1);
return ((offset_t)-1);
#ifdef _ILP32
return (SPEC_MAXOFFSET_T);
#endif /* _ILP32 */
return (MAXOFF_T);
}
/*ARGSUSED*/
static int
{
return (-1);
}
return (0);
}
/*ARGSUSED1*/
static void
{
}
int
{
};
extern struct vnodeops *spec_vnodeops;
extern const fs_operation_def_t spec_vnodeops_template[];
int error;
/*
* Associate vfs and vnode operations.
*/
if (error != 0) {
return (error);
}
if (error != 0) {
(void) vfs_freevfsops_by_type(fstype);
return (error);
}
/*
* Create snode cache
*/
/*
* Associate vfs operations with spec_vfs
*/
dev = 0;
return (0);
}
int
{
int error;
switch (type) {
case VCHR:
} else
break;
case VBLK:
/*
* On last close a block device we must
* invalidate any in-core blocks so that we
* can, for example, change floppy disks.
*/
break;
default:
panic("device_close: not a device");
/*NOTREACHED*/
}
return (error);
}
struct vnode *
{
}
return (vp);
}
void
{
int i;
for (i = 0; i < STABLESIZE; i++) {
goto out;
}
}
out:
}
int
{
}
return (0);
}
int
{
}
return (0);
}
/*
* We may be invoked with a NULL vp in which case we fence off
* all snodes associated with dip
*/
int
{
int retired;
int i;
char *path;
int emitted;
retired = 0;
retired = 1;
if (!retired)
return (0);
return (0);
}
emitted = 0;
for (i = 0; i < STABLESIZE; i++) {
/* fence off the common snode */
if (!emitted) {
emitted++;
}
}
}
}
return (0);
}
int
{
int i;
char *path;
int emitted;
emitted = 0;
for (i = 0; i < STABLESIZE; i++) {
/* unfence the common snode */
if (!emitted) {
emitted++;
}
}
}
}
return (0);
}
void
{
}
}