/* * Copyright (c) 1992, Sun Microsystems, Inc. * All rights reserved. #pragma ident "@(#)cachefs_cnode.c 1.75 94/04/21 SMI" */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cachefs_fs.h" #include #include #include #include #include extern vnodeops_t cachefs_vnodeops; /* * Functions for cnode management. */ void cachefs_addfidhash(struct cnode *cp) { struct fscache *fscp = C_TO_FSCACHE(cp); u_int hash; CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_addfidhash: ENTER cp 0x%p\n", cp)); ASSERT(VALID_ADDR(cp)); ASSERT((cp->c_frontfid.fid_len > 0) && (cp->c_frontfid.fid_len <= MAXFIDSZ)); ASSERT(!cfind_by_fid(&cp->c_frontfid, fscp)); hash = fidhash(&cp->c_frontfid, CNODE_BUCKET_SIZE); cp->c_hash_by_fid = fscp->fs_cnode_by_fid[hash]; fscp->fs_cnode_by_fid[hash] = cp; #ifdef DEBUG cp->c_infidhash = 1; #endif /* DEBUG */ CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_addfidhash: EXIT\n")); } void cachefs_remfidhash(struct cnode *cp) { struct cnode **headpp; struct fscache *fscp = C_TO_FSCACHE(cp); int found = 0; u_int hash; CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_remfidhash: ENTER cp 0x%p\n", cp)); ASSERT(VALID_ADDR(cp)); ASSERT(cp->c_frontfid.fid_len || (!cp->c_hash_by_fid && !cp->c_infidhash)); if (cp->c_frontfid.fid_len) { ASSERT(cp->c_frontfid.fid_len <= MAXFIDSZ); hash = fidhash(&cp->c_frontfid, CNODE_BUCKET_SIZE); for (headpp = &fscp->fs_cnode_by_fid[hash]; *headpp != NULL; headpp = &(*headpp)->c_hash_by_fid) { if (*headpp == cp) { *headpp = cp->c_hash_by_fid; cp->c_hash_by_fid = NULL; found++; fscp->fs_hashvers++; break; } } CFS_DEBUG(CFSDEBUG_CNODE, if (!found) printf("cachefs_remfidhash: cnode 0x%p not found in fid hash\n", cp)); } #ifdef DEBUG cp->c_infidhash = 0; #endif /* DEBUG */ CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_remfidhash: EXIT\n")); } /* * Add a cnode to the hash queues. */ void cachefs_addhash(struct cnode *cp) { struct fscache *fscp = C_TO_FSCACHE(cp); u_int hash = fidhash(cp->c_backfid, CNODE_BUCKET_SIZE); CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_addhash: ENTER cp 0x%p\n", cp)); ASSERT(VALID_ADDR(cp->c_backfid)); ASSERT(VALID_ADDR(cp)); ASSERT(cp->c_hash == NULL); ASSERT(!cp->c_inhash); ASSERT(!cfind(cp->c_backfid, fscp)); cp->c_hash = fscp->fs_cnode[hash]; fscp->fs_cnode[hash] = cp; if (cp->c_frontfid.fid_len) { cachefs_addfidhash(cp); ASSERT(cp->c_infidhash); #ifdef DEBUG } else { ASSERT(!C_CACHING(cp)); #endif /* DEBUG */ } fscp->fs_hashvers++; #ifdef DEBUG cp->c_inhash = 1; #endif /* DEBUG */ CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_addhash: EXIT\n")); } /* * Remove a cnode from the hash queues */ void cachefs_remhash(struct cnode *cp) { struct cnode **headpp; struct fscache *fscp = C_TO_FSCACHE(cp); int found = 0; CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_remhash: ENTER cp 0x%p\n", cp)); ASSERT(VALID_ADDR(cp)); ASSERT(VALID_ADDR(cp->c_backfid)); ASSERT(cp->c_backfid->fid_len <= MAXFIDSZ); for (headpp = &fscp->fs_cnode[fidhash(cp->c_backfid, CNODE_BUCKET_SIZE)]; *headpp != NULL; headpp = &(*headpp)->c_hash) { if (*headpp == cp) { ASSERT(cp->c_inhash); *headpp = cp->c_hash; cp->c_hash = NULL; found++; fscp->fs_hashvers++; #ifdef DEBUG cp->c_inhash = 0; #endif /* DEBUG */ break; } } cachefs_remfidhash(cp); ASSERT(!cp->c_inhash && !cp->c_infidhash); ASSERT((cp->c_flags & CN_DESTROY) || !cfind(cp->c_backfid, fscp)); ASSERT(!cp->c_frontfid.fid_len || (cfind_by_fid(&cp->c_frontfid, fscp) != cp)); CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_remhash: EXIT\n")); } /* * Search for the cnode on the hash queues hanging off the fscp struct. * On success, returns 0 and *cpp is set to the cnode. */ cnode_t * cfind(fid_t *key, struct fscache *fscp) { struct cnode *head; #ifdef DEBUG int found = 0; #endif cnode_t *cp = NULL; u_int hv; CFS_DEBUG(CFSDEBUG_CNODE, printf("cfind: ENTER fscp 0x%p\n", fscp)); ASSERT(VALID_ADDR(fscp)); ASSERT(VALID_ADDR(key)); ASSERT(key->fid_len <= MAXFIDSZ); CACHEFUNC_TRACE(CFTRACE_OTHER, (void *)cfind, current_pid(), key, fscp); hv = fidhash(key, CNODE_BUCKET_SIZE); for (head = fscp->fs_cnode[hv]; head != NULL; head = head->c_hash) { /* * cnodes marked CN_DESTROY may be in the hash. They are removed * by fscache_sync. */ if ( !(head ->c_flags & CN_DESTROY) && FID_MATCH( head->c_backfid, key ) ) { #ifdef DEBUG CFS_DEBUG(CFSDEBUG_CNODE, if (found) printf("cfind: duplicate cnodes cp 0x%p head 0x%p\n", cp, head)); ASSERT(found == 0); found = 1; #endif cp = head; #ifndef DEBUG break; #endif } } ASSERT(!found || cp); ASSERT(found || !cp); ASSERT(!found || cp->c_inhash); ASSERT(!cp || !(cp ->c_flags & CN_DESTROY)); CFS_DEBUG(CFSDEBUG_CNODE, printf("cfind: EXIT cp 0x%p\n", cp)); return ( cp ); } cnode_t * cfind_by_fid(fid_t *fidp, fscache_t *fscp) { struct cnode *head; u_int hv; cnode_t *cp = NULL; #ifdef DEBUG int found = 0; #endif CFS_DEBUG(CFSDEBUG_CNODE, printf("cfind_by_fid: ENTER fidp 0x%p fscp 0x%p\n", fidp, fscp)); ASSERT(VALID_ADDR(fscp)); ASSERT(VALID_ADDR(fidp)); CACHEFUNC_TRACE(CFTRACE_OTHER, (void *)cfind_by_fid, current_pid(), fidp, fscp); for (hv = fidhash(fidp, CNODE_BUCKET_SIZE), head = fscp->fs_cnode_by_fid[hv]; head != NULL; head = head->c_hash_by_fid) { /* * cnodes marked CN_DESTROY may be in the hash. They are removed * by fscache_sync. */ if ( !(head ->c_flags & CN_DESTROY) && FID_MATCH( &head->c_frontfid, fidp ) ) { #ifdef DEBUG CFS_DEBUG(CFSDEBUG_CNODE, if (found) printf("cfind_by_fid: duplicate cnodes cp 0x%p " "head 0x%p\n", cp, head)); ASSERT(found == 0); found = 1; #endif cp = head; #ifndef DEBUG break; #endif } } ASSERT(!found || cp); ASSERT(found || !cp); ASSERT(!found || cp->c_inhash); #ifdef DEBUG if (cp) { ASSERT(!(cp ->c_flags & CN_DESTROY)); ASSERT(cp->c_inhash); ASSERT(C_TO_FSCACHE(cp) == fscp); ASSERT(cfind(cp->c_backfid, fscp) == cp); } #endif CFS_DEBUG(CFSDEBUG_CNODE, printf("cfind_by_fid: EXIT cp 0x%p\n", cp)); return ( cp ); } void cachefs_cnode_free( struct cnode *cp ) { CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_cnode_free: ENTER cp 0x%p\n", cp)); ASSERT(VALID_ADDR(cp)); ASSERT(cp->c_hash == NULL); ASSERT(cp->c_hash_by_fid == NULL); ASSERT(!cp->c_inhash); ASSERT(!cp->c_infidhash); ASSERT(!cp->c_nio); ASSERT(!(cp->c_ioflags & (CIO_ASYNCWRITE | CIO_ASYNCREAD))); ASSERT(cp->c_vnode == NULL); ASSERT(cp->c_flags & CN_DESTROY); ASSERT(!cp->c_frontfid.fid_len || !C_TO_FSCACHE(cp) || (cfind_by_fid(&cp->c_frontfid, C_TO_FSCACHE(cp)) != cp)); ASSERT(cp->c_refcnt == 0); ASSERT(!(cp->c_flags & CN_UPDATED)); CACHEFUNC_TRACE(CFTRACE_OTHER, (void *)cachefs_cnode_free, current_pid(), cp, 0); /* release front and back files */ if (cp->c_cred != NULL) { crfree(cp->c_cred); } if (cp->c_dcp) { CN_RELE(cp->c_dcp); cp->c_dcp = NULL; } if (cp->c_frontdirvp) VN_RELE(cp->c_frontdirvp); if (cp->c_frontvp) VN_RELE(cp->c_frontvp); if (cp->c_backvp) { VN_RELE(cp->c_backvp); } if ( cp->c_fileheader ) { CACHEFS_RELEASE_FILEHEADER(&cp->c_frontfid, cp->c_fileheader); } else if (cp->c_backfid) { CACHEFS_ZONE_FREE(Cachefs_fid_zone, cp->c_backfid); } if (cp->c_fscache) { CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_cnode_free: fscache_rele cp 0x%p fscache 0x%p\n", cp, cp->c_fscache)); fscache_rele(cp->c_fscache); } sv_destroy( &cp->c_iosv ); spinlock_destroy( &cp->c_iolock ); rw_destroy( &cp->c_rwlock ); spinlock_destroy( &cp->c_statelock ); sv_destroy( &cp->c_popwait_sv ); #if defined(DEBUG) && defined(_CNODE_TRACE) spinlock_destroy( &cp->c_trace.ct_lock ); #endif /* DEBUG && _CNODE_TRACE */ bzero((caddr_t)cp, sizeof (cnode_t)); CACHEFS_ZONE_FREE(Cachefs_cnode_zone, cp); (void) cachefs_cnode_cnt(-1); CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_cnode_free: EXIT\n")); } static int cachefs_get_fids( cnode_t *cp ) { int error = 0; CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_get_fids: ENTER cp 0x%p\n", cp)); CACHEFUNC_TRACE(CFTRACE_OTHER, (void *)cachefs_get_fids, current_pid(), cp, 0); ASSERT(VALID_ADDR(cp)); ASSERT(VALID_ADDR(cp->c_frontvp)); VOP_FID2( cp->c_frontvp, &cp->c_frontfid, error ); if ( !error ) { if (cp->c_frontdirvp) { ASSERT(VALID_ADDR(cp->c_frontdirvp)); VOP_FID2( cp->c_frontdirvp, &cp->c_frontdirfid, error ); if ( error ) { cp->c_frontfid.fid_len = 0; CACHEFS_STATS->cs_fronterror++; CFS_DEBUG(CFSDEBUG_ERROR, printf("cachefs_get_fids(line %d): error getting " "front dir file ID, cnode 0x%p: %d\n", __LINE__, cp, error)); } } } else { CACHEFS_STATS->cs_fronterror++; CFS_DEBUG(CFSDEBUG_ERROR, printf("cachefs_get_fids(line %d): error getting " "front file ID, cnode 0x%p: %d\n", __LINE__, cp, error)); } CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_get_fids: EXIT, error = %d\n", error)); return(error); } /* * Validate the allocation map in the file header against the attibutes from * the back file and also validate the size of the front file against the * allocation map and the back file attributes. */ int valid_allocmap( cnode_t *cp ) { int ent; int error = 0; int valid = 1; off_t lastoff; vattr_t *fap = NULL; fileheader_t *fhp = cp->c_fileheader; allocmap_t *amp; u_short entries; ASSERT(VALID_ADDR(cp)); if (!C_CACHING(cp)) { return(1); } ASSERT(VALID_ADDR(fhp)); if (fhp->fh_metadata.md_state & MD_POPULATED) { ASSERT(VALID_ADDR(cp->c_attr)); amp = fhp->fh_allocmap; entries = fhp->fh_metadata.md_allocents; CACHEFUNC_TRACE(CFTRACE_OTHER, (void *)valid_allocmap, current_pid(), amp, entries); if (!cp->c_frontfid.fid_len) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_allocmap: file populated but no front file\n")); valid = 0; } else if (!(cp->c_flags & CN_UPDATED)) { fap = (vattr_t *)CACHEFS_ZONE_ZALLOC(Cachefs_attr_zone, KM_SLEEP); fap->va_mask = AT_SIZE; error = FRONT_VOP_GETATTR(cp, fap, 0, sys_cred); if (!error) { switch (CTOV(cp)->v_type) { case VDIR: /* * If the directory data has been populated, the * allocation map must contain exactly one entry. */ if ((entries != 1) || (amp->am_start_off != (off_t)0) || (BTOBB(amp->am_size) != BTOBB(fap->va_size - (off_t)DATA_START(C_TO_FSCACHE(cp))))) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_allocmap: invalid front dir " "file size : %d should be %d\n", (int)fap->va_size, (int)(fhp->fh_allocmap[0].am_size + (off_t)DATA_START(C_TO_FSCACHE(cp))))); valid = 0; } break; case VLNK: if (fhp->fh_metadata.md_state & MD_NOALLOCMAP) { if ((entries > sizeof(fhp->fh_allocmap)) || (fap->va_size != (off_t)DATA_START( C_TO_FSCACHE(cp)))) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_allocmap: invalid front " "file size (VLNK): entries %d " "(%d max), front file size %d\n", entries, sizeof(fhp->fh_metadata.md_allocents), (int)fap->va_size)); valid = 0; } } else if (cp->c_size != (fap->va_size - (off_t)DATA_START( C_TO_FSCACHE(cp)))) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_allocmap: invalid front file " "size (VLNK): %d should be %d\n", fap->va_size - (off_t)DATA_START(C_TO_FSCACHE(cp)), cp->c_size)); valid = 0; } else if ((entries != 1) || (amp->am_start_off != 0) || (amp->am_size != cp->c_size)) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_allocmap: invalid map " "entry 0 (VLNK): %d entries, " "am_start_off %d, am_size %d, " "c_size %d\n", entries, (int)amp->am_start_off, (int)amp->am_size, (int)cp->c_size)); valid = 0; break; } break; case VNON: valid = 0; CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_allocmap: invalid file type\n")); break; case VREG: default: if (BTOBB(cp->c_attr->ca_size) != BTOBB(fap->va_size - (off_t)DATA_START(C_TO_FSCACHE(cp)))) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_allocmap: invalid front file " "size\n")); valid = 0; } else { for ( lastoff = (off_t)0; entries; lastoff = amp->am_start_off + amp->am_size, entries--, amp++ ) { if ((amp->am_size == (off_t)0) || (amp->am_start_off < (off_t)0) || (lastoff && (amp->am_start_off <= lastoff)) || (amp->am_start_off + amp->am_size - 1 > cp->c_attr->ca_size)) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_allocmap: " "invalid map entry %d, " "cnode 0x%p\n", (int)(fhp->fh_metadata.md_allocents - entries), cp)); valid = 0; break; } } } } } else { CACHEFS_STATS->cs_fronterror++; CFS_DEBUG(CFSDEBUG_ERROR, printf("valid_allocmap(line %d): error getting " "front file attributes, vnode 0x%p: %d\n", __LINE__, cp, error)); valid = 0; } } if (fap) { CACHEFS_ZONE_FREE(Cachefs_attr_zone, fap); } } else if (CTOV(cp)->v_type == VREG) { if (fhp->fh_metadata.md_allocents > ALLOCMAP_SIZE) { valid = 0; CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_allocmap: bad entry count %d\n", fhp->fh_metadata.md_allocents)); } else { for (ent = 0; ent < fhp->fh_metadata.md_allocents - 1; ent++) { if ((fhp->fh_allocmap[ent].am_start_off + fhp->fh_allocmap[ent].am_size) > fhp->fh_allocmap[ent + 1].am_start_off) { valid = 0; CFS_DEBUG(CFSDEBUG_FILEHEADER, printf( "valid_allocmap: bad allocation map, entry %d\n", ent)); break; } } } } return(valid); } static int all_zero(caddr_t addr, int size) { int i; ASSERT(addr); for (i = 0; i < size; addr++, i++) { if (*addr != 0) { return(0); } } return(1); } int valid_file_header(fileheader_t *fhp, fid_t *backfid) { int valid = 1; CFS_DEBUG(CFSDEBUG_CNODE, printf("valid_file_header: ENTER fhp 0x%p\n", fhp)); CACHEFUNC_TRACE(CFTRACE_OTHER, (void *)valid_file_header, current_pid(), fhp, 0); ASSERT(VALID_ADDR(fhp)); ASSERT(!backfid || VALID_ADDR(backfid)); ASSERT(!backfid || (backfid->fid_len <= MAXFIDSZ)); if (!VALID_MD_STATE(fhp->fh_metadata.md_state)) { valid = 0; CFS_DEBUG(CFSDEBUG_FILEHEADER, printf( "valid_file_header: bad state 0x%x\n", (int)fhp->fh_metadata.md_state)); } else if (!(fhp->fh_metadata.md_state & MD_MDVALID)) { if (fhp->fh_metadata.md_state != MD_INIT) { valid = 0; CFS_DEBUG(CFSDEBUG_FILEHEADER, printf( "valid_file_header: MD_MDVALID not set\n")); } else if (fhp->fh_metadata.md_allocents != 0) { valid = 0; CFS_DEBUG(CFSDEBUG_FILEHEADER, printf( "valid_file_header: bad allocents value for MD_INIT: %d\n", fhp->fh_metadata.md_allocents)); } else if (!all_zero((caddr_t)&fhp->fh_metadata.md_backfid, sizeof(fid_t))) { valid = 0; CFS_DEBUG(CFSDEBUG_FILEHEADER, printf( "valid_file_header: bad initial back file ID\n")); } else if (!all_zero((caddr_t)&fhp->fh_metadata.md_token, sizeof(ctoken_t))) { valid = 0; CFS_DEBUG(CFSDEBUG_FILEHEADER, printf( "valid_file_header: bad initial token\n")); } } else if (!(fhp->fh_metadata.md_state & MD_NOALLOCMAP) && (fhp->fh_metadata.md_allocents > ALLOCMAP_SIZE)) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf( "valid_file_header: invalid allocation map entry count\n")); valid = 0; } else if ((fhp->fh_metadata.md_state & MD_NOALLOCMAP) && (fhp->fh_metadata.md_allocents > sizeof(fhp->fh_allocmap))){ CFS_DEBUG(CFSDEBUG_FILEHEADER, printf( "valid_file_header: invalid allocation map data length\n")); valid = 0; } else if (!(fhp->fh_metadata.md_state & MD_INIT)) { if (fhp->fh_metadata.md_backfid.fid_len == 0) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_file_header: empty back file ID\n")); valid = 0; } else if (fhp->fh_metadata.md_backfid.fid_len > MAXFIDSZ) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_file_header: bad back file ID length %d\n", fhp->fh_metadata.md_backfid.fid_len)); valid = 0; } else if (fhp->fh_metadata.md_attributes.ca_type == VNON) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_file_header: invalid attribute type %d\n", (int)fhp->fh_metadata.md_attributes.ca_type)); valid = 0; } else if (backfid && !FID_MATCH(backfid, &fhp->fh_metadata.md_backfid)) { CFS_DEBUG(CFSDEBUG_FILEHEADER, printf("valid_file_header: back FID mismatch\n")); valid = 0; } } CFS_DEBUG(CFSDEBUG_CNODE, printf("valid_file_header: EXIT, %s\n", valid ? "valid" : "invalid")); return(valid); } #ifdef DEBUG /* * A valid cnode supplied to cachefs_initcnode adheres to the following * contitions. */ #define GOOD_CNODE(cp) \ (!(cp)->c_flags && !(cp)->c_hash && !(cp)->c_hash_by_fid && \ !(cp)->c_frontvp && !(cp)->c_frontdirvp && \ !(cp)->c_backvp && VALID_ADDR((cp)->c_vnode) && \ !(cp)->c_frontfid.fid_len && !(cp)->c_frontdirfid.fid_len && \ !(cp)->c_backfid && !(cp)->c_fileheader && !(cp)->c_attr && \ !(cp)->c_size && !(cp)->c_token && !(cp)->c_error && \ !(cp)->c_nio && !(cp)->c_ioflags && !(cp)->c_cred && \ !(cp)->c_fscache && !(cp)->c_inhash && !(cp)->c_infidhash && \ !(cp)->c_written) #endif /* DEBUG */ /* * We have to initialize the cnode contents. Fill in the contents from the * cache (attrcache file), from the info passed in, whatever it takes. */ /* * It is assumed that this function is only called when a cnode has just * been allocated from kernel memory. Thus, no vnode will be associated * with the cnode upon entry to this function. Violation of this assumption * by the caller will lead to memory leakage. * It is assumed that the caller did not initialize any part of the data * structure. */ static int cachefs_initcnode( cnode_t *cp, cnode_t *dp, fid_t *backfid, struct fscache *fscp, fileheader_t *fhp, vnode_t *frontvp, vnode_t *frontdirvp, vnode_t *backvp, int flag, vattr_t *vap, cred_t *cr) { int error = 0; vattr_t *attrs = NULL; vattr_t *chkattr = vap; vnode_t *realvp = NULL; int mandlock; CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_initcnode: ENTER cp 0x%p fhp 0x%p\n", cp, fhp)); CACHEFUNC_TRACE(CFTRACE_OTHER, (void *)cachefs_initcnode, current_pid(), cp, dp); ASSERT(VALID_ADDR(cp)); ASSERT(!dp || VALID_ADDR(dp)); ASSERT(VALID_ADDR(backfid)); ASSERT(VALID_ADDR(fscp)); ASSERT(!fhp || VALID_ADDR(fhp)); ASSERT(!frontvp || VALID_ADDR(frontvp)); ASSERT(!frontdirvp || VALID_ADDR(frontdirvp)); ASSERT(!backvp || VALID_ADDR(backvp)); ASSERT(!backvp || (backvp->v_count >= 1)); ASSERT(!vap || VALID_ADDR(vap)); ASSERT(VALID_ADDR(cr)); ASSERT(backvp || fhp); ASSERT(GOOD_CNODE(cp)); ASSERT(!issplhi(getsr())); /* * Initialize the locks, etc. first. */ rw_init(&cp->c_rwlock, "cnode serialize lock", RW_DEFAULT, NULL); spinlock_init(&cp->c_statelock, "cnode state lock"); spinlock_init(&cp->c_iolock, "cnode IO lock"); sv_init(&cp->c_iosv, SV_DEFAULT, "Async IO CV"); sv_init(&cp->c_popwait_sv, SV_DEFAULT, "popwait"); #if defined(DEBUG) && defined (_CNODE_TRACE) /* * If we are debugging, initiaize the trace buffer lock. */ spinlock_init(&cp->c_trace.ct_lock, "cnode trace lock"); #endif /* DEBUG && _CNODE_TRACE */ /* * point the cnode back to its fscache * this is somewhat redundant as the vfs structure also points to this * it is necessary, however, since cachefs_reclaim will set c_vnode to * null but not necessarily throw away the cnode */ CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_initcnode: fscache_hold cp 0x%p fscache 0x%p\n", cp, fscp)); fscache_hold(fscp); cp->c_fscache = fscp; cp->c_flags = flag; if (cp->c_flags & CN_NOCACHE) { CACHEFS_STATS->cs_nocache++; } if (!vap) { if (backvp) { if (!fhp || (fhp->fh_metadata.md_state & MD_INIT)) { attrs = (vattr_t *)CACHEFS_ZONE_ALLOC(Cachefs_attr_zone, KM_SLEEP); attrs->va_mask = AT_ALL; NET_VOP_GETATTR_NOSTAT(backvp, attrs, 0, cr, C_ISFS_NONBLOCK(fscp) ? BACKOP_NONBLOCK : BACKOP_BLOCK, error); if (error) { CFS_DEBUG(CFSDEBUG_ERROR, printf("cachefs_initcnode: getattr vp 0x%p: %d\n", backvp, error)); goto out; } chkattr = vap = attrs; ASSERT(vap->va_type != VNON); } else { vap = NULL; ASSERT(fhp->fh_metadata.md_attributes.ca_type != VNON); ASSERT(fhp->fh_metadata.md_attributes.ca_type == backvp->v_type); } } else { ASSERT(fhp); vap = NULL; ASSERT(fhp->fh_metadata.md_attributes.ca_type != VNON); } #ifdef DEBUG } else { ASSERT(vap->va_type != VNON); #endif } #ifdef DEBUG if (flag & CN_UPDATED) { ASSERT(fhp); CNODE_TRACE(CNTRACE_UPDATE, cp, (void *)cachefs_initcnode, cp->c_flags, fhp->fh_metadata.md_allocents); } if (flag & CN_NOCACHE) { CNODE_TRACE(CNTRACE_NOCACHE, cp, cachefs_initcnode, (int)cp->c_flags, 0); } #endif /* DEBUG */ if (dp) { CN_HOLD(dp); cp->c_dcp = dp; } /* * Complete the initialization of the vnode */ ASSERT(attrs || vap || fhp); ASSERT(cp->c_vnode); if (attrs) { cp->c_type = cp->c_vnode->v_type = attrs->va_type; } else if (vap) { cp->c_type = cp->c_vnode->v_type = vap->va_type; } else { cp->c_type = cp->c_vnode->v_type = fhp->fh_metadata.md_attributes.ca_type; } /* * If there is a back vnode, put it into the cnode and hold it. * Make sure we have the NFS vnode not some other one. */ if (backvp) { VOP_REALVP(backvp, &realvp, error); if (!error) { ASSERT(realvp); backvp = realvp; } VN_HOLD( backvp ); cp->c_backvp = backvp; error = 0; } /* * If there is a back vnode, put it into the cnode and hold it. Also * check for a front directory vnode. There should not be a front dir * vp if there is no front file vp. */ if (frontvp) { VN_HOLD(frontvp); cp->c_frontvp = frontvp; ASSERT(frontvp->v_count > 1); if (frontdirvp) { VN_HOLD(frontdirvp); cp->c_frontdirvp = frontdirvp; ASSERT(frontdirvp->v_count > 1); } /* * Get the file IDs for the front file and front directory if there * is one. */ error = cachefs_get_fids( cp ); if (error) { goto out; } } cp->c_fileheader = fhp; CNODE_TRACE(CNTRACE_FILEHEADER, cp, cachefs_initcnode, fhp, 0); ASSERT(fhp || vap); if (!fhp) { cp->c_flags |= CN_NOCACHE; cp->c_size = vap->va_size; cp->c_attr = NULL; cp->c_token = NULL; cp->c_backfid = (fid_t *)CACHEFS_ZONE_ALLOC(Cachefs_fid_zone, KM_SLEEP); cp->c_backfid->fid_len = backfid->fid_len; ASSERT(backfid->fid_len <= MAXFIDSZ); bcopy( (void *)backfid->fid_data, (void *)cp->c_backfid->fid_data, backfid->fid_len ); } else if (fhp->fh_metadata.md_state & MD_INIT) { cp->c_attr = &fhp->fh_metadata.md_attributes; cp->c_backfid = &fhp->fh_metadata.md_backfid; cp->c_token = &fhp->fh_metadata.md_token; /* * Fill in the back file ID. */ fhp->fh_metadata.md_backfid.fid_len = backfid->fid_len; bcopy( (void *)backfid->fid_data, (void *)&fhp->fh_metadata.md_backfid.fid_data, fhp->fh_metadata.md_backfid.fid_len ); error = CFSOP_INIT_COBJECT(fscp, cp, vap, cr); ASSERT(!issplhi(getsr())); if (error) { goto out; } cp->c_fileheader->fh_metadata.md_state &= ~MD_INIT; cp->c_fileheader->fh_metadata.md_state |= MD_MDVALID; cp->c_flags |= CN_UPDATED; CNODE_TRACE(CNTRACE_UPDATE, cp, (void *)cachefs_initcnode, cp->c_flags, cp->c_fileheader->fh_metadata.md_allocents); ASSERT(cp->c_frontvp); ASSERT(cachefs_zone_validate((caddr_t)cp->c_fileheader, FILEHEADER_BLOCK_SIZE(fscp->fs_cache))); ASSERT(!backvp || (cp->c_attr->ca_type == backvp->v_type)); ASSERT(cp->c_backfid->fid_len <= MAXFIDSZ); ASSERT(valid_file_header(cp->c_fileheader, NULL)); } else { ASSERT(frontvp); /* * If a file header has been supplied, then the front file existed. * Assume that the supplied file header is valid. */ ASSERT(valid_file_header(cp->c_fileheader, NULL)); /* * Initialize the cached object using the attributes in the * file header. We initialize from the file header so that * CFSOP_CHECK_COBJECT can validate the file contents based * upon the attributes from the file header. */ cp->c_attr = &fhp->fh_metadata.md_attributes; cp->c_backfid = &fhp->fh_metadata.md_backfid; cp->c_token = &fhp->fh_metadata.md_token; error = CFSOP_INIT_COBJECT(fscp, cp, NULL, cr); ASSERT(!issplhi(getsr())); if (error) { goto out; } ASSERT(!backvp || (cp->c_attr->ca_type == backvp->v_type)); ASSERT(cp->c_backfid->fid_len <= MAXFIDSZ); ASSERT(valid_file_header(cp->c_fileheader, NULL)); } /* * Check the object to make sure it is current. Always do this when * creating a new cnode. If the attributes were supplied by the caller, * use those (vap), otherwise, pass attrs to CFSOP_CHECK_COBJECT. If * attrs is NULL, no attributes have been retrieved from the back file. * chkattr points to the attributes originally supplied by the caller * or to the attributes gotten above. * This cnode is not in the hash yet, so no other process can have * access to it. Thus, we use the lock mode NOLOCK to indicate that * if invalidation is necessary, c_rwlock need not be acuired. */ error = CFSOP_CHECK_COBJECT(fscp, cp, chkattr, cr, NOLOCK); ASSERT(!issplhi(getsr())); if (error) { goto out; } /* * it is assumed that vn_alloc will always succeed * assert that this is the case */ ASSERT(cp->c_hash == NULL); out: if (error) { if (cp->c_backvp) { VN_RELE(cp->c_backvp); cp->c_backvp = NULL; } if (cp->c_frontvp) { VN_RELE(cp->c_frontvp); cp->c_frontvp = NULL; } if (cp->c_frontdirvp) { VN_RELE(cp->c_frontdirvp); cp->c_frontdirvp = NULL; } if (cp->c_vnode) { vn_free(cp->c_vnode); cp->c_vnode = NULL; } cp->c_flags = 0; } if (attrs) { CACHEFS_ZONE_FREE(Cachefs_attr_zone, attrs); } ASSERT(error || !cp->c_fileheader || valid_file_header(cp->c_fileheader, cp->c_backfid)); ASSERT(!issplhi(getsr())); ASSERT(!fhp || cachefs_zone_validate((caddr_t)fhp, FILEHEADER_BLOCK_SIZE(fscp->fs_cache))); CFS_DEBUG(CFSDEBUG_CNODE, printf("cachefs_initcnode: EXIT error %d\n", error)); return (error); } #ifdef DEBUG /* * check that the file header in the cnode matches the one in the front * file */ void validate_fileheader(cnode_t *cp, char *file, int line) { fileheader_t *fhp; int match = 1; int error = 0; int ospl; ASSERT(VALID_ADDR(cp)); ASSERT(VALID_ADDR(file)); if (cp->c_frontvp && C_CACHING(cp) && !(cp->c_flags & (CN_UPDATED | CN_UPDATE_PENDING))) { ASSERT(cp->c_fileheader); error = cachefs_read_file_header(cp->c_frontvp, &fhp, cp->c_attr ? cp->c_attr->ca_type : VNON, C_TO_FSCACHE(cp)->fs_cache); if (!error) { ospl = mutex_spinlock(&cp->c_statelock); if (!(cp->c_flags & (CN_UPDATED | CN_UPDATE_PENDING))) { if (fhp->fh_metadata.md_state != cp->c_fileheader->fh_metadata.md_state) { printf("validate_fileheader: (file %s, line %d) fhp " "0x%p cp 0x%p state mismatch\n", file, line, fhp, cp); match = 0; } else if (fhp->fh_metadata.md_allocents != cp->c_fileheader->fh_metadata.md_allocents) { printf("validate_fileheader: (file %s, line %d) fhp " "0x%p cp 0x%p allocents mismatch\n", file, line, fhp, cp); match = 0; } else if (bcmp(&fhp->fh_metadata.md_attributes, &cp->c_fileheader->fh_metadata.md_attributes, sizeof(vattr_t)) != 0) { printf("validate_fileheader: (file %s, line %d) fhp " "0x%p cp 0x%p attribute mismatch\n", file, line, fhp, cp); match = 0; } else if (bcmp(&fhp->fh_metadata.md_backfid, &cp->c_fileheader->fh_metadata.md_backfid, sizeof(fid_t)) != 0) { printf("validate_fileheader: (file %s, line %s) fhp " "0x%p cp 0x%p back FID mismatch\n", file, line, fhp, cp); match = 0; } else if (bcmp(fhp->fh_allocmap, cp->c_fileheader->fh_allocmap, sizeof(allocmap_t)) != 0) { printf("validate_fileheader: (file %s, line %d) fhp " "0x%p cp 0x%p alloc map mismatch\n", file, line, fhp, cp); match = 0; } ASSERT(match); } mutex_spinunlock(&cp->c_statelock, ospl); } else { printf("validate_fileheader: (file %s, line %d) fhp 0x%p cp " "0x%p error %d\n", file, line, fhp, cp, error); } ASSERT(!(fhp->fh_metadata.md_state & MD_INIT)); CACHEFS_RELEASE_FILEHEADER(&cp->c_frontfid, fhp); } } #endif /* * makecachefsnode() called from lookup (and create). It finds a cnode if one * exists. If one doesn't exist on the hash queues, then it allocates one and * fills it in calling c_initcnode(). */ int makecachefsnode( cnode_t *dp, /* parent directory cnode */ fid_t *backfid, /* file ID for back file */ struct fscache *fscp, /* pointer to mounted file system record */ fileheader_t *fhp, /* pointer to file header */ /* assumed to have already been validated */ vnode_t *frontvp, /* front file system vnode (cached data) */ vnode_t *frontdirvp, /* front file system directory vnode */ /* non-NULL only for directories */ vnode_t *backvp, /* back file system vnode for the file */ /* to which this cnode will correspond */ vattr_t *vap, /* vnode attributes for the new cnode */ /* this is allowed to be NULL; if it is, */ /* we will just get it with getattr */ cred_t *cr, /* credentials */ int flags, /* flags for cnode; normally 0, CN_ROOT */ /* or CN_NOCACHE */ struct cnode **cpp ) /* place to which the cnode pointer for */ /* the new cnode is to be returned */ { vnode_t *realvp = NULL; int have_fid = 0; int check_cnode = 0; int validate = (fhp != NULL) && !(flags & CN_NOCACHE); cnode_t *newcp = NULL; u_int hashvers; struct cnode *cp = NULL; int error; vmap_t vmap; vnode_t *vp; int ospl; fid_t frontfid; int match_error; CFS_DEBUG(CFSDEBUG_CNODE, printf("makecachefsnode: ENTER dcp 0x%p frontvp 0x%p, backvp 0x%p\n", dp, frontvp, backvp)); ASSERT(!(flags & CN_NOCACHE) || !fhp); ASSERT(VALID_ADDR(cpp)); ASSERT(!dp || VALID_ADDR(dp)); ASSERT(!backfid || VALID_ADDR(backfid)); ASSERT(VALID_ADDR(fscp)); ASSERT(!fhp || VALID_ADDR(fhp)); ASSERT(!frontvp || VALID_ADDR(frontvp)); ASSERT(!frontdirvp || VALID_ADDR(frontdirvp)); ASSERT(!backvp || VALID_ADDR(backvp)); ASSERT(!vap || VALID_ADDR(vap)); ASSERT(VALID_ADDR(cr)); ASSERT(!frontvp || (frontvp->v_count > 0)); ASSERT(!frontdirvp || (frontdirvp->v_count > 0)); ASSERT(!backvp || (backvp->v_count > 0)); ASSERT(!issplhi(getsr())); CACHEFUNC_TRACE(CFTRACE_OTHER, (void *)makecachefsnode, current_pid(), dp ? CTOV(dp) : NULL, backfid); CACHEFS_STATS->cs_makecnode++; restart: match_error = 0; error = 0; cp = NULL; if (fhp && !valid_file_header(fhp, backfid)) { CACHEFS_STATS->cs_badheader.cbh_data++; return(ESTALE); } mutex_enter( &fscp->fs_cnodelock ); if (!backfid) { if (frontvp) { VOP_FID2(frontvp, &frontfid, error); if (!error) { have_fid = 1; /* * Look up the cnode by front file ID. If one is * found, activate it. */ if (cp = cfind_by_fid(&frontfid, fscp)) { CFS_DEBUG(CFSDEBUG_CNODE, printf("makecachefsnode: cfind_by_fid 0x%p\n", cp)); goto activate; } } } mutex_exit( &fscp->fs_cnodelock ); return(ENOENT); } lookagain: if ( !(cp = cfind(backfid, fscp)) ) { /* * We will need to create a new cnode. * If the cnode is to be cached (CN_NOCACHE is not set in flags) and * no front vnode has been supplied, return ENOENT. */ if ( !(flags & CN_NOCACHE) && !frontvp && !backvp ) { CACHEFS_STATS->cs_nocnode++; error = ENOENT; cp = NULL; } else { if (!newcp) { CACHEFS_STATS->cs_newcnodes++; /* * We must release the cnode mutex here to allocate the vnode. * It is possible that vn_alloc will attempt to reclaim a vnode * for a cachefs file. cachefs_reclaim acquires fs_cnodelock. */ hashvers = fscp->fs_hashvers; mutex_exit(&fscp->fs_cnodelock); /* always create a new one */ newcp = (struct cnode *)CACHEFS_ZONE_ZALLOC(Cachefs_cnode_zone, KM_SLEEP); (void) cachefs_cnode_cnt(1); newcp->c_vnode = vn_alloc( FSC_TO_VFS(fscp), VNON, FSC_TO_VFS(fscp)->vfs_dev); bhv_desc_init(CTOBHV(newcp), newcp, newcp->c_vnode, &cachefs_vnodeops); vn_bhv_insert_initial(VN_BHV_HEAD(newcp->c_vnode), CTOBHV(newcp)); error = cachefs_initcnode( newcp, dp, backfid, fscp, fhp, frontvp, frontdirvp, backvp, flags, vap, cr ); if (error) { /* * An error occurred in cnode initializaion. Make sure the * file header is not freed. The caller may want it. * Make sure c_backfid is also set to NULL, otherwise, * cachefs_cnode_free will try to free it. That would be * bad. */ newcp->c_fileheader = NULL; newcp->c_backfid = NULL; CNODE_TRACE(CNTRACE_FILEHEADER, newcp, cachefs_initcnode, NULL, 0); newcp->c_flags |= CN_DESTROY; cachefs_cnode_free( newcp ); newcp = NULL; cp = NULL; goto out; } mutex_enter(&fscp->fs_cnodelock); if (hashvers != fscp->fs_hashvers) { CACHEFS_STATS->cs_cnodelookagain++; goto lookagain; } } /* * At this point, we are assured that there is no other cnode * matching the one being created now. */ cp = newcp; newcp = NULL; ASSERT(!error); /* * No matching cnode was found, so enter the one * we just created. */ ASSERT(!cfind(cp->c_backfid, C_TO_FSCACHE(cp))); cachefs_addhash(cp); /* * There is one further set of validations to be * performed on the file header. The caller cannot * be expected to do these as it may not have all of * the necessary information (i.e., back file type and * back file attributes). The validation to be * performed is on the allocation map. * * If the allocation map is invalid, simply invalidate * the cached object. * * We validate here because we know that no other * process has created another cnode with this identity. * * No lock need be acquired by cachefs_inval_object * because this is the only process which can have * access to this cnode at this point. */ if (validate && !valid_allocmap(cp)) { cachefs_inval_object(cp, NOLOCK); } ASSERT(!cp->c_frontvp || (cp->c_frontvp->v_count > 1)); ASSERT(!cp->c_frontdirvp || (cp->c_frontdirvp->v_count > 1)); ASSERT(!cp->c_backvp || (cp->c_backvp->v_count > 1)); CNODE_TRACE(CNTRACE_ACT, cp, (void *)makecachefsnode, cp->c_flags, CTOV(cp)->v_count); CFS_DEBUG(CFSDEBUG_VALIDATE, validate_fileheader(cp, __FILE__, __LINE__)); } } else { CFS_DEBUG(CFSDEBUG_CNODE, printf("makecachefsnode: found 0x%p\n", cp)); if (newcp) { /* * We found a cnode with a matching back file ID. * We created a cnode above but were racing with * another process in this creation. The other * process created the cnode first, now we must * free the one we have and use the one we just * found in the hash. */ ASSERT(newcp != cp); ASSERT(!newcp->c_inhash); /* * If fhp points to the same data as c_fileheader * in the cnode being destroyed, set fhp to NULL * so that it will not be freed later. Otherwise, * we want to free both fhp and c_fileheader. */ if (fhp == newcp->c_fileheader) { fhp = NULL; } /* * The vnode should have no other references, so we * can just use vn_free to free it. */ vn_bhv_remove( VN_BHV_HEAD(newcp->c_vnode), CTOBHV(newcp) ); vn_free( newcp->c_vnode ); /* * We must set c_vnode to NULL and turn on CN_DESTROY * prior to calling cachefs_cnode_free. It is not * absolutely necessary except for debugging. There * are ASSERTs in cachefs_cnode_free which will blow * if the following settings are not made. */ newcp->c_vnode = NULL; newcp->c_flags |= CN_DESTROY; cachefs_cnode_free( newcp ); newcp = NULL; CACHEFS_STATS->cs_cnodetoss++; } activate: ASSERT(cp); /* * We found a cnode with a matching back file ID. */ ospl = mutex_spinlock( &cp->c_statelock ); if ((flags & CN_NOCACHE) && !(cp->c_flags & CN_NOCACHE)) { CACHEFS_STATS->cs_nocache++; CNODE_TRACE(CNTRACE_NOCACHE, cp, makecachefsnode, (int)cp->c_flags, 0); } if (!C_CACHING(cp)) { flags &= ~CN_UPDATED; } cp->c_flags |= flags; if (flags & CN_UPDATED) { CNODE_TRACE(CNTRACE_UPDATE, cp, makecachefsnode, (int)cp->c_flags, 0); if (!cp->c_frontvp) { mutex_spinunlock( &cp->c_statelock, ospl ); error = cachefs_getfrontvp(cp); if (error) { cachefs_nocache(cp); error = 0; } ospl = mutex_spinlock( &cp->c_statelock ); } } if ( vp = CTOV(cp) ) { VMAP(vp, vmap); mutex_spinunlock( &cp->c_statelock, ospl ); CFS_DEBUG(CFSDEBUG_VALIDATE, validate_fileheader(cp, __FILE__, __LINE__)); /* * Check the file ID of the front file provided by the * caller against that contained in the cnode. */ if (C_CACHING(cp)) { if (have_fid) { if (!FID_MATCH(&frontfid, &cp->c_frontfid)) { match_error = EEXIST; } } else if (frontvp) { VOP_FID2(frontvp, &frontfid, error); if (error) { CFS_DEBUG(CFSDEBUG_ERROR, printf("makecachefsnode: error %d getting fid for " "vnode 0x%p\n", error, frontvp)); cachefs_nocache(cp); error = 0; } else { if (!FID_MATCH(&frontfid, &cp->c_frontfid)) { match_error = EEXIST; } } } } /* * The back vnode is set here if it had not already been set. */ if (backvp && !cp->c_backvp) { VOP_REALVP(backvp, &realvp, error); if (!error) { ASSERT(realvp); backvp = realvp; } VN_HOLD(backvp); cp->c_backvp = backvp; error = 0; } /* * The vnode pointer in the cnode is non-null. We've gotten * the vmap for the vnode. Now, get a reference to the vnode. * The race with VOP_RECLAIM is handled in vn_get. If NULL is * returned, the vnode has been reclaimed. The cnode data is * about to be or has already been freed. We must search the * hash again. */ mutex_exit(&fscp->fs_cnodelock); if ( !(vp = vn_get(vp, &vmap, 0)) ) { CACHEFS_STATS->cs_cnoderestart++; CACHEFS_STATS->cs_race.cr_reclaim++; CFS_DEBUG(CFSDEBUG_CNODE, printf("makecachefsnode: NULL vn_get restart, current " "cnode 0x%p\n", cp)); goto restart; } ospl = mutex_spinlock( &cp->c_statelock ); ASSERT(cp->c_vnode == vp); if (cp->c_flags & CN_DESTROY) { mutex_spinunlock( &cp->c_statelock, ospl ); VN_RELE(vp); CACHEFS_STATS->cs_cnoderestart++; CACHEFS_STATS->cs_race.cr_destroy++; CFS_DEBUG(CFSDEBUG_CNODE, printf("makecachefsnode: CN_DESTROY restart, current " "cnode 0x%p\n", cp)); goto restart; } mutex_spinunlock( &cp->c_statelock, ospl ); mutex_enter( &fscp->fs_cnodelock ); ASSERT( vp->v_vfsp->vfs_fstype == cachefsfstyp ); } else if (!(cp->c_flags & CN_RECLAIM)) { if (!(cp->c_flags & CN_VALLOC)) { cp->c_flags |= CN_VALLOC; mutex_spinunlock( &cp->c_statelock, ospl ); mutex_exit(&fscp->fs_cnodelock); vp = vn_alloc( FSC_TO_VFS(fscp), cp->c_type, FSC_TO_VFS(fscp)->vfs_dev); ospl = mutex_spinlock( &cp->c_statelock ); bhv_desc_init(CTOBHV(cp), cp, vp, &cachefs_vnodeops); vn_bhv_insert_initial(VN_BHV_HEAD(vp), CTOBHV(cp)); cp->c_vnode = vp; cp->c_flags &= ~CN_VALLOC; sv_broadcast(&cp->c_valloc_wait); mutex_spinunlock( &cp->c_statelock, ospl ); mutex_enter( &fscp->fs_cnodelock ); } else { mutex_exit(&fscp->fs_cnodelock); sv_wait(&cp->c_valloc_wait, PZERO, &cp->c_statelock, ospl); /* * At this point, another process has woken us, indicating * that the vnode allocation has completed. Since we * had to release fs_cnodelock, we don't know what might * have happened between when we entered sv_wait and now. * We must start over. */ goto restart; } } else { mutex_spinunlock( &cp->c_statelock, ospl ); mutex_exit(&fscp->fs_cnodelock); CACHEFS_STATS->cs_race.cr_reclaim++; CACHEFS_STATS->cs_cnoderestart++; CFS_DEBUG(CFSDEBUG_CNODE, printf("makecachefsnode: NULL vnode restart, current " "cnode 0x%p\n", cp)); goto restart; } ASSERT(!(cp->c_flags & CN_RECLAIM)); CACHEFS_STATS->cs_cnodehit++; check_cnode = 1; /* * The cnode existed and was not being reclaimed. We do not * need the file header supplied by the caller. We free it here * because the only way the caller knows to free it is when an * error is returned. */ if (fhp && !error) { if (frontvp != cp->c_frontvp) { if (!have_fid) { if (frontvp) { VOP_FID2(frontvp, &frontfid, error); CACHEFS_RELEASE_FILEHEADER(error ? NULL : &frontfid, fhp); error = 0; } else { CACHEFS_RELEASE_FILEHEADER(NULL, fhp); } } else { CACHEFS_RELEASE_FILEHEADER(&frontfid, fhp); } } else { CACHEFS_RELEASE_FILEHEADER(&cp->c_frontfid, fhp); } } /* * Make sure the cnode for the parent has been filled in and * matches the one supplied by the caller. If it does not * match, the file has been renamed, so update the cnode. */ if (dp && (cp->c_dcp != dp)) { if (cp->c_dcp) { CN_RELE(cp->c_dcp); } CN_HOLD(dp); cp->c_dcp = dp; } CNODE_TRACE(CNTRACE_ACT, cp, (void *)makecachefsnode, cp->c_flags, CTOV(cp)->v_count); CFS_DEBUG(CFSDEBUG_VALIDATE, validate_fileheader(cp, __FILE__, __LINE__)); } #ifdef DEBUG if (!error || (error == EEXIST)) { ASSERT(cp); ASSERT(C_TO_FSCACHE(cp) == fscp); ASSERT(cp->c_frontfid.fid_len || (cp->c_flags & CN_NOCACHE)); ASSERT(!cp->c_frontvp || (cp->c_frontvp->v_count > 0)); ASSERT(!cp->c_frontdirvp || (cp->c_frontdirvp->v_count > 0)); if (cp->c_frontvp && fscp->fs_rootvp && fscp->fs_root->c_frontvp) { ASSERT(cp->c_frontvp->v_vfsp == fscp->fs_root->c_frontvp->v_vfsp); } if (cp->c_backvp) { if (fscp->fs_rootvp && fscp->fs_root->c_backvp) { ASSERT(cp->c_backvp->v_vfsp == fscp->fs_root->c_backvp->v_vfsp); } ASSERT(cp->c_backvp->v_count > 0); ASSERT(CTOV(cp)->v_type == cp->c_backvp->v_type); ASSERT(!fhp || (cp->c_attr->ca_type == cp->c_backvp->v_type)); } ASSERT(!(cp->c_flags & CN_DESTROY)); ASSERT(!cp->c_fileheader || !(cp->c_fileheader->fh_metadata.md_state & MD_INIT)); ASSERT(cfind(cp->c_backfid, C_TO_FSCACHE(cp)) == cp); ASSERT(!cp->c_fileheader || valid_file_header(cp->c_fileheader, backfid)); ASSERT(!cp->c_fileheader || find_fileheader_by_fid(&cp->c_frontfid, cp->c_fileheader)); } ASSERT(!issplhi(getsr())); #endif mutex_exit( &fscp->fs_cnodelock ); if (check_cnode) { /* * We check the cached object here, primarily to make sure it * is not stale. If we get an error, destroy the cnode and * return the error to the caller. Set CN_DESTROY before * VN_RELE so that cachefs_inactive will see CN_DESTROY. * Do this after releasing fs_cnodelock to prevent a double * trip of the mutex. */ error = CFSOP_CHECK_COBJECT(fscp, cp, vap, cr, UNLOCKED); if (error) { ospl = mutex_spinlock( &cp->c_statelock ); cp->c_flags |= CN_DESTROY; mutex_spinunlock( &cp->c_statelock, ospl ); /* * fs_cnodelock must be released here because VN_RELE may * go through cachefs_inactive, where fs_cnodelock will * be acquired. */ ASSERT(CTOV(cp)); VN_RELE(CTOV(cp)); cp = NULL; } } out: ASSERT(!(error && (error != EEXIST)) || !cp); ASSERT(!(!error || (error == EEXIST)) || cp); ASSERT(!newcp); *cpp = cp; CFS_DEBUG(CFSDEBUG_CNODE, printf("makecachefsnode: EXIT cp 0x%p error %d\n", *cpp, error)); return (error ? error : match_error); } /* * Adjusts the number of cnodes by the specified amount and * returns the new number of cnodes. */ int cachefs_cnode_count = 0; lock_t cachefs_cnode_cnt_lock; int cachefs_cnode_cnt(int delta) { int old_spl; CACHEFUNC_TRACE(CFTRACE_OTHER, (void *)cachefs_cnode_cnt, current_pid(), delta, 0); /* grab lock */ old_spl = mutex_spinlock( &cachefs_cnode_cnt_lock ); /* adjust count, check for error */ cachefs_cnode_count += delta; ASSERT(cachefs_cnode_count >= 0); /* free lock */ mutex_spinunlock( &cachefs_cnode_cnt_lock, old_spl ); /* return new value of count */ return (cachefs_cnode_count); }