/* Try to discard pages, in order to recycle a vcache entry. * * We also make some sanity checks: ref count, open count, held locks. * * We also do some non-VM-related chores, such as releasing the cred pointer * (for AIX and Solaris) and releasing the gnode (for AIX). * * Locking: afs_xvcache lock is held. If it is dropped and re-acquired, * *slept should be set to warn the caller. * * Formerly, afs_xvcache was dropped and re-acquired for Solaris, but now it * is not dropped and re-acquired for any platform. It may be that *slept is * therefore obsolescent. */ int osi_VM_FlushVCache(struct vcache *avc, int *slept) { if (avc->vrefCount != 0) return EBUSY; if (avc->opens) return EBUSY; /* if a lock is held, give up */ if (CheckLock(&avc->lock)) return EBUSY; AFS_GUNLOCK(); pvn_vplist_dirty(AFSTOV(avc), 0, NULL, B_TRUNC | B_INVAL, CRED()); AFS_GLOCK(); /* Might as well make the obvious check */ if (AFSTOV(avc)->v_pages) return EBUSY; /* should be all gone still */ rw_destroy(&avc->rwlock); if (avc->credp) { crfree(avc->credp); avc->credp = NULL; } return 0; }
/* Try to invalidate pages, for "fs flush" or "fs flushv"; or * try to free pages, when deleting a file. * * Locking: the vcache entry's lock is held. It may be dropped and * re-obtained. */ void osi_VM_TryToSmush(struct vcache *avc, afs_ucred_t *acred, int sync) { AFS_GUNLOCK(); (void)pvn_vplist_dirty(AFSTOV(avc), (u_offset_t) 0, afs_putapage, (sync ? B_INVAL : B_FREE), acred); AFS_GLOCK(); }
/* Try to store pages to cache, in order to store a file back to the server. * * Locking: the vcache entry's lock is held. It will usually be dropped and * re-obtained. */ void osi_VM_StoreAllSegments(struct vcache *avc) { AFS_GUNLOCK(); (void)pvn_vplist_dirty(AFSTOV(avc), (u_offset_t) 0, afs_putapage, 0, CRED()); AFS_GLOCK(); }
/* Purge pages beyond end-of-file, when truncating a file. * * Locking: no lock is held, not even the global lock. * Pageins are blocked (activeV is raised). */ void osi_VM_Truncate(struct vcache *avc, int alen, afs_ucred_t *acred) { /* * It's OK to specify afs_putapage here, even though we aren't holding * the vcache entry lock, because it isn't going to get called. */ pvn_vplist_dirty(AFSTOV(avc), alen, afs_putapage, B_TRUNC | B_INVAL, acred); }
/* * The disk has been changed! */ void pc_diskchanged(struct pcfs *fsp) { struct pcnode *pcp, *npcp = NULL; struct pchead *hp; struct vnode *vp; extern vfs_t EIO_vfs; struct vfs *vfsp; /* * Eliminate all pcnodes (dir & file) associated with this fs. * If the node is internal, ie, no references outside of * pcfs itself, then release the associated vnode structure. * Invalidate the in core FAT. * Invalidate cached data blocks and blocks waiting for I/O. */ PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp); vfsp = PCFSTOVFS(fsp); for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) { for (pcp = hp->pch_forw; pcp != (struct pcnode *)hp; pcp = npcp) { npcp = pcp -> pc_forw; vp = PCTOV(pcp); if ((vp->v_vfsp == vfsp) && !(pcp->pc_flags & PC_RELEHOLD)) { mutex_enter(&(vp)->v_lock); if (vp->v_count > 0) { mutex_exit(&(vp)->v_lock); continue; } mutex_exit(&(vp)->v_lock); VN_HOLD(vp); remque(pcp); vp->v_data = NULL; vp->v_vfsp = &EIO_vfs; vp->v_type = VBAD; VN_RELE(vp); if (!(pcp->pc_flags & PC_EXTERNAL)) { (void) pvn_vplist_dirty(vp, (u_offset_t)0, pcfs_putapage, B_INVAL | B_TRUNC, (struct cred *)NULL); vn_free(vp); } kmem_free(pcp, sizeof (struct pcnode)); fsp->pcfs_nrefs --; VFS_RELE(vfsp); } } } for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) { for (pcp = hp->pch_forw; fsp->pcfs_frefs && pcp != (struct pcnode *)hp; pcp = npcp) { npcp = pcp -> pc_forw; vp = PCTOV(pcp); if ((vp->v_vfsp == vfsp) && !(pcp->pc_flags & PC_RELEHOLD)) { mutex_enter(&(vp)->v_lock); if (vp->v_count > 0) { mutex_exit(&(vp)->v_lock); continue; } mutex_exit(&(vp)->v_lock); VN_HOLD(vp); remque(pcp); vp->v_data = NULL; vp->v_vfsp = &EIO_vfs; vp->v_type = VBAD; VN_RELE(vp); if (!(pcp->pc_flags & PC_EXTERNAL)) { (void) pvn_vplist_dirty(vp, (u_offset_t)0, pcfs_putapage, B_INVAL | B_TRUNC, (struct cred *)NULL); vn_free(vp); } kmem_free(pcp, sizeof (struct pcnode)); fsp->pcfs_frefs--; fsp->pcfs_nrefs--; VFS_RELE(vfsp); } } } #ifdef undef if (fsp->pcfs_frefs) { rw_exit(&pcnodes_lock); panic("pc_diskchanged: frefs"); } if (fsp->pcfs_nrefs) { rw_exit(&pcnodes_lock); panic("pc_diskchanged: nrefs"); } #endif if (!(vfsp->vfs_flag & VFS_UNMOUNTED) && fsp->pcfs_fatp != (uchar_t *)0) { pc_invalfat(fsp); } else { binval(fsp->pcfs_xdev); } }
/* * Truncate a file to a length. * Node must be locked. */ int pc_truncate(struct pcnode *pcp, uint_t length) { struct pcfs *fsp; struct vnode *vp; int error = 0; PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n", (void *)pcp, length, pcp->pc_size); vp = PCTOV(pcp); if (pcp->pc_flags & PC_INVAL) return (EIO); fsp = VFSTOPCFS(vp->v_vfsp); /* * directories are always truncated to zero and are not marked */ if (vp->v_type == VDIR) { error = pc_bfree(pcp, 0); return (error); } /* * If length is the same as the current size * just mark the pcnode and return. */ if (length > pcp->pc_size) { daddr_t bno; uint_t llcn = howmany((offset_t)length, fsp->pcfs_clsize); /* * We are extending a file. * Extend it with _one_ call to pc_balloc (no holes) * since we don't need to use the block number(s). */ if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) < (daddr_t)llcn) { error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno); } if (error) { pc_cluster32_t ncl = 0; PC_DPRINTF1(2, "pc_truncate: error=%d\n", error); /* * probably ran out disk space; * determine current file size */ if (pc_fileclsize(fsp, pcp->pc_scluster, &ncl)) { PC_DPRINTF1(2, "cluster chain corruption, " "scluster=%d\n", pcp->pc_scluster); pcp->pc_flags |= PC_INVAL; } pcp->pc_size = fsp->pcfs_clsize * ncl; } else pcp->pc_size = length; } else if (length < pcp->pc_size) { /* * We are shrinking a file. * Free blocks after the block that length points to. */ if (pc_blkoff(fsp, length) == 0) { /* * Truncation to a block (cluster size) boundary only * requires us to invalidate everything after the new * end of the file. */ (void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length, pcfs_putapage, B_INVAL | B_TRUNC, CRED()); } else { /* * pvn_vpzero() cannot deal with more than MAXBSIZE * chunks. Since the FAT clustersize can get larger * than that, we'll zero from the new length to the * end of the cluster for clustersizes smaller than * MAXBSIZE - or the end of the MAXBSIZE block in * case we've got a large clustersize. */ size_t nbytes = roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) - length; pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes); (void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length + nbytes, pcfs_putapage, B_INVAL | B_TRUNC, CRED()); } error = pc_bfree(pcp, (pc_cluster32_t) howmany((offset_t)length, fsp->pcfs_clsize)); pcp->pc_size = length; } /* * This is the only place in PCFS code where pc_mark_mod() is called * without setting PC_MOD. May be a historical artifact ... */ pc_mark_mod(fsp, pcp); return (error); }
void pc_rele(struct pcnode *pcp) { struct pcfs *fsp; struct vnode *vp; int err; vp = PCTOV(pcp); PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp); fsp = VFSTOPCFS(vp->v_vfsp); ASSERT(fsp->pcfs_flags & PCFS_LOCKED); rw_enter(&pcnodes_lock, RW_WRITER); pcp->pc_flags |= PC_RELEHOLD; retry: if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) { /* * If the file was removed while active it may be safely * truncated now. */ if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) { (void) pc_truncate(pcp, 0); } else if (pcp->pc_flags & PC_CHG) { (void) pc_nodeupdate(pcp); } err = syncpcp(pcp, B_INVAL); if (err) { (void) syncpcp(pcp, B_INVAL | B_FORCE); } } if (vn_has_cached_data(vp)) { /* * pvn_vplist_dirty will abort all old pages */ (void) pvn_vplist_dirty(vp, (u_offset_t)0, pcfs_putapage, B_INVAL, (struct cred *)NULL); } (void) pc_syncfat(fsp); mutex_enter(&vp->v_lock); if (vn_has_cached_data(vp)) { mutex_exit(&vp->v_lock); goto retry; } ASSERT(!vn_has_cached_data(vp)); vp->v_count--; /* release our hold from vn_rele */ if (vp->v_count > 0) { /* Is this check still needed? */ PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp); mutex_exit(&vp->v_lock); pcp->pc_flags &= ~PC_RELEHOLD; rw_exit(&pcnodes_lock); return; } remque(pcp); rw_exit(&pcnodes_lock); /* * XXX - old code had a check for !(pcp->pc_flags & PC_INVAL) * here. Seems superfluous/incorrect, but then earlier on PC_INVAL * was never set anywhere in PCFS. Now it is, and we _have_ to drop * the file reference here. Else, we'd screw up umount/modunload. */ if ((vp->v_type == VREG)) { fsp->pcfs_frefs--; } fsp->pcfs_nrefs--; VFS_RELE(vp->v_vfsp); if (fsp->pcfs_nrefs < 0) { panic("pc_rele: nrefs count"); } if (fsp->pcfs_frefs < 0) { panic("pc_rele: frefs count"); } mutex_exit(&vp->v_lock); vn_invalid(vp); vn_free(vp); kmem_free(pcp, sizeof (struct pcnode)); }
/************************************************************************ * iumfs_free_node() * * 指定された vnode および iumnode を解放する * * 1. iumnode に関連づいたリソースを解放 * 2. iumnode 構造体を解放 * 3. vnode 構造体を解放 * * これが呼ばれるのは、iumfs_inactive() もしくは iumfs_unmount() 経由の * iumfs_free_all_node() だけ。つまり、v_count が 1(未参照状態)である * 事が確かな場合だけ。 * * 引数: * * vp: 解放する vnode 構造体のポインタ * cr : システムコールを呼び出したユーザのクレデンシャル * * 戻り値: * 無し * ************************************************************************/ void iumfs_free_node(vnode_t *vp, struct cred *cr) { iumnode_t *inp; // ファイルシステム型依存のノード情報(iumnode構造体) vnode_t *rootvp; // ファイルシステムのルートディレクトリの vnode。 iumfs_t *iumfsp; // ファイルシステム型依存のプライベートデータ構造体 vfs_t *vfsp; // ファイルシステム構造体 int err; DEBUG_PRINT((CE_CONT, "iumfs_free_node is called\n")); iumfsp = VNODE2IUMFS(vp); vfsp = VNODE2VFS(vp); inp = VNODE2IUMNODE(vp); DEBUG_PRINT((CE_CONT, "iumfs_free_node: vnode=%p, vp->v_count=%d\n", vp, vp->v_count)); /* * 最初にノードリンクリストから iumnode をはずす。誰かが利用中(EBUSY)だったらリターン。 * 仮にノードリストに入っていなかったとしても、(ありえないはずだが) vnode のフリーは行う。 */ if((err = iumfs_remove_node_from_list(vfsp, vp)) != 0){ if (err == ENOENT) cmn_err(CE_CONT, "iumfs_free_node: can't find vnode in the list. Free it anyway.\n"); else return; } // debug 用 rootvp = VNODE2ROOT(vp); if (rootvp != NULL && VN_CMP(rootvp, vp) != 0) { DEBUG_PRINT((CE_CONT, "iumfs_free_node: rootvnode is being freed\n")); mutex_enter(&(iumfsp->iumfs_lock)); iumfsp->rootvnode = NULL; mutex_exit(&(iumfsp->iumfs_lock)); } /* * もし iumnode にデータ(ディレクトリエントリ等) * を含んでいたらそれらも解放する。 */ if (inp->data != NULL) { kmem_free(inp->data, inp->dlen); } /* * この vnode に関連した page を無効にする */ err = pvn_vplist_dirty(vp, 0, iumfs_putapage, B_INVAL, cr); DEBUG_PRINT((CE_CONT, "iumfs_free_node: pvn_vplist_dirty returned with (%d)\n", err)); if (vn_has_cached_data(vp)) { cmn_err(CE_WARN, "iumfs_free_node: vnode still have cached\n"); } // iumnode を解放 mutex_destroy(&(inp)->i_dlock); rw_destroy(&(inp)->i_listlock); kmem_free(inp, sizeof (iumnode_t)); // vnode を解放 #ifdef SOL10 vn_free(vp); #else mutex_destroy(&(vp)->v_lock); kmem_free(vp, sizeof (vnode_t)); #endif DEBUG_PRINT((CE_CONT, "iumfs_free_node: return\n")); return; }