17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 520a1ae8aSjkennedy * Common Development and Distribution License (the "License"). 620a1ae8aSjkennedy * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 277c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate /* 307c478bd9Sstevel@tonic-gate * Portions of this source code were derived from Berkeley 4.3 BSD 317c478bd9Sstevel@tonic-gate * under license from the Regents of the University of California. 327c478bd9Sstevel@tonic-gate */ 337c478bd9Sstevel@tonic-gate 347c478bd9Sstevel@tonic-gate #include <sys/types.h> 357c478bd9Sstevel@tonic-gate #include <sys/systm.h> 367c478bd9Sstevel@tonic-gate #include <sys/errno.h> 377c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 387c478bd9Sstevel@tonic-gate #include <sys/buf.h> 397c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 407c478bd9Sstevel@tonic-gate #include <sys/vfs.h> 417c478bd9Sstevel@tonic-gate #include <sys/user.h> 427c478bd9Sstevel@tonic-gate #include <sys/callb.h> 437c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 447c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h> 457c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_log.h> 467c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_trans.h> 477c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_acl.h> 487c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_bio.h> 497c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h> 507c478bd9Sstevel@tonic-gate #include <sys/debug.h> 517c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 527c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 53b7deb15bSWolfgang Schremser #include <vm/pvn.h> 547c478bd9Sstevel@tonic-gate 557c478bd9Sstevel@tonic-gate extern pri_t minclsyspri; 567c478bd9Sstevel@tonic-gate extern int hash2ints(); 577c478bd9Sstevel@tonic-gate extern struct kmem_cache *inode_cache; /* cache of free inodes */ 587c478bd9Sstevel@tonic-gate extern int ufs_idle_waiters; 597c478bd9Sstevel@tonic-gate extern struct instats ins; 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate static void ufs_attr_purge(struct inode *); 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate /* 647c478bd9Sstevel@tonic-gate * initialize a thread's queue struct 657c478bd9Sstevel@tonic-gate */ 667c478bd9Sstevel@tonic-gate void 677c478bd9Sstevel@tonic-gate ufs_thread_init(struct ufs_q *uq, int lowat) 687c478bd9Sstevel@tonic-gate { 697c478bd9Sstevel@tonic-gate bzero((caddr_t)uq, sizeof (*uq)); 707c478bd9Sstevel@tonic-gate cv_init(&uq->uq_cv, NULL, CV_DEFAULT, NULL); 717c478bd9Sstevel@tonic-gate mutex_init(&uq->uq_mutex, NULL, MUTEX_DEFAULT, NULL); 727c478bd9Sstevel@tonic-gate uq->uq_lowat = lowat; 737c478bd9Sstevel@tonic-gate uq->uq_hiwat = 2 * lowat; 747c478bd9Sstevel@tonic-gate uq->uq_threadp = NULL; 757c478bd9Sstevel@tonic-gate } 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate /* 787c478bd9Sstevel@tonic-gate * start a thread for a queue (assumes success) 797c478bd9Sstevel@tonic-gate */ 807c478bd9Sstevel@tonic-gate void 817c478bd9Sstevel@tonic-gate ufs_thread_start(struct ufs_q *uq, void (*func)(), struct vfs *vfsp) 827c478bd9Sstevel@tonic-gate { 837c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 847c478bd9Sstevel@tonic-gate if (uq->uq_threadp == NULL) { 857c478bd9Sstevel@tonic-gate uq->uq_threadp = thread_create(NULL, 0, func, vfsp, 0, &p0, 867c478bd9Sstevel@tonic-gate TS_RUN, minclsyspri); 877c478bd9Sstevel@tonic-gate uq->uq_flags = 0; 887c478bd9Sstevel@tonic-gate } 897c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 907c478bd9Sstevel@tonic-gate } 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate /* 937c478bd9Sstevel@tonic-gate * wait for the thread to exit 947c478bd9Sstevel@tonic-gate */ 957c478bd9Sstevel@tonic-gate void 967c478bd9Sstevel@tonic-gate ufs_thread_exit(struct ufs_q *uq) 977c478bd9Sstevel@tonic-gate { 987c478bd9Sstevel@tonic-gate kt_did_t ufs_thread_did = 0; 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 1017c478bd9Sstevel@tonic-gate uq->uq_flags &= ~(UQ_SUSPEND | UQ_SUSPENDED); 1027c478bd9Sstevel@tonic-gate if (uq->uq_threadp != NULL) { 1037c478bd9Sstevel@tonic-gate ufs_thread_did = uq->uq_threadp->t_did; 1047c478bd9Sstevel@tonic-gate uq->uq_flags |= (UQ_EXIT|UQ_WAIT); 1057c478bd9Sstevel@tonic-gate cv_broadcast(&uq->uq_cv); 1067c478bd9Sstevel@tonic-gate } 1077c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate /* 1107c478bd9Sstevel@tonic-gate * It's safe to call thread_join() with an already-gone 1117c478bd9Sstevel@tonic-gate * t_did, but we have to obtain it before the kernel 1127c478bd9Sstevel@tonic-gate * thread structure is freed. We do so above under the 1137c478bd9Sstevel@tonic-gate * protection of the uq_mutex when we're sure the thread 1147c478bd9Sstevel@tonic-gate * still exists and it's save to de-reference it. 1157c478bd9Sstevel@tonic-gate * We also have to check if ufs_thread_did is != 0 1167c478bd9Sstevel@tonic-gate * before calling thread_join() since thread 0 in the system 1177c478bd9Sstevel@tonic-gate * gets a t_did of 0. 1187c478bd9Sstevel@tonic-gate */ 1197c478bd9Sstevel@tonic-gate if (ufs_thread_did) 1207c478bd9Sstevel@tonic-gate thread_join(ufs_thread_did); 1217c478bd9Sstevel@tonic-gate } 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate /* 1247c478bd9Sstevel@tonic-gate * wait for a thread to suspend itself on the caller's behalf 1257c478bd9Sstevel@tonic-gate * the caller is responsible for continuing the thread 1267c478bd9Sstevel@tonic-gate */ 1277c478bd9Sstevel@tonic-gate void 1287c478bd9Sstevel@tonic-gate ufs_thread_suspend(struct ufs_q *uq) 1297c478bd9Sstevel@tonic-gate { 1307c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 1317c478bd9Sstevel@tonic-gate if (uq->uq_threadp != NULL) { 1327c478bd9Sstevel@tonic-gate /* 1337c478bd9Sstevel@tonic-gate * wait while another thread is suspending this thread. 1347c478bd9Sstevel@tonic-gate * no need to do a cv_broadcast(), as whoever suspended 135121be23bSjkennedy * the thread must continue it at some point. 1367c478bd9Sstevel@tonic-gate */ 1377c478bd9Sstevel@tonic-gate while ((uq->uq_flags & UQ_SUSPEND) && 1387c478bd9Sstevel@tonic-gate (uq->uq_threadp != NULL)) { 139121be23bSjkennedy /* 140121be23bSjkennedy * We can't use cv_signal() because if our 141121be23bSjkennedy * signal doesn't happen to hit the desired 142121be23bSjkennedy * thread but instead some other waiter like 143121be23bSjkennedy * ourselves, we'll wait forever for a 144121be23bSjkennedy * response. Well, at least an indeterminate 145121be23bSjkennedy * amount of time until we just happen to get 146121be23bSjkennedy * lucky from whomever did get signalled doing 147121be23bSjkennedy * a cv_signal() of their own. This is an 148121be23bSjkennedy * unfortunate performance lossage. 149121be23bSjkennedy */ 1507c478bd9Sstevel@tonic-gate uq->uq_flags |= UQ_WAIT; 1517c478bd9Sstevel@tonic-gate cv_wait(&uq->uq_cv, &uq->uq_mutex); 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate 1548db829a5Smishra uq->uq_flags |= (UQ_SUSPEND | UQ_WAIT); 1558db829a5Smishra 1567c478bd9Sstevel@tonic-gate /* 1577c478bd9Sstevel@tonic-gate * wait for the thread to suspend itself 1587c478bd9Sstevel@tonic-gate */ 1598db829a5Smishra if ((uq->uq_flags & UQ_SUSPENDED) == 0 && 1608db829a5Smishra (uq->uq_threadp != NULL)) { 1618db829a5Smishra cv_broadcast(&uq->uq_cv); 1628db829a5Smishra } 1638db829a5Smishra 1647c478bd9Sstevel@tonic-gate while (((uq->uq_flags & UQ_SUSPENDED) == 0) && 1657c478bd9Sstevel@tonic-gate (uq->uq_threadp != NULL)) { 1667c478bd9Sstevel@tonic-gate cv_wait(&uq->uq_cv, &uq->uq_mutex); 1677c478bd9Sstevel@tonic-gate } 1687c478bd9Sstevel@tonic-gate } 1697c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 1707c478bd9Sstevel@tonic-gate } 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate /* 1737c478bd9Sstevel@tonic-gate * allow a thread to continue from a ufs_thread_suspend() 1747c478bd9Sstevel@tonic-gate * This thread must be the same as the thread that called 1757c478bd9Sstevel@tonic-gate * ufs_thread_suspend. 1767c478bd9Sstevel@tonic-gate */ 1777c478bd9Sstevel@tonic-gate void 1787c478bd9Sstevel@tonic-gate ufs_thread_continue(struct ufs_q *uq) 1797c478bd9Sstevel@tonic-gate { 1807c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 1817c478bd9Sstevel@tonic-gate uq->uq_flags &= ~(UQ_SUSPEND | UQ_SUSPENDED); 1827c478bd9Sstevel@tonic-gate cv_broadcast(&uq->uq_cv); 1837c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 1847c478bd9Sstevel@tonic-gate } 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate /* 1877c478bd9Sstevel@tonic-gate * some common code for managing a threads execution 1887c478bd9Sstevel@tonic-gate * uq is locked at entry and return 1897c478bd9Sstevel@tonic-gate * may sleep 1907c478bd9Sstevel@tonic-gate * may exit 1917c478bd9Sstevel@tonic-gate */ 1927c478bd9Sstevel@tonic-gate /* 1937c478bd9Sstevel@tonic-gate * Kind of a hack passing in the callb_cpr_t * here. 1947c478bd9Sstevel@tonic-gate * It should really be part of the ufs_q structure. 1957c478bd9Sstevel@tonic-gate * I did not put it in there because we are already in beta 1967c478bd9Sstevel@tonic-gate * and I was concerned that changing ufs_inode.h to include 1977c478bd9Sstevel@tonic-gate * callb.h might break something. 1987c478bd9Sstevel@tonic-gate */ 1997c478bd9Sstevel@tonic-gate int 2007c478bd9Sstevel@tonic-gate ufs_thread_run(struct ufs_q *uq, callb_cpr_t *cprinfop) 2017c478bd9Sstevel@tonic-gate { 2027c478bd9Sstevel@tonic-gate again: 2037c478bd9Sstevel@tonic-gate ASSERT(uq->uq_ne >= 0); 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate if (uq->uq_flags & UQ_SUSPEND) { 2067c478bd9Sstevel@tonic-gate uq->uq_flags |= UQ_SUSPENDED; 2077c478bd9Sstevel@tonic-gate } else if (uq->uq_flags & UQ_EXIT) { 2087c478bd9Sstevel@tonic-gate /* 2097c478bd9Sstevel@tonic-gate * exiting; empty the queue (may infinite loop) 2107c478bd9Sstevel@tonic-gate */ 2117c478bd9Sstevel@tonic-gate if (uq->uq_ne) 2127c478bd9Sstevel@tonic-gate return (uq->uq_ne); 2137c478bd9Sstevel@tonic-gate uq->uq_threadp = NULL; 214121be23bSjkennedy if (uq->uq_flags & UQ_WAIT) { 2157c478bd9Sstevel@tonic-gate cv_broadcast(&uq->uq_cv); 216121be23bSjkennedy } 2177c478bd9Sstevel@tonic-gate uq->uq_flags &= ~(UQ_EXIT | UQ_WAIT); 2187c478bd9Sstevel@tonic-gate CALLB_CPR_EXIT(cprinfop); 2197c478bd9Sstevel@tonic-gate thread_exit(); 2207c478bd9Sstevel@tonic-gate } else if (uq->uq_ne >= uq->uq_lowat) { 2217c478bd9Sstevel@tonic-gate /* 2227c478bd9Sstevel@tonic-gate * process a block of entries until below high water mark 2237c478bd9Sstevel@tonic-gate */ 2247c478bd9Sstevel@tonic-gate return (uq->uq_ne - (uq->uq_lowat >> 1)); 2257c478bd9Sstevel@tonic-gate } 2267c478bd9Sstevel@tonic-gate if (uq->uq_flags & UQ_WAIT) { 2277c478bd9Sstevel@tonic-gate uq->uq_flags &= ~UQ_WAIT; 2287c478bd9Sstevel@tonic-gate cv_broadcast(&uq->uq_cv); 2297c478bd9Sstevel@tonic-gate } 2307c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(cprinfop); 2317c478bd9Sstevel@tonic-gate cv_wait(&uq->uq_cv, &uq->uq_mutex); 2327c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(cprinfop, &uq->uq_mutex); 2337c478bd9Sstevel@tonic-gate goto again; 2347c478bd9Sstevel@tonic-gate } 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gate /* 2377c478bd9Sstevel@tonic-gate * DELETE INODE 2387c478bd9Sstevel@tonic-gate * The following routines implement the protocol for freeing the resources 2397c478bd9Sstevel@tonic-gate * held by an idle and deleted inode. 2407c478bd9Sstevel@tonic-gate */ 2417c478bd9Sstevel@tonic-gate void 2427c478bd9Sstevel@tonic-gate ufs_delete(struct ufsvfs *ufsvfsp, struct inode *ip, int dolockfs) 2437c478bd9Sstevel@tonic-gate { 2447c478bd9Sstevel@tonic-gate ushort_t mode; 2457c478bd9Sstevel@tonic-gate struct vnode *vp = ITOV(ip); 2467c478bd9Sstevel@tonic-gate struct ulockfs *ulp; 2477c478bd9Sstevel@tonic-gate int trans_size; 2487c478bd9Sstevel@tonic-gate int dorwlock = ((ip->i_mode & IFMT) == IFREG); 2497c478bd9Sstevel@tonic-gate int issync; 2507c478bd9Sstevel@tonic-gate int err; 2517c478bd9Sstevel@tonic-gate struct inode *dp; 252121be23bSjkennedy struct ufs_q *delq = &ufsvfsp->vfs_delete; 253121be23bSjkennedy struct ufs_delq_info *delq_info = &ufsvfsp->vfs_delete_info; 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate /* 2567c478bd9Sstevel@tonic-gate * Ignore if deletes are not allowed (wlock/hlock) 2577c478bd9Sstevel@tonic-gate */ 2587c478bd9Sstevel@tonic-gate if (ULOCKFS_IS_NOIDEL(ITOUL(ip))) { 259d13c09a7Svk154806 mutex_enter(&delq->uq_mutex); 260d13c09a7Svk154806 delq_info->delq_unreclaimed_blocks -= ip->i_blocks; 261d13c09a7Svk154806 delq_info->delq_unreclaimed_files--; 262d13c09a7Svk154806 mutex_exit(&delq->uq_mutex); 2637c478bd9Sstevel@tonic-gate VN_RELE(vp); 2647c478bd9Sstevel@tonic-gate return; 2657c478bd9Sstevel@tonic-gate } 2667c478bd9Sstevel@tonic-gate 2677c478bd9Sstevel@tonic-gate if ((vp->v_count > 1) || (ip->i_mode == 0)) { 268d13c09a7Svk154806 mutex_enter(&delq->uq_mutex); 269d13c09a7Svk154806 delq_info->delq_unreclaimed_blocks -= ip->i_blocks; 270d13c09a7Svk154806 delq_info->delq_unreclaimed_files--; 271d13c09a7Svk154806 mutex_exit(&delq->uq_mutex); 2727c478bd9Sstevel@tonic-gate VN_RELE(vp); 2737c478bd9Sstevel@tonic-gate return; 2747c478bd9Sstevel@tonic-gate } 2757c478bd9Sstevel@tonic-gate /* 2767c478bd9Sstevel@tonic-gate * If we are called as part of setting a fs lock, then only 2777c478bd9Sstevel@tonic-gate * do part of the lockfs protocol. In other words, don't hang. 2787c478bd9Sstevel@tonic-gate */ 2797c478bd9Sstevel@tonic-gate if (dolockfs) { 2807c478bd9Sstevel@tonic-gate if (ufs_lockfs_begin(ufsvfsp, &ulp, ULOCKFS_DELETE_MASK)) 2817c478bd9Sstevel@tonic-gate return; 2827c478bd9Sstevel@tonic-gate } else { 2837c478bd9Sstevel@tonic-gate /* 2847c478bd9Sstevel@tonic-gate * check for recursive VOP call 2857c478bd9Sstevel@tonic-gate */ 2867c478bd9Sstevel@tonic-gate if (curthread->t_flag & T_DONTBLOCK) { 2877c478bd9Sstevel@tonic-gate ulp = NULL; 2887c478bd9Sstevel@tonic-gate } else { 2897c478bd9Sstevel@tonic-gate ulp = &ufsvfsp->vfs_ulockfs; 2907c478bd9Sstevel@tonic-gate curthread->t_flag |= T_DONTBLOCK; 2917c478bd9Sstevel@tonic-gate } 2927c478bd9Sstevel@tonic-gate } 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate /* 2957c478bd9Sstevel@tonic-gate * Hold rwlock to synchronize with (nfs) writes 2967c478bd9Sstevel@tonic-gate */ 2977c478bd9Sstevel@tonic-gate if (dorwlock) 2987c478bd9Sstevel@tonic-gate rw_enter(&ip->i_rwlock, RW_WRITER); 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate /* 3017c478bd9Sstevel@tonic-gate * Delete the attribute directory. 3027c478bd9Sstevel@tonic-gate */ 3037c478bd9Sstevel@tonic-gate if (ip->i_oeftflag != 0) { 3047c478bd9Sstevel@tonic-gate TRANS_BEGIN_CSYNC(ufsvfsp, issync, TOP_REMOVE, 3057c478bd9Sstevel@tonic-gate trans_size = (int)TOP_REMOVE_SIZE(ip)); 3067c478bd9Sstevel@tonic-gate rw_enter(&ip->i_contents, RW_WRITER); 3077c478bd9Sstevel@tonic-gate err = ufs_iget(ip->i_vfs, ip->i_oeftflag, 3087c478bd9Sstevel@tonic-gate &dp, CRED()); 3097c478bd9Sstevel@tonic-gate if (err == 0) { 3107c478bd9Sstevel@tonic-gate rw_enter(&dp->i_rwlock, RW_WRITER); 3117c478bd9Sstevel@tonic-gate rw_enter(&dp->i_contents, RW_WRITER); 3127c478bd9Sstevel@tonic-gate dp->i_flag |= IUPD|ICHG; 3137c478bd9Sstevel@tonic-gate dp->i_seq++; 3147c478bd9Sstevel@tonic-gate TRANS_INODE(dp->i_ufsvfs, dp); 3157c478bd9Sstevel@tonic-gate dp->i_nlink -= 2; 3167c478bd9Sstevel@tonic-gate ufs_setreclaim(dp); 3177c478bd9Sstevel@tonic-gate /* 3187c478bd9Sstevel@tonic-gate * Should get rid of any negative cache entries that 3197c478bd9Sstevel@tonic-gate * might be lingering, as well as ``.'' and 3207c478bd9Sstevel@tonic-gate * ``..''. If we don't, the VN_RELE() below 3217c478bd9Sstevel@tonic-gate * won't actually put dp on the delete queue 3227c478bd9Sstevel@tonic-gate * and it'll hang out until someone forces it 3237c478bd9Sstevel@tonic-gate * (lockfs -f, umount, ...). The only reliable 3247c478bd9Sstevel@tonic-gate * way of doing this at the moment is to call 3257c478bd9Sstevel@tonic-gate * dnlc_purge_vp(ITOV(dp)), which is unacceptably 3267c478bd9Sstevel@tonic-gate * slow, so we'll just note the problem in this 3277c478bd9Sstevel@tonic-gate * comment for now. 3287c478bd9Sstevel@tonic-gate */ 3297c478bd9Sstevel@tonic-gate dnlc_remove(ITOV(dp), "."); 3307c478bd9Sstevel@tonic-gate dnlc_remove(ITOV(dp), ".."); 3317c478bd9Sstevel@tonic-gate ITIMES_NOLOCK(dp); 3327c478bd9Sstevel@tonic-gate if (!TRANS_ISTRANS(ufsvfsp)) { 3337c478bd9Sstevel@tonic-gate ufs_iupdat(dp, I_SYNC); 3347c478bd9Sstevel@tonic-gate } 3357c478bd9Sstevel@tonic-gate rw_exit(&dp->i_contents); 3367c478bd9Sstevel@tonic-gate rw_exit(&dp->i_rwlock); 3377c478bd9Sstevel@tonic-gate VN_RELE(ITOV(dp)); 3387c478bd9Sstevel@tonic-gate } 3397c478bd9Sstevel@tonic-gate /* 3407c478bd9Sstevel@tonic-gate * Clear out attribute pointer 3417c478bd9Sstevel@tonic-gate */ 3427c478bd9Sstevel@tonic-gate ip->i_oeftflag = 0; 3437c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 3447c478bd9Sstevel@tonic-gate TRANS_END_CSYNC(ufsvfsp, err, issync, 3457c478bd9Sstevel@tonic-gate TOP_REMOVE, trans_size); 3467c478bd9Sstevel@tonic-gate dnlc_remove(ITOV(ip), XATTR_DIR_NAME); 3477c478bd9Sstevel@tonic-gate } 3487c478bd9Sstevel@tonic-gate 3497c478bd9Sstevel@tonic-gate if ((ip->i_mode & IFMT) == IFATTRDIR) { 3507c478bd9Sstevel@tonic-gate ufs_attr_purge(ip); 3517c478bd9Sstevel@tonic-gate } 3527c478bd9Sstevel@tonic-gate 353121be23bSjkennedy (void) TRANS_ITRUNC(ip, (u_offset_t)0, I_FREE | I_ACCT, CRED()); 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate /* 3567c478bd9Sstevel@tonic-gate * the inode's space has been freed; now free the inode 3577c478bd9Sstevel@tonic-gate */ 3587c478bd9Sstevel@tonic-gate if (ulp) { 3597c478bd9Sstevel@tonic-gate trans_size = TOP_IFREE_SIZE(ip); 3607c478bd9Sstevel@tonic-gate TRANS_BEGIN_ASYNC(ufsvfsp, TOP_IFREE, trans_size); 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate rw_enter(&ufsvfsp->vfs_dqrwlock, RW_READER); 3637c478bd9Sstevel@tonic-gate rw_enter(&ip->i_contents, RW_WRITER); 3647c478bd9Sstevel@tonic-gate TRANS_INODE(ufsvfsp, ip); 3657c478bd9Sstevel@tonic-gate mode = ip->i_mode; 3667c478bd9Sstevel@tonic-gate ip->i_mode = 0; 3677c478bd9Sstevel@tonic-gate ip->i_rdev = 0; 3687c478bd9Sstevel@tonic-gate ip->i_ordev = 0; 3697c478bd9Sstevel@tonic-gate ip->i_flag |= IMOD; 3707c478bd9Sstevel@tonic-gate if (ip->i_ufs_acl) { 3717c478bd9Sstevel@tonic-gate (void) ufs_si_free(ip->i_ufs_acl, vp->v_vfsp, CRED()); 3727c478bd9Sstevel@tonic-gate ip->i_ufs_acl = NULL; 3737c478bd9Sstevel@tonic-gate ip->i_shadow = 0; 3747c478bd9Sstevel@tonic-gate } 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate /* 3777c478bd9Sstevel@tonic-gate * This inode is torn down but still retains it's identity 3787c478bd9Sstevel@tonic-gate * (inode number). It could get recycled soon so it's best 3797c478bd9Sstevel@tonic-gate * to clean up the vnode just in case. 3807c478bd9Sstevel@tonic-gate */ 3817c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 3827c478bd9Sstevel@tonic-gate vn_recycle(vp); 3837c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 3847c478bd9Sstevel@tonic-gate 3857c478bd9Sstevel@tonic-gate /* 3867c478bd9Sstevel@tonic-gate * free the inode 3877c478bd9Sstevel@tonic-gate */ 3887c478bd9Sstevel@tonic-gate ufs_ifree(ip, ip->i_number, mode); 3897c478bd9Sstevel@tonic-gate /* 3907c478bd9Sstevel@tonic-gate * release quota resources; can't fail 3917c478bd9Sstevel@tonic-gate */ 3927c478bd9Sstevel@tonic-gate (void) chkiq((struct ufsvfs *)vp->v_vfsp->vfs_data, 3937c478bd9Sstevel@tonic-gate /* change */ -1, ip, (uid_t)ip->i_uid, 0, CRED(), 3947c478bd9Sstevel@tonic-gate (char **)NULL, (size_t *)NULL); 3957c478bd9Sstevel@tonic-gate dqrele(ip->i_dquot); 3967c478bd9Sstevel@tonic-gate ip->i_dquot = NULL; 3977c478bd9Sstevel@tonic-gate ip->i_flag &= ~(IDEL | IDIRECTIO); 3987c478bd9Sstevel@tonic-gate ip->i_cflags = 0; 3997c478bd9Sstevel@tonic-gate if (!TRANS_ISTRANS(ufsvfsp)) { 4007c478bd9Sstevel@tonic-gate ufs_iupdat(ip, I_SYNC); 401121be23bSjkennedy } else { 402121be23bSjkennedy mutex_enter(&delq->uq_mutex); 403121be23bSjkennedy delq_info->delq_unreclaimed_files--; 404121be23bSjkennedy mutex_exit(&delq->uq_mutex); 4057c478bd9Sstevel@tonic-gate } 4067c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 4077c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 4087c478bd9Sstevel@tonic-gate if (dorwlock) 4097c478bd9Sstevel@tonic-gate rw_exit(&ip->i_rwlock); 4107c478bd9Sstevel@tonic-gate VN_RELE(vp); 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate /* 4137c478bd9Sstevel@tonic-gate * End of transaction 4147c478bd9Sstevel@tonic-gate */ 4157c478bd9Sstevel@tonic-gate if (ulp) { 4167c478bd9Sstevel@tonic-gate TRANS_END_ASYNC(ufsvfsp, TOP_IFREE, trans_size); 4177c478bd9Sstevel@tonic-gate if (dolockfs) 4187c478bd9Sstevel@tonic-gate ufs_lockfs_end(ulp); 4197c478bd9Sstevel@tonic-gate else 4207c478bd9Sstevel@tonic-gate curthread->t_flag &= ~T_DONTBLOCK; 4217c478bd9Sstevel@tonic-gate } 4227c478bd9Sstevel@tonic-gate } 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate /* 425121be23bSjkennedy * Create the delete thread and init the delq_info for this fs 426121be23bSjkennedy */ 427121be23bSjkennedy void 428121be23bSjkennedy ufs_delete_init(struct ufsvfs *ufsvfsp, int lowat) 429121be23bSjkennedy { 430121be23bSjkennedy struct ufs_delq_info *delq_info = &ufsvfsp->vfs_delete_info; 431121be23bSjkennedy 432121be23bSjkennedy ufs_thread_init(&ufsvfsp->vfs_delete, lowat); 433a135774bSjkennedy (void) memset((void *)delq_info, 0, sizeof (*delq_info)); 434121be23bSjkennedy } 435121be23bSjkennedy 436121be23bSjkennedy /* 4377c478bd9Sstevel@tonic-gate * thread that frees up deleted inodes 4387c478bd9Sstevel@tonic-gate */ 4397c478bd9Sstevel@tonic-gate void 4407c478bd9Sstevel@tonic-gate ufs_thread_delete(struct vfs *vfsp) 4417c478bd9Sstevel@tonic-gate { 4427c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp = (struct ufsvfs *)vfsp->vfs_data; 4437c478bd9Sstevel@tonic-gate struct ufs_q *uq = &ufsvfsp->vfs_delete; 4447c478bd9Sstevel@tonic-gate struct inode *ip; 4457c478bd9Sstevel@tonic-gate long ne; 4467c478bd9Sstevel@tonic-gate callb_cpr_t cprinfo; 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &uq->uq_mutex, callb_generic_cpr, 4497c478bd9Sstevel@tonic-gate "ufsdelete"); 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 4527c478bd9Sstevel@tonic-gate again: 4537c478bd9Sstevel@tonic-gate /* 454121be23bSjkennedy * Sleep until there is work to do. Only do one entry at 455121be23bSjkennedy * a time, to reduce the wait time for checking for a suspend 456709bb9d7Svk154806 * request. The ?: is for pedantic portability. 4577c478bd9Sstevel@tonic-gate */ 458121be23bSjkennedy ne = ufs_thread_run(uq, &cprinfo) ? 1 : 0; 459121be23bSjkennedy 4607c478bd9Sstevel@tonic-gate /* 461121be23bSjkennedy * process an entry, if there are any 4627c478bd9Sstevel@tonic-gate */ 463121be23bSjkennedy if (ne && (ip = uq->uq_ihead)) { 4647c478bd9Sstevel@tonic-gate /* 4657c478bd9Sstevel@tonic-gate * process first entry on queue. Assumed conditions are: 4667c478bd9Sstevel@tonic-gate * ip is held (v_count >= 1) 4677c478bd9Sstevel@tonic-gate * ip is referenced (i_flag & IREF) 4687c478bd9Sstevel@tonic-gate * ip is free (i_nlink <= 0) 4697c478bd9Sstevel@tonic-gate */ 4707c478bd9Sstevel@tonic-gate if ((uq->uq_ihead = ip->i_freef) == ip) 4717c478bd9Sstevel@tonic-gate uq->uq_ihead = NULL; 4727c478bd9Sstevel@tonic-gate ip->i_freef->i_freeb = ip->i_freeb; 4737c478bd9Sstevel@tonic-gate ip->i_freeb->i_freef = ip->i_freef; 4747c478bd9Sstevel@tonic-gate ip->i_freef = ip; 4757c478bd9Sstevel@tonic-gate ip->i_freeb = ip; 4767c478bd9Sstevel@tonic-gate uq->uq_ne--; 4777c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 4787c478bd9Sstevel@tonic-gate ufs_delete(ufsvfsp, ip, 1); 4797c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 4807c478bd9Sstevel@tonic-gate } 4817c478bd9Sstevel@tonic-gate goto again; 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate 4847c478bd9Sstevel@tonic-gate /* 4857c478bd9Sstevel@tonic-gate * drain ne entries off the delete queue. As new queue entries may 4867c478bd9Sstevel@tonic-gate * be added while we're working, ne is interpreted as follows: 4877c478bd9Sstevel@tonic-gate * 4887c478bd9Sstevel@tonic-gate * ne > 0 => remove up to ne entries 4897c478bd9Sstevel@tonic-gate * ne == 0 => remove all entries currently on the queue 4907c478bd9Sstevel@tonic-gate * ne == -1 => remove entries until the queue is empty 4917c478bd9Sstevel@tonic-gate */ 4927c478bd9Sstevel@tonic-gate void 4937c478bd9Sstevel@tonic-gate ufs_delete_drain(struct vfs *vfsp, int ne, int dolockfs) 4947c478bd9Sstevel@tonic-gate { 4957c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp = (struct ufsvfs *)vfsp->vfs_data; 4967c478bd9Sstevel@tonic-gate struct ufs_q *uq; 4977c478bd9Sstevel@tonic-gate struct inode *ip; 4987c478bd9Sstevel@tonic-gate int drain_cnt = 0; 4997c478bd9Sstevel@tonic-gate int done; 5007c478bd9Sstevel@tonic-gate 5017c478bd9Sstevel@tonic-gate /* 5027c478bd9Sstevel@tonic-gate * if forcibly unmounted; ignore 5037c478bd9Sstevel@tonic-gate */ 5047c478bd9Sstevel@tonic-gate if (ufsvfsp == NULL) 5057c478bd9Sstevel@tonic-gate return; 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate uq = &ufsvfsp->vfs_delete; 5087c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 5097c478bd9Sstevel@tonic-gate if (ne == 0) 5107c478bd9Sstevel@tonic-gate drain_cnt = uq->uq_ne; 5117c478bd9Sstevel@tonic-gate else if (ne > 0) 5127c478bd9Sstevel@tonic-gate drain_cnt = ne; 5137c478bd9Sstevel@tonic-gate 5147c478bd9Sstevel@tonic-gate /* 5157c478bd9Sstevel@tonic-gate * process up to ne entries 5167c478bd9Sstevel@tonic-gate */ 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate done = 0; 5197c478bd9Sstevel@tonic-gate while (!done && (ip = uq->uq_ihead)) { 5207c478bd9Sstevel@tonic-gate if (ne != -1) 5217c478bd9Sstevel@tonic-gate drain_cnt--; 5227c478bd9Sstevel@tonic-gate if (ne != -1 && drain_cnt == 0) 5237c478bd9Sstevel@tonic-gate done = 1; 5247c478bd9Sstevel@tonic-gate if ((uq->uq_ihead = ip->i_freef) == ip) 5257c478bd9Sstevel@tonic-gate uq->uq_ihead = NULL; 5267c478bd9Sstevel@tonic-gate ip->i_freef->i_freeb = ip->i_freeb; 5277c478bd9Sstevel@tonic-gate ip->i_freeb->i_freef = ip->i_freef; 5287c478bd9Sstevel@tonic-gate ip->i_freef = ip; 5297c478bd9Sstevel@tonic-gate ip->i_freeb = ip; 5307c478bd9Sstevel@tonic-gate uq->uq_ne--; 5317c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 5327c478bd9Sstevel@tonic-gate ufs_delete(ufsvfsp, ip, dolockfs); 5337c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 5347c478bd9Sstevel@tonic-gate } 5357c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 5367c478bd9Sstevel@tonic-gate } 5377c478bd9Sstevel@tonic-gate 5387c478bd9Sstevel@tonic-gate void 5397c478bd9Sstevel@tonic-gate ufs_sync_with_thread(struct ufs_q *uq) 5407c478bd9Sstevel@tonic-gate { 5417c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 5428db829a5Smishra 5437c478bd9Sstevel@tonic-gate /* 5448db829a5Smishra * Wake up delete thread to free up space. 5457c478bd9Sstevel@tonic-gate */ 5468db829a5Smishra if ((uq->uq_flags & UQ_WAIT) == 0) { 5478db829a5Smishra uq->uq_flags |= UQ_WAIT; 5487c478bd9Sstevel@tonic-gate cv_broadcast(&uq->uq_cv); 5498db829a5Smishra } 5508db829a5Smishra 5518db829a5Smishra while ((uq->uq_threadp != NULL) && (uq->uq_flags & UQ_WAIT)) { 5527c478bd9Sstevel@tonic-gate cv_wait(&uq->uq_cv, &uq->uq_mutex); 5537c478bd9Sstevel@tonic-gate } 5548db829a5Smishra 5557c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 5567c478bd9Sstevel@tonic-gate } 5577c478bd9Sstevel@tonic-gate 5587c478bd9Sstevel@tonic-gate /* 5597c478bd9Sstevel@tonic-gate * Get rid of everything that's currently in the delete queue, 5607c478bd9Sstevel@tonic-gate * plus whatever the delete thread is working on at the moment. 5617c478bd9Sstevel@tonic-gate * 5627c478bd9Sstevel@tonic-gate * This ability is required for providing true POSIX semantics 5637c478bd9Sstevel@tonic-gate * regarding close(2), unlink(2), etc, even when logging is enabled. 5647c478bd9Sstevel@tonic-gate * The standard requires that the released space be immediately 5657c478bd9Sstevel@tonic-gate * observable (statvfs(2)) and allocatable (e.g., write(2)). 5667c478bd9Sstevel@tonic-gate */ 5677c478bd9Sstevel@tonic-gate void 5687c478bd9Sstevel@tonic-gate ufs_delete_drain_wait(struct ufsvfs *ufsvfsp, int dolockfs) 5697c478bd9Sstevel@tonic-gate { 5707c478bd9Sstevel@tonic-gate struct ufs_q *uq = &ufsvfsp->vfs_delete; 5717c478bd9Sstevel@tonic-gate int error; 5728db829a5Smishra struct ufs_q *delq = &ufsvfsp->vfs_delete; 5738db829a5Smishra struct ufs_delq_info *delq_info = &ufsvfsp->vfs_delete_info; 5747c478bd9Sstevel@tonic-gate 5758db829a5Smishra /* 5768db829a5Smishra * If there is something on delq or delete thread 5778db829a5Smishra * working on delq. 5788db829a5Smishra */ 5798db829a5Smishra mutex_enter(&delq->uq_mutex); 5808db829a5Smishra if (delq_info->delq_unreclaimed_files > 0) { 5818db829a5Smishra mutex_exit(&delq->uq_mutex); 5827c478bd9Sstevel@tonic-gate (void) ufs_delete_drain(ufsvfsp->vfs_vfs, 0, dolockfs); 5837c478bd9Sstevel@tonic-gate ufs_sync_with_thread(uq); 5848db829a5Smishra } else { 5858db829a5Smishra ASSERT(delq_info->delq_unreclaimed_files == 0); 5868db829a5Smishra mutex_exit(&delq->uq_mutex); 5878db829a5Smishra return; 5888db829a5Smishra } 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate /* 5917c478bd9Sstevel@tonic-gate * Commit any outstanding transactions to make sure 5927c478bd9Sstevel@tonic-gate * any canceled freed blocks are available for allocation. 5937c478bd9Sstevel@tonic-gate */ 5947c478bd9Sstevel@tonic-gate curthread->t_flag |= T_DONTBLOCK; 5957c478bd9Sstevel@tonic-gate TRANS_BEGIN_SYNC(ufsvfsp, TOP_COMMIT_UPDATE, TOP_COMMIT_SIZE, error); 5967c478bd9Sstevel@tonic-gate if (!error) { 5977c478bd9Sstevel@tonic-gate TRANS_END_SYNC(ufsvfsp, error, TOP_COMMIT_UPDATE, 5987c478bd9Sstevel@tonic-gate TOP_COMMIT_SIZE); 5997c478bd9Sstevel@tonic-gate } 6007c478bd9Sstevel@tonic-gate curthread->t_flag &= ~T_DONTBLOCK; 6017c478bd9Sstevel@tonic-gate } 6027c478bd9Sstevel@tonic-gate 6037c478bd9Sstevel@tonic-gate /* 6047c478bd9Sstevel@tonic-gate * Adjust the resource usage in a struct statvfs based on 605121be23bSjkennedy * what's in the delete queue. 6067c478bd9Sstevel@tonic-gate * 6077c478bd9Sstevel@tonic-gate * We do not consider the impact of ACLs or extended attributes 6087c478bd9Sstevel@tonic-gate * that may be deleted as a side-effect of deleting a file. 6097c478bd9Sstevel@tonic-gate * Those are metadata, and their sizes aren't reflected in the 6107c478bd9Sstevel@tonic-gate * sizes returned by stat(), so this is not a problem. 6117c478bd9Sstevel@tonic-gate */ 6127c478bd9Sstevel@tonic-gate void 6137c478bd9Sstevel@tonic-gate ufs_delete_adjust_stats(struct ufsvfs *ufsvfsp, struct statvfs64 *sp) 6147c478bd9Sstevel@tonic-gate { 6157c478bd9Sstevel@tonic-gate struct ufs_q *uq = &ufsvfsp->vfs_delete; 616121be23bSjkennedy struct ufs_delq_info *delq_info = &ufsvfsp->vfs_delete_info; 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 61920a1ae8aSjkennedy /* 62020a1ae8aSjkennedy * The blocks accounted for in the delete queue info are 62120a1ae8aSjkennedy * counted in DEV_BSIZE chunks, but ufs_statvfs counts in 62220a1ae8aSjkennedy * filesystem fragments, so a conversion is required here. 62320a1ae8aSjkennedy */ 62420a1ae8aSjkennedy sp->f_bfree += dbtofsb(ufsvfsp->vfs_fs, 62520a1ae8aSjkennedy delq_info->delq_unreclaimed_blocks); 626121be23bSjkennedy sp->f_ffree += delq_info->delq_unreclaimed_files; 6277c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 6287c478bd9Sstevel@tonic-gate } 6297c478bd9Sstevel@tonic-gate 6307c478bd9Sstevel@tonic-gate /* 6317c478bd9Sstevel@tonic-gate * IDLE INODE 6327c478bd9Sstevel@tonic-gate * The following routines implement the protocol for maintaining an 6337c478bd9Sstevel@tonic-gate * LRU list of idle inodes and for moving the idle inodes to the 6347c478bd9Sstevel@tonic-gate * reuse list when the number of allocated inodes exceeds the user 6357c478bd9Sstevel@tonic-gate * tunable high-water mark (ufs_ninode). 6367c478bd9Sstevel@tonic-gate */ 6377c478bd9Sstevel@tonic-gate 6387c478bd9Sstevel@tonic-gate /* 6397c478bd9Sstevel@tonic-gate * clean an idle inode and move it to the reuse list 6407c478bd9Sstevel@tonic-gate */ 6417c478bd9Sstevel@tonic-gate static void 6427c478bd9Sstevel@tonic-gate ufs_idle_free(struct inode *ip) 6437c478bd9Sstevel@tonic-gate { 6447c478bd9Sstevel@tonic-gate int pages; 6457c478bd9Sstevel@tonic-gate int hno; 6467c478bd9Sstevel@tonic-gate kmutex_t *ihm; 6477c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp = ip->i_ufsvfs; 6487c478bd9Sstevel@tonic-gate struct vnode *vp = ITOV(ip); 649b7deb15bSWolfgang Schremser int vn_has_data, vn_modified; 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate /* 6527c478bd9Sstevel@tonic-gate * inode is held 6537c478bd9Sstevel@tonic-gate */ 6547c478bd9Sstevel@tonic-gate 6557c478bd9Sstevel@tonic-gate /* 6567c478bd9Sstevel@tonic-gate * remember `pages' for stats below 6577c478bd9Sstevel@tonic-gate */ 6587c478bd9Sstevel@tonic-gate pages = (ip->i_mode && vn_has_cached_data(vp) && vp->v_type != VCHR); 6597c478bd9Sstevel@tonic-gate 6607c478bd9Sstevel@tonic-gate /* 6617c478bd9Sstevel@tonic-gate * start the dirty pages to disk and then invalidate them 6627c478bd9Sstevel@tonic-gate * unless the inode is invalid (ISTALE) 6637c478bd9Sstevel@tonic-gate */ 6647c478bd9Sstevel@tonic-gate if ((ip->i_flag & ISTALE) == 0) { 6657c478bd9Sstevel@tonic-gate (void) TRANS_SYNCIP(ip, B_ASYNC, I_ASYNC, TOP_SYNCIP_FREE); 6667c478bd9Sstevel@tonic-gate (void) TRANS_SYNCIP(ip, 66780d34432Sfrankho (TRANS_ISERROR(ufsvfsp)) ? B_INVAL | B_FORCE : B_INVAL, 6687c478bd9Sstevel@tonic-gate I_ASYNC, TOP_SYNCIP_FREE); 6697c478bd9Sstevel@tonic-gate } 6707c478bd9Sstevel@tonic-gate 6717c478bd9Sstevel@tonic-gate /* 6727c478bd9Sstevel@tonic-gate * wait for any current ufs_iget to finish and block future ufs_igets 6737c478bd9Sstevel@tonic-gate */ 6747c478bd9Sstevel@tonic-gate ASSERT(ip->i_number != 0); 6757c478bd9Sstevel@tonic-gate hno = INOHASH(ip->i_number); 6767c478bd9Sstevel@tonic-gate ihm = &ih_lock[hno]; 6777c478bd9Sstevel@tonic-gate mutex_enter(ihm); 6787c478bd9Sstevel@tonic-gate 6797c478bd9Sstevel@tonic-gate /* 6807c478bd9Sstevel@tonic-gate * It must be guaranteed that v_count >= 2, otherwise 6817c478bd9Sstevel@tonic-gate * something must be wrong with this vnode already. 6827c478bd9Sstevel@tonic-gate * That is why we use v_count-- instead of VN_RELE(). 6837c478bd9Sstevel@tonic-gate * Acquire the vnode lock in case another thread is in 6847c478bd9Sstevel@tonic-gate * VN_RELE(). 6857c478bd9Sstevel@tonic-gate */ 6867c478bd9Sstevel@tonic-gate mutex_enter(&vp->v_lock); 6877c478bd9Sstevel@tonic-gate 6887c478bd9Sstevel@tonic-gate if (vp->v_count < 2) 6897c478bd9Sstevel@tonic-gate cmn_err(CE_PANIC, 6907c478bd9Sstevel@tonic-gate "ufs_idle_free: vnode ref count is less than 2"); 6917c478bd9Sstevel@tonic-gate 6927c478bd9Sstevel@tonic-gate vp->v_count--; 693b7deb15bSWolfgang Schremser 694b7deb15bSWolfgang Schremser vn_has_data = (vp->v_type != VCHR && vn_has_cached_data(vp)); 695b7deb15bSWolfgang Schremser vn_modified = (ip->i_flag & (IMOD|IMODACC|IACC|ICHG|IUPD|IATTCHG)); 696b7deb15bSWolfgang Schremser 697b7deb15bSWolfgang Schremser if (vp->v_count != 1 || 698b7deb15bSWolfgang Schremser ((vn_has_data || vn_modified) && 699b7deb15bSWolfgang Schremser ((ip->i_flag & ISTALE) == 0))) { 7007c478bd9Sstevel@tonic-gate /* 7017c478bd9Sstevel@tonic-gate * Another thread has referenced this inode while 7027c478bd9Sstevel@tonic-gate * we are trying to free it. Call VN_RELE() to 703b7deb15bSWolfgang Schremser * release our reference, if v_count > 1 data is 704b7deb15bSWolfgang Schremser * present or one of the modified etc. flags was 705b7deb15bSWolfgang Schremser * set, whereby ISTALE wasn't set. 706b7deb15bSWolfgang Schremser * If we'd proceed with ISTALE set here, we might 707b7deb15bSWolfgang Schremser * get ourselves into a deadlock situation. 7087c478bd9Sstevel@tonic-gate */ 7097c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 7107c478bd9Sstevel@tonic-gate mutex_exit(ihm); 7117c478bd9Sstevel@tonic-gate VN_RELE(vp); 7127c478bd9Sstevel@tonic-gate } else { 7137c478bd9Sstevel@tonic-gate /* 7147c478bd9Sstevel@tonic-gate * The inode is currently unreferenced and can not 7157c478bd9Sstevel@tonic-gate * acquire further references because it has no pages 7167c478bd9Sstevel@tonic-gate * and the hash is locked. Inodes acquire references 7177c478bd9Sstevel@tonic-gate * via the hash list or via their pages. 7187c478bd9Sstevel@tonic-gate */ 7197c478bd9Sstevel@tonic-gate 7207c478bd9Sstevel@tonic-gate mutex_exit(&vp->v_lock); 7217c478bd9Sstevel@tonic-gate 7227c478bd9Sstevel@tonic-gate /* 7237c478bd9Sstevel@tonic-gate * remove it from the cache 7247c478bd9Sstevel@tonic-gate */ 7257c478bd9Sstevel@tonic-gate remque(ip); 7267c478bd9Sstevel@tonic-gate mutex_exit(ihm); 7277c478bd9Sstevel@tonic-gate /* 7287c478bd9Sstevel@tonic-gate * Stale inodes have no valid ufsvfs 7297c478bd9Sstevel@tonic-gate */ 7307c478bd9Sstevel@tonic-gate if ((ip->i_flag & ISTALE) == 0 && ip->i_dquot) { 7317c478bd9Sstevel@tonic-gate TRANS_DQRELE(ufsvfsp, ip->i_dquot); 7327c478bd9Sstevel@tonic-gate ip->i_dquot = NULL; 7337c478bd9Sstevel@tonic-gate } 734b7deb15bSWolfgang Schremser if ((ip->i_flag & ISTALE) && 735b7deb15bSWolfgang Schremser vn_has_data) { 736b7deb15bSWolfgang Schremser /* 737b7deb15bSWolfgang Schremser * ISTALE inodes may have data 738b7deb15bSWolfgang Schremser * and this data needs to be 739b7deb15bSWolfgang Schremser * cleaned up. 740b7deb15bSWolfgang Schremser */ 741b7deb15bSWolfgang Schremser (void) pvn_vplist_dirty(vp, (u_offset_t)0, 742b7deb15bSWolfgang Schremser ufs_putapage, B_INVAL | B_TRUNC, 743b7deb15bSWolfgang Schremser (struct cred *)NULL); 744b7deb15bSWolfgang Schremser } 7457c478bd9Sstevel@tonic-gate ufs_si_del(ip); 7467c478bd9Sstevel@tonic-gate if (pages) { 7477c478bd9Sstevel@tonic-gate CPU_STATS_ADDQ(CPU, sys, ufsipage, 1); 7487c478bd9Sstevel@tonic-gate } else { 7497c478bd9Sstevel@tonic-gate CPU_STATS_ADDQ(CPU, sys, ufsinopage, 1); 7507c478bd9Sstevel@tonic-gate } 7517c478bd9Sstevel@tonic-gate ASSERT((vp->v_type == VCHR) || !vn_has_cached_data(vp)); 7525bde59abSbatschul 7535bde59abSbatschul /* 7545bde59abSbatschul * We had better not have a vnode reference count > 1 7555bde59abSbatschul * at this point, if we do then something is broken as 7565bde59abSbatschul * this inode/vnode acquired a reference underneath of us. 7575bde59abSbatschul */ 7585bde59abSbatschul ASSERT(vp->v_count == 1); 7595bde59abSbatschul 7607c478bd9Sstevel@tonic-gate ufs_free_inode(ip); 7617c478bd9Sstevel@tonic-gate } 7627c478bd9Sstevel@tonic-gate } 7637c478bd9Sstevel@tonic-gate 7647c478bd9Sstevel@tonic-gate /* 7657c478bd9Sstevel@tonic-gate * this thread processes the global idle queue 7667c478bd9Sstevel@tonic-gate */ 7677c478bd9Sstevel@tonic-gate iqhead_t *ufs_junk_iq; 7687c478bd9Sstevel@tonic-gate iqhead_t *ufs_useful_iq; 7697c478bd9Sstevel@tonic-gate int ufs_njunk_iq = 0; 7707c478bd9Sstevel@tonic-gate int ufs_nuseful_iq = 0; 7717c478bd9Sstevel@tonic-gate int ufs_niqhash; 7727c478bd9Sstevel@tonic-gate int ufs_iqhashmask; 7737c478bd9Sstevel@tonic-gate struct ufs_q ufs_idle_q; 7747c478bd9Sstevel@tonic-gate 7757c478bd9Sstevel@tonic-gate void 7767c478bd9Sstevel@tonic-gate ufs_thread_idle(void) 7777c478bd9Sstevel@tonic-gate { 7787c478bd9Sstevel@tonic-gate callb_cpr_t cprinfo; 7797c478bd9Sstevel@tonic-gate int i; 7807c478bd9Sstevel@tonic-gate int ne; 7817c478bd9Sstevel@tonic-gate 7827c478bd9Sstevel@tonic-gate ufs_niqhash = (ufs_idle_q.uq_lowat >> 1) / IQHASHQLEN; 7837c478bd9Sstevel@tonic-gate ufs_niqhash = 1 << highbit(ufs_niqhash); /* round up to power of 2 */ 7847c478bd9Sstevel@tonic-gate ufs_iqhashmask = ufs_niqhash - 1; 7857c478bd9Sstevel@tonic-gate ufs_junk_iq = kmem_alloc(ufs_niqhash * sizeof (*ufs_junk_iq), 7867c478bd9Sstevel@tonic-gate KM_SLEEP); 7877c478bd9Sstevel@tonic-gate ufs_useful_iq = kmem_alloc(ufs_niqhash * sizeof (*ufs_useful_iq), 7887c478bd9Sstevel@tonic-gate KM_SLEEP); 7897c478bd9Sstevel@tonic-gate 7907c478bd9Sstevel@tonic-gate /* Initialize hash queue headers */ 7917c478bd9Sstevel@tonic-gate for (i = 0; i < ufs_niqhash; i++) { 7927c478bd9Sstevel@tonic-gate ufs_junk_iq[i].i_freef = (inode_t *)&ufs_junk_iq[i]; 7937c478bd9Sstevel@tonic-gate ufs_junk_iq[i].i_freeb = (inode_t *)&ufs_junk_iq[i]; 7947c478bd9Sstevel@tonic-gate ufs_useful_iq[i].i_freef = (inode_t *)&ufs_useful_iq[i]; 7957c478bd9Sstevel@tonic-gate ufs_useful_iq[i].i_freeb = (inode_t *)&ufs_useful_iq[i]; 7967c478bd9Sstevel@tonic-gate } 7977c478bd9Sstevel@tonic-gate 7987c478bd9Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &ufs_idle_q.uq_mutex, callb_generic_cpr, 7997c478bd9Sstevel@tonic-gate "ufsidle"); 8007c478bd9Sstevel@tonic-gate again: 8017c478bd9Sstevel@tonic-gate /* 8027c478bd9Sstevel@tonic-gate * Whenever the idle thread is awakened, it repeatedly gives 8037c478bd9Sstevel@tonic-gate * back half of the idle queue until the idle queue falls 8047c478bd9Sstevel@tonic-gate * below lowat. 8057c478bd9Sstevel@tonic-gate */ 8067c478bd9Sstevel@tonic-gate mutex_enter(&ufs_idle_q.uq_mutex); 8077c478bd9Sstevel@tonic-gate if (ufs_idle_q.uq_ne < ufs_idle_q.uq_lowat) { 8087c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 8097c478bd9Sstevel@tonic-gate cv_wait(&ufs_idle_q.uq_cv, &ufs_idle_q.uq_mutex); 8107c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, &ufs_idle_q.uq_mutex); 8117c478bd9Sstevel@tonic-gate } 8127c478bd9Sstevel@tonic-gate mutex_exit(&ufs_idle_q.uq_mutex); 8137c478bd9Sstevel@tonic-gate 8147c478bd9Sstevel@tonic-gate /* 8157c478bd9Sstevel@tonic-gate * Give back 1/2 of the idle queue 8167c478bd9Sstevel@tonic-gate */ 8177c478bd9Sstevel@tonic-gate ne = ufs_idle_q.uq_ne >> 1; 8187c478bd9Sstevel@tonic-gate ins.in_tidles.value.ul += ne; 8197c478bd9Sstevel@tonic-gate ufs_idle_some(ne); 8207c478bd9Sstevel@tonic-gate goto again; 8217c478bd9Sstevel@tonic-gate } 8227c478bd9Sstevel@tonic-gate 8237c478bd9Sstevel@tonic-gate /* 8247c478bd9Sstevel@tonic-gate * Reclaim callback for ufs inode cache. 8257c478bd9Sstevel@tonic-gate * Invoked by the kernel memory allocator when memory gets tight. 8267c478bd9Sstevel@tonic-gate */ 8277c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 8287c478bd9Sstevel@tonic-gate void 8297c478bd9Sstevel@tonic-gate ufs_inode_cache_reclaim(void *cdrarg) 8307c478bd9Sstevel@tonic-gate { 8317c478bd9Sstevel@tonic-gate /* 8327c478bd9Sstevel@tonic-gate * If we are low on memory and the idle queue is over its 8337c478bd9Sstevel@tonic-gate * halfway mark, then free 50% of the idle q 8347c478bd9Sstevel@tonic-gate * 8357c478bd9Sstevel@tonic-gate * We don't free all of the idle inodes because the inodes 8367c478bd9Sstevel@tonic-gate * for popular NFS files may have been kicked from the dnlc. 8377c478bd9Sstevel@tonic-gate * The inodes for these files will end up on the idle queue 8387c478bd9Sstevel@tonic-gate * after every NFS access. 8397c478bd9Sstevel@tonic-gate * 8407c478bd9Sstevel@tonic-gate * If we repeatedly push them from the idle queue then 8417c478bd9Sstevel@tonic-gate * NFS users may be unhappy as an extra buf cache operation 8427c478bd9Sstevel@tonic-gate * is incurred for every NFS operation to these files. 8437c478bd9Sstevel@tonic-gate * 8447c478bd9Sstevel@tonic-gate * It's not common, but I have seen it happen. 8457c478bd9Sstevel@tonic-gate * 8467c478bd9Sstevel@tonic-gate */ 8477c478bd9Sstevel@tonic-gate if (ufs_idle_q.uq_ne < (ufs_idle_q.uq_lowat >> 1)) 8487c478bd9Sstevel@tonic-gate return; 8497c478bd9Sstevel@tonic-gate mutex_enter(&ufs_idle_q.uq_mutex); 8507c478bd9Sstevel@tonic-gate cv_broadcast(&ufs_idle_q.uq_cv); 8517c478bd9Sstevel@tonic-gate mutex_exit(&ufs_idle_q.uq_mutex); 8527c478bd9Sstevel@tonic-gate } 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate /* 8557c478bd9Sstevel@tonic-gate * Free up some idle inodes 8567c478bd9Sstevel@tonic-gate */ 8577c478bd9Sstevel@tonic-gate void 8587c478bd9Sstevel@tonic-gate ufs_idle_some(int ne) 8597c478bd9Sstevel@tonic-gate { 8607c478bd9Sstevel@tonic-gate int i; 8617c478bd9Sstevel@tonic-gate struct inode *ip; 8627c478bd9Sstevel@tonic-gate struct vnode *vp; 8637c478bd9Sstevel@tonic-gate static int junk_rotor = 0; 8647c478bd9Sstevel@tonic-gate static int useful_rotor = 0; 8657c478bd9Sstevel@tonic-gate 8667c478bd9Sstevel@tonic-gate for (i = 0; i < ne; ++i) { 8677c478bd9Sstevel@tonic-gate mutex_enter(&ufs_idle_q.uq_mutex); 8687c478bd9Sstevel@tonic-gate 8697c478bd9Sstevel@tonic-gate if (ufs_njunk_iq) { 8707c478bd9Sstevel@tonic-gate while (ufs_junk_iq[junk_rotor].i_freef == 8717c478bd9Sstevel@tonic-gate (inode_t *)&ufs_junk_iq[junk_rotor]) { 8727c478bd9Sstevel@tonic-gate junk_rotor = IQNEXT(junk_rotor); 8737c478bd9Sstevel@tonic-gate } 8747c478bd9Sstevel@tonic-gate ip = ufs_junk_iq[junk_rotor].i_freef; 8757c478bd9Sstevel@tonic-gate ASSERT(ip->i_flag & IJUNKIQ); 8767c478bd9Sstevel@tonic-gate } else if (ufs_nuseful_iq) { 8777c478bd9Sstevel@tonic-gate while (ufs_useful_iq[useful_rotor].i_freef == 8787c478bd9Sstevel@tonic-gate (inode_t *)&ufs_useful_iq[useful_rotor]) { 8797c478bd9Sstevel@tonic-gate useful_rotor = IQNEXT(useful_rotor); 8807c478bd9Sstevel@tonic-gate } 8817c478bd9Sstevel@tonic-gate ip = ufs_useful_iq[useful_rotor].i_freef; 8827c478bd9Sstevel@tonic-gate ASSERT(!(ip->i_flag & IJUNKIQ)); 8837c478bd9Sstevel@tonic-gate } else { 8847c478bd9Sstevel@tonic-gate mutex_exit(&ufs_idle_q.uq_mutex); 8857c478bd9Sstevel@tonic-gate return; 8867c478bd9Sstevel@tonic-gate } 8877c478bd9Sstevel@tonic-gate 8887c478bd9Sstevel@tonic-gate /* 8897c478bd9Sstevel@tonic-gate * emulate ufs_iget 8907c478bd9Sstevel@tonic-gate */ 8917c478bd9Sstevel@tonic-gate vp = ITOV(ip); 8927c478bd9Sstevel@tonic-gate VN_HOLD(vp); 8937c478bd9Sstevel@tonic-gate mutex_exit(&ufs_idle_q.uq_mutex); 8947c478bd9Sstevel@tonic-gate rw_enter(&ip->i_contents, RW_WRITER); 8957c478bd9Sstevel@tonic-gate /* 8967c478bd9Sstevel@tonic-gate * VN_RELE should not be called if 8977c478bd9Sstevel@tonic-gate * ufs_rmidle returns true, as it will 8987c478bd9Sstevel@tonic-gate * effectively be done in ufs_idle_free. 8997c478bd9Sstevel@tonic-gate */ 9007c478bd9Sstevel@tonic-gate if (ufs_rmidle(ip)) { 9017c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 9027c478bd9Sstevel@tonic-gate ufs_idle_free(ip); 9037c478bd9Sstevel@tonic-gate } else { 9047c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 9057c478bd9Sstevel@tonic-gate VN_RELE(vp); 9067c478bd9Sstevel@tonic-gate } 9077c478bd9Sstevel@tonic-gate } 9087c478bd9Sstevel@tonic-gate } 9097c478bd9Sstevel@tonic-gate 9107c478bd9Sstevel@tonic-gate /* 9117c478bd9Sstevel@tonic-gate * drain entries for vfsp from the idle queue 9127c478bd9Sstevel@tonic-gate * vfsp == NULL means drain the entire thing 9137c478bd9Sstevel@tonic-gate */ 9147c478bd9Sstevel@tonic-gate void 9157c478bd9Sstevel@tonic-gate ufs_idle_drain(struct vfs *vfsp) 9167c478bd9Sstevel@tonic-gate { 9177c478bd9Sstevel@tonic-gate struct inode *ip, *nip; 9187c478bd9Sstevel@tonic-gate struct inode *ianchor = NULL; 9197c478bd9Sstevel@tonic-gate int i; 9207c478bd9Sstevel@tonic-gate 9217c478bd9Sstevel@tonic-gate mutex_enter(&ufs_idle_q.uq_mutex); 9227c478bd9Sstevel@tonic-gate if (ufs_njunk_iq) { 9237c478bd9Sstevel@tonic-gate /* for each hash q */ 9247c478bd9Sstevel@tonic-gate for (i = 0; i < ufs_niqhash; i++) { 9257c478bd9Sstevel@tonic-gate /* search down the hash q */ 9267c478bd9Sstevel@tonic-gate for (ip = ufs_junk_iq[i].i_freef; 9277c478bd9Sstevel@tonic-gate ip != (inode_t *)&ufs_junk_iq[i]; 9287c478bd9Sstevel@tonic-gate ip = ip->i_freef) { 9297c478bd9Sstevel@tonic-gate if (ip->i_vfs == vfsp || vfsp == NULL) { 9307c478bd9Sstevel@tonic-gate /* found a matching entry */ 9317c478bd9Sstevel@tonic-gate VN_HOLD(ITOV(ip)); 9327c478bd9Sstevel@tonic-gate mutex_exit(&ufs_idle_q.uq_mutex); 9337c478bd9Sstevel@tonic-gate rw_enter(&ip->i_contents, RW_WRITER); 9347c478bd9Sstevel@tonic-gate /* 9357c478bd9Sstevel@tonic-gate * See comments in ufs_idle_some() 9367c478bd9Sstevel@tonic-gate * as we will call ufs_idle_free() 9377c478bd9Sstevel@tonic-gate * after scanning both queues. 9387c478bd9Sstevel@tonic-gate */ 9397c478bd9Sstevel@tonic-gate if (ufs_rmidle(ip)) { 9407c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 9417c478bd9Sstevel@tonic-gate ip->i_freef = ianchor; 9427c478bd9Sstevel@tonic-gate ianchor = ip; 9437c478bd9Sstevel@tonic-gate } else { 9447c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 9457c478bd9Sstevel@tonic-gate VN_RELE(ITOV(ip)); 9467c478bd9Sstevel@tonic-gate } 9477c478bd9Sstevel@tonic-gate /* restart this hash q */ 9487c478bd9Sstevel@tonic-gate ip = (inode_t *)&ufs_junk_iq[i]; 9497c478bd9Sstevel@tonic-gate mutex_enter(&ufs_idle_q.uq_mutex); 9507c478bd9Sstevel@tonic-gate } 9517c478bd9Sstevel@tonic-gate } 9527c478bd9Sstevel@tonic-gate } 9537c478bd9Sstevel@tonic-gate } 9547c478bd9Sstevel@tonic-gate if (ufs_nuseful_iq) { 9557c478bd9Sstevel@tonic-gate /* for each hash q */ 9567c478bd9Sstevel@tonic-gate for (i = 0; i < ufs_niqhash; i++) { 9577c478bd9Sstevel@tonic-gate /* search down the hash q */ 9587c478bd9Sstevel@tonic-gate for (ip = ufs_useful_iq[i].i_freef; 9597c478bd9Sstevel@tonic-gate ip != (inode_t *)&ufs_useful_iq[i]; 9607c478bd9Sstevel@tonic-gate ip = ip->i_freef) { 9617c478bd9Sstevel@tonic-gate if (ip->i_vfs == vfsp || vfsp == NULL) { 9627c478bd9Sstevel@tonic-gate /* found a matching entry */ 9637c478bd9Sstevel@tonic-gate VN_HOLD(ITOV(ip)); 9647c478bd9Sstevel@tonic-gate mutex_exit(&ufs_idle_q.uq_mutex); 9657c478bd9Sstevel@tonic-gate rw_enter(&ip->i_contents, RW_WRITER); 9667c478bd9Sstevel@tonic-gate /* 9677c478bd9Sstevel@tonic-gate * See comments in ufs_idle_some() 9687c478bd9Sstevel@tonic-gate * as we will call ufs_idle_free() 9697c478bd9Sstevel@tonic-gate * after scanning both queues. 9707c478bd9Sstevel@tonic-gate */ 9717c478bd9Sstevel@tonic-gate if (ufs_rmidle(ip)) { 9727c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 9737c478bd9Sstevel@tonic-gate ip->i_freef = ianchor; 9747c478bd9Sstevel@tonic-gate ianchor = ip; 9757c478bd9Sstevel@tonic-gate } else { 9767c478bd9Sstevel@tonic-gate rw_exit(&ip->i_contents); 9777c478bd9Sstevel@tonic-gate VN_RELE(ITOV(ip)); 9787c478bd9Sstevel@tonic-gate } 9797c478bd9Sstevel@tonic-gate /* restart this hash q */ 9807c478bd9Sstevel@tonic-gate ip = (inode_t *)&ufs_useful_iq[i]; 9817c478bd9Sstevel@tonic-gate mutex_enter(&ufs_idle_q.uq_mutex); 9827c478bd9Sstevel@tonic-gate } 9837c478bd9Sstevel@tonic-gate } 9847c478bd9Sstevel@tonic-gate } 9857c478bd9Sstevel@tonic-gate } 9867c478bd9Sstevel@tonic-gate 9877c478bd9Sstevel@tonic-gate mutex_exit(&ufs_idle_q.uq_mutex); 9887c478bd9Sstevel@tonic-gate /* no more matching entries, release those we have found (if any) */ 9897c478bd9Sstevel@tonic-gate for (ip = ianchor; ip; ip = nip) { 9907c478bd9Sstevel@tonic-gate nip = ip->i_freef; 9917c478bd9Sstevel@tonic-gate ip->i_freef = ip; 9927c478bd9Sstevel@tonic-gate ufs_idle_free(ip); 9937c478bd9Sstevel@tonic-gate } 9947c478bd9Sstevel@tonic-gate } 9957c478bd9Sstevel@tonic-gate 9967c478bd9Sstevel@tonic-gate /* 9977c478bd9Sstevel@tonic-gate * RECLAIM DELETED INODES 9987c478bd9Sstevel@tonic-gate * The following thread scans the file system once looking for deleted files 9997c478bd9Sstevel@tonic-gate */ 10007c478bd9Sstevel@tonic-gate void 10017c478bd9Sstevel@tonic-gate ufs_thread_reclaim(struct vfs *vfsp) 10027c478bd9Sstevel@tonic-gate { 10037c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp = (struct ufsvfs *)vfsp->vfs_data; 10047c478bd9Sstevel@tonic-gate struct ufs_q *uq = &ufsvfsp->vfs_reclaim; 10057c478bd9Sstevel@tonic-gate struct fs *fs = ufsvfsp->vfs_fs; 10067c478bd9Sstevel@tonic-gate struct buf *bp = 0; 10077c478bd9Sstevel@tonic-gate int err = 0; 10087c478bd9Sstevel@tonic-gate daddr_t bno; 10097c478bd9Sstevel@tonic-gate ino_t ino; 10107c478bd9Sstevel@tonic-gate struct dinode *dp; 10117c478bd9Sstevel@tonic-gate struct inode *ip; 10127c478bd9Sstevel@tonic-gate callb_cpr_t cprinfo; 10137c478bd9Sstevel@tonic-gate 10147c478bd9Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &uq->uq_mutex, callb_generic_cpr, 10157c478bd9Sstevel@tonic-gate "ufsreclaim"); 10167c478bd9Sstevel@tonic-gate 10177c478bd9Sstevel@tonic-gate /* 10187c478bd9Sstevel@tonic-gate * mount decided that we don't need a reclaim thread 10197c478bd9Sstevel@tonic-gate */ 10207c478bd9Sstevel@tonic-gate if ((fs->fs_reclaim & FS_RECLAIMING) == 0) 10217c478bd9Sstevel@tonic-gate err++; 10227c478bd9Sstevel@tonic-gate 10237c478bd9Sstevel@tonic-gate /* 10247c478bd9Sstevel@tonic-gate * don't reclaim if readonly 10257c478bd9Sstevel@tonic-gate */ 10267c478bd9Sstevel@tonic-gate if (fs->fs_ronly) 10277c478bd9Sstevel@tonic-gate err++; 10287c478bd9Sstevel@tonic-gate 10297c478bd9Sstevel@tonic-gate for (ino = 0; ino < (fs->fs_ncg * fs->fs_ipg) && !err; ++ino) { 10307c478bd9Sstevel@tonic-gate 10317c478bd9Sstevel@tonic-gate /* 10327c478bd9Sstevel@tonic-gate * Check whether we are the target of another 10337c478bd9Sstevel@tonic-gate * thread having called ufs_thread_exit() or 10347c478bd9Sstevel@tonic-gate * ufs_thread_suspend(). 10357c478bd9Sstevel@tonic-gate */ 10367c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 10377c478bd9Sstevel@tonic-gate again: 10387c478bd9Sstevel@tonic-gate if (uq->uq_flags & UQ_EXIT) { 10397c478bd9Sstevel@tonic-gate err++; 10407c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 10417c478bd9Sstevel@tonic-gate break; 10427c478bd9Sstevel@tonic-gate } else if (uq->uq_flags & UQ_SUSPEND) { 10437c478bd9Sstevel@tonic-gate uq->uq_flags |= UQ_SUSPENDED; 10447c478bd9Sstevel@tonic-gate /* 10457c478bd9Sstevel@tonic-gate * Release the buf before we cv_wait() 10467c478bd9Sstevel@tonic-gate * otherwise we may deadlock with the 10477c478bd9Sstevel@tonic-gate * thread that called ufs_thread_suspend(). 10487c478bd9Sstevel@tonic-gate */ 10497c478bd9Sstevel@tonic-gate if (bp) { 10507c478bd9Sstevel@tonic-gate brelse(bp); 10517c478bd9Sstevel@tonic-gate bp = 0; 10527c478bd9Sstevel@tonic-gate } 10537c478bd9Sstevel@tonic-gate if (uq->uq_flags & UQ_WAIT) { 10547c478bd9Sstevel@tonic-gate uq->uq_flags &= ~UQ_WAIT; 10557c478bd9Sstevel@tonic-gate cv_broadcast(&uq->uq_cv); 10567c478bd9Sstevel@tonic-gate } 10577c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 10587c478bd9Sstevel@tonic-gate cv_wait(&uq->uq_cv, &uq->uq_mutex); 10597c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, &uq->uq_mutex); 10607c478bd9Sstevel@tonic-gate goto again; 10617c478bd9Sstevel@tonic-gate } 10627c478bd9Sstevel@tonic-gate mutex_exit(&uq->uq_mutex); 10637c478bd9Sstevel@tonic-gate 10647c478bd9Sstevel@tonic-gate /* 10657c478bd9Sstevel@tonic-gate * if we don't already have the buf; get it 10667c478bd9Sstevel@tonic-gate */ 10677c478bd9Sstevel@tonic-gate bno = fsbtodb(fs, itod(fs, ino)); 10687c478bd9Sstevel@tonic-gate if ((bp == 0) || (bp->b_blkno != bno)) { 10697c478bd9Sstevel@tonic-gate if (bp) 10707c478bd9Sstevel@tonic-gate brelse(bp); 10717c478bd9Sstevel@tonic-gate bp = UFS_BREAD(ufsvfsp, 10727c478bd9Sstevel@tonic-gate ufsvfsp->vfs_dev, bno, fs->fs_bsize); 10737c478bd9Sstevel@tonic-gate bp->b_flags |= B_AGE; 10747c478bd9Sstevel@tonic-gate } 10757c478bd9Sstevel@tonic-gate if (bp->b_flags & B_ERROR) { 10767c478bd9Sstevel@tonic-gate err++; 10777c478bd9Sstevel@tonic-gate continue; 10787c478bd9Sstevel@tonic-gate } 10797c478bd9Sstevel@tonic-gate /* 10807c478bd9Sstevel@tonic-gate * nlink <= 0 and mode != 0 means deleted 10817c478bd9Sstevel@tonic-gate */ 10827c478bd9Sstevel@tonic-gate dp = (struct dinode *)bp->b_un.b_addr + itoo(fs, ino); 10837c478bd9Sstevel@tonic-gate if ((dp->di_nlink <= 0) && (dp->di_mode != 0)) { 10847c478bd9Sstevel@tonic-gate /* 10857c478bd9Sstevel@tonic-gate * can't hold the buf (deadlock) 10867c478bd9Sstevel@tonic-gate */ 10877c478bd9Sstevel@tonic-gate brelse(bp); 10887c478bd9Sstevel@tonic-gate bp = 0; 10897c478bd9Sstevel@tonic-gate rw_enter(&ufsvfsp->vfs_dqrwlock, RW_READER); 10907c478bd9Sstevel@tonic-gate /* 10917c478bd9Sstevel@tonic-gate * iget/iput sequence will put inode on ifree 10927c478bd9Sstevel@tonic-gate * thread queue if it is idle. This is a nop 10937c478bd9Sstevel@tonic-gate * for busy (open, deleted) inodes 10947c478bd9Sstevel@tonic-gate */ 10957c478bd9Sstevel@tonic-gate if (ufs_iget(vfsp, ino, &ip, CRED())) 10967c478bd9Sstevel@tonic-gate err++; 10977c478bd9Sstevel@tonic-gate else 10987c478bd9Sstevel@tonic-gate VN_RELE(ITOV(ip)); 10997c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 11007c478bd9Sstevel@tonic-gate } 11017c478bd9Sstevel@tonic-gate } 11027c478bd9Sstevel@tonic-gate 11037c478bd9Sstevel@tonic-gate if (bp) 11047c478bd9Sstevel@tonic-gate brelse(bp); 11057c478bd9Sstevel@tonic-gate if (!err) { 11067c478bd9Sstevel@tonic-gate /* 11077c478bd9Sstevel@tonic-gate * reset the reclaiming-bit 11087c478bd9Sstevel@tonic-gate */ 11097c478bd9Sstevel@tonic-gate mutex_enter(&ufsvfsp->vfs_lock); 11107c478bd9Sstevel@tonic-gate fs->fs_reclaim &= ~FS_RECLAIMING; 11117c478bd9Sstevel@tonic-gate mutex_exit(&ufsvfsp->vfs_lock); 11127c478bd9Sstevel@tonic-gate TRANS_SBWRITE(ufsvfsp, TOP_SBWRITE_RECLAIM); 11137c478bd9Sstevel@tonic-gate } 11147c478bd9Sstevel@tonic-gate 11157c478bd9Sstevel@tonic-gate /* 11167c478bd9Sstevel@tonic-gate * exit the reclaim thread 11177c478bd9Sstevel@tonic-gate */ 11187c478bd9Sstevel@tonic-gate mutex_enter(&uq->uq_mutex); 11197c478bd9Sstevel@tonic-gate uq->uq_threadp = NULL; 11207c478bd9Sstevel@tonic-gate uq->uq_flags &= ~UQ_WAIT; 11217c478bd9Sstevel@tonic-gate cv_broadcast(&uq->uq_cv); 11227c478bd9Sstevel@tonic-gate CALLB_CPR_EXIT(&cprinfo); 11237c478bd9Sstevel@tonic-gate thread_exit(); 11247c478bd9Sstevel@tonic-gate } 11257c478bd9Sstevel@tonic-gate /* 11267c478bd9Sstevel@tonic-gate * HLOCK FILE SYSTEM 11277c478bd9Sstevel@tonic-gate * hlock the file system's whose logs have device errors 11287c478bd9Sstevel@tonic-gate */ 11297c478bd9Sstevel@tonic-gate struct ufs_q ufs_hlock; 11307c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 11317c478bd9Sstevel@tonic-gate void 11327c478bd9Sstevel@tonic-gate ufs_thread_hlock(void *ignore) 11337c478bd9Sstevel@tonic-gate { 11347c478bd9Sstevel@tonic-gate int retry; 11357c478bd9Sstevel@tonic-gate callb_cpr_t cprinfo; 11367c478bd9Sstevel@tonic-gate 11377c478bd9Sstevel@tonic-gate CALLB_CPR_INIT(&cprinfo, &ufs_hlock.uq_mutex, callb_generic_cpr, 11387c478bd9Sstevel@tonic-gate "ufshlock"); 11397c478bd9Sstevel@tonic-gate 11407c478bd9Sstevel@tonic-gate for (;;) { 11417c478bd9Sstevel@tonic-gate /* 11427c478bd9Sstevel@tonic-gate * sleep until there is work to do 11437c478bd9Sstevel@tonic-gate */ 11447c478bd9Sstevel@tonic-gate mutex_enter(&ufs_hlock.uq_mutex); 11457c478bd9Sstevel@tonic-gate (void) ufs_thread_run(&ufs_hlock, &cprinfo); 11467c478bd9Sstevel@tonic-gate ufs_hlock.uq_ne = 0; 11477c478bd9Sstevel@tonic-gate mutex_exit(&ufs_hlock.uq_mutex); 11487c478bd9Sstevel@tonic-gate /* 11497c478bd9Sstevel@tonic-gate * hlock the error'ed fs's 11507c478bd9Sstevel@tonic-gate * retry after a bit if another app is doing lockfs stuff 11517c478bd9Sstevel@tonic-gate */ 11527c478bd9Sstevel@tonic-gate do { 11537c478bd9Sstevel@tonic-gate retry = ufs_trans_hlock(); 11547c478bd9Sstevel@tonic-gate if (retry) { 11557c478bd9Sstevel@tonic-gate mutex_enter(&ufs_hlock.uq_mutex); 11567c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_BEGIN(&cprinfo); 1157*d3d50737SRafael Vanoni (void) cv_reltimedwait(&ufs_hlock.uq_cv, 1158*d3d50737SRafael Vanoni &ufs_hlock.uq_mutex, hz, TR_CLOCK_TICK); 11597c478bd9Sstevel@tonic-gate CALLB_CPR_SAFE_END(&cprinfo, 11607c478bd9Sstevel@tonic-gate &ufs_hlock.uq_mutex); 11617c478bd9Sstevel@tonic-gate mutex_exit(&ufs_hlock.uq_mutex); 11627c478bd9Sstevel@tonic-gate } 11637c478bd9Sstevel@tonic-gate } while (retry); 11647c478bd9Sstevel@tonic-gate } 11657c478bd9Sstevel@tonic-gate } 11667c478bd9Sstevel@tonic-gate 11677c478bd9Sstevel@tonic-gate static void 11687c478bd9Sstevel@tonic-gate ufs_attr_purge(struct inode *dp) 11697c478bd9Sstevel@tonic-gate { 11707c478bd9Sstevel@tonic-gate int err; 11717c478bd9Sstevel@tonic-gate int error; 11727c478bd9Sstevel@tonic-gate off_t dirsize; /* size of the directory */ 11737c478bd9Sstevel@tonic-gate off_t offset; /* offset in the directory */ 11747c478bd9Sstevel@tonic-gate int entryoffsetinblk; /* offset of ep in fbp's buffer */ 11757c478bd9Sstevel@tonic-gate struct inode *tp; 11767c478bd9Sstevel@tonic-gate struct fbuf *fbp; /* pointer to directory block */ 11777c478bd9Sstevel@tonic-gate struct direct *ep; /* directory entry */ 11787c478bd9Sstevel@tonic-gate int trans_size; 11797c478bd9Sstevel@tonic-gate int issync; 11807c478bd9Sstevel@tonic-gate struct ufsvfs *ufsvfsp = dp->i_ufsvfs; 11817c478bd9Sstevel@tonic-gate 11827c478bd9Sstevel@tonic-gate rw_enter(&ufsvfsp->vfs_dqrwlock, RW_READER); 11837c478bd9Sstevel@tonic-gate 11847c478bd9Sstevel@tonic-gate fbp = NULL; 11857c478bd9Sstevel@tonic-gate dirsize = roundup(dp->i_size, DIRBLKSIZ); 11867c478bd9Sstevel@tonic-gate offset = 0; 11877c478bd9Sstevel@tonic-gate entryoffsetinblk = 0; 11887c478bd9Sstevel@tonic-gate 11897c478bd9Sstevel@tonic-gate /* 11907c478bd9Sstevel@tonic-gate * Purge directory cache 11917c478bd9Sstevel@tonic-gate */ 11927c478bd9Sstevel@tonic-gate 11937c478bd9Sstevel@tonic-gate dnlc_dir_purge(&dp->i_danchor); 11947c478bd9Sstevel@tonic-gate 11957c478bd9Sstevel@tonic-gate while (offset < dirsize) { 11967c478bd9Sstevel@tonic-gate /* 11977c478bd9Sstevel@tonic-gate * If offset is on a block boundary, 11987c478bd9Sstevel@tonic-gate * read the next directory block. 11997c478bd9Sstevel@tonic-gate * Release previous if it exists. 12007c478bd9Sstevel@tonic-gate */ 12017c478bd9Sstevel@tonic-gate if (blkoff(dp->i_fs, offset) == 0) { 12027c478bd9Sstevel@tonic-gate if (fbp != NULL) { 12037c478bd9Sstevel@tonic-gate fbrelse(fbp, S_OTHER); 12047c478bd9Sstevel@tonic-gate } 12057c478bd9Sstevel@tonic-gate 12067c478bd9Sstevel@tonic-gate err = blkatoff(dp, offset, (char **)0, &fbp); 12077c478bd9Sstevel@tonic-gate if (err) { 12087c478bd9Sstevel@tonic-gate goto out; 12097c478bd9Sstevel@tonic-gate } 12107c478bd9Sstevel@tonic-gate entryoffsetinblk = 0; 12117c478bd9Sstevel@tonic-gate } 12127c478bd9Sstevel@tonic-gate ep = (struct direct *)(fbp->fb_addr + entryoffsetinblk); 12137c478bd9Sstevel@tonic-gate if (ep->d_ino == 0 || (ep->d_name[0] == '.' && 12147c478bd9Sstevel@tonic-gate ep->d_name[1] == '\0') || 12157c478bd9Sstevel@tonic-gate (ep->d_name[0] == '.' && ep->d_name[1] == '.' && 12167c478bd9Sstevel@tonic-gate ep->d_name[2] == '\0')) { 12177c478bd9Sstevel@tonic-gate 12187c478bd9Sstevel@tonic-gate entryoffsetinblk += ep->d_reclen; 12197c478bd9Sstevel@tonic-gate 12207c478bd9Sstevel@tonic-gate } else { 12217c478bd9Sstevel@tonic-gate 12227c478bd9Sstevel@tonic-gate if ((err = ufs_iget(dp->i_vfs, ep->d_ino, 12237c478bd9Sstevel@tonic-gate &tp, CRED())) != 0) { 12247c478bd9Sstevel@tonic-gate goto out; 12257c478bd9Sstevel@tonic-gate } 12267c478bd9Sstevel@tonic-gate 12277c478bd9Sstevel@tonic-gate TRANS_BEGIN_CSYNC(ufsvfsp, issync, TOP_REMOVE, 12287c478bd9Sstevel@tonic-gate trans_size = (int)TOP_REMOVE_SIZE(tp)); 12297c478bd9Sstevel@tonic-gate 12307c478bd9Sstevel@tonic-gate /* 12317c478bd9Sstevel@tonic-gate * Delete inode. 12327c478bd9Sstevel@tonic-gate */ 12337c478bd9Sstevel@tonic-gate 12347c478bd9Sstevel@tonic-gate dnlc_remove(ITOV(dp), ep->d_name); 12357c478bd9Sstevel@tonic-gate 12367c478bd9Sstevel@tonic-gate rw_enter(&tp->i_contents, RW_WRITER); 12377c478bd9Sstevel@tonic-gate tp->i_flag |= ICHG; 12387c478bd9Sstevel@tonic-gate tp->i_seq++; 12397c478bd9Sstevel@tonic-gate TRANS_INODE(tp->i_ufsvfs, tp); 12407c478bd9Sstevel@tonic-gate tp->i_nlink--; 12417c478bd9Sstevel@tonic-gate ufs_setreclaim(tp); 12427c478bd9Sstevel@tonic-gate ITIMES_NOLOCK(tp); 12437c478bd9Sstevel@tonic-gate rw_exit(&tp->i_contents); 12447c478bd9Sstevel@tonic-gate 12457c478bd9Sstevel@tonic-gate VN_RELE(ITOV(tp)); 12467c478bd9Sstevel@tonic-gate entryoffsetinblk += ep->d_reclen; 12477c478bd9Sstevel@tonic-gate TRANS_END_CSYNC(ufsvfsp, error, 12487c478bd9Sstevel@tonic-gate issync, TOP_REMOVE, trans_size); 12497c478bd9Sstevel@tonic-gate 12507c478bd9Sstevel@tonic-gate } 12517c478bd9Sstevel@tonic-gate offset += ep->d_reclen; 12527c478bd9Sstevel@tonic-gate } 12537c478bd9Sstevel@tonic-gate 12547c478bd9Sstevel@tonic-gate if (fbp) { 12557c478bd9Sstevel@tonic-gate fbrelse(fbp, S_OTHER); 12567c478bd9Sstevel@tonic-gate } 12577c478bd9Sstevel@tonic-gate 12587c478bd9Sstevel@tonic-gate out: 12597c478bd9Sstevel@tonic-gate rw_exit(&ufsvfsp->vfs_dqrwlock); 12607c478bd9Sstevel@tonic-gate } 1261