xref: /netbsd-src/sys/kern/vfs_cwd.c (revision e6c7e151de239c49d2e38720a061ed9d1fa99309)
1 /*	$NetBSD: vfs_cwd.c,v 1.5 2020/02/23 22:14:03 ad Exp $	*/
2 
3 /*-
4  * Copyright (c) 2008, 2020 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Current working directory.
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: vfs_cwd.c,v 1.5 2020/02/23 22:14:03 ad Exp $");
35 
36 #include <sys/param.h>
37 #include <sys/atomic.h>
38 #include <sys/filedesc.h>
39 #include <sys/proc.h>
40 #include <sys/vnode.h>
41 #include <sys/xcall.h>
42 
43 static int	cwdi_ctor(void *, void *, int);
44 static void	cwdi_dtor(void *, void *);
45 
46 static pool_cache_t cwdi_cache;
47 
48 void
49 cwd_sys_init(void)
50 {
51 
52 	cwdi_cache = pool_cache_init(sizeof(struct cwdinfo), coherency_unit,
53 	    0, 0, "cwdi", NULL, IPL_NONE, cwdi_ctor, cwdi_dtor, NULL);
54 	KASSERT(cwdi_cache != NULL);
55 }
56 
57 /*
58  * Create an initial cwdinfo structure, using the same current and root
59  * directories as curproc.
60  */
61 struct cwdinfo *
62 cwdinit(void)
63 {
64 	struct cwdinfo *cwdi;
65 	struct cwdinfo *copy;
66 
67 	cwdi = pool_cache_get(cwdi_cache, PR_WAITOK);
68 
69 	copy = cwdenter(RW_READER);
70 	cwdi->cwdi_cdir = copy->cwdi_cdir;
71 	if (cwdi->cwdi_cdir)
72 		vref(cwdi->cwdi_cdir);
73 	cwdi->cwdi_rdir = copy->cwdi_rdir;
74 	if (cwdi->cwdi_rdir)
75 		vref(cwdi->cwdi_rdir);
76 	cwdi->cwdi_edir = copy->cwdi_edir;
77 	if (cwdi->cwdi_edir)
78 		vref(cwdi->cwdi_edir);
79 	cwdi->cwdi_cmask = copy->cwdi_cmask;
80 	cwdi->cwdi_refcnt = 1;
81 	cwdexit(copy);
82 
83 	return (cwdi);
84 }
85 
86 static int
87 cwdi_ctor(void *arg, void *obj, int flags)
88 {
89 	struct cwdinfo *cwdi = obj;
90 
91 	mutex_init(&cwdi->cwdi_lock, MUTEX_DEFAULT, IPL_NONE);
92 
93 	return 0;
94 }
95 
96 static void
97 cwdi_dtor(void *arg, void *obj)
98 {
99 	struct cwdinfo *cwdi = obj;
100 
101 	mutex_destroy(&cwdi->cwdi_lock);
102 }
103 
104 /*
105  * Make p2 share p1's cwdinfo.
106  */
107 void
108 cwdshare(struct proc *p2)
109 {
110 	struct cwdinfo *cwdi;
111 
112 	cwdi = curproc->p_cwdi;
113 
114 	atomic_inc_uint(&cwdi->cwdi_refcnt);
115 	p2->p_cwdi = cwdi;
116 }
117 
118 /*
119  * Make sure proc has only one reference to its cwdi, creating
120  * a new one if necessary.
121  */
122 void
123 cwdunshare(struct proc *p)
124 {
125 	struct cwdinfo *cwdi = p->p_cwdi;
126 
127 	if (cwdi->cwdi_refcnt > 1) {
128 		cwdi = cwdinit();
129 		cwdfree(p->p_cwdi);
130 		p->p_cwdi = cwdi;
131 	}
132 }
133 
134 /*
135  * Release a cwdinfo structure.
136  */
137 void
138 cwdfree(struct cwdinfo *cwdi)
139 {
140 
141 	if (atomic_dec_uint_nv(&cwdi->cwdi_refcnt) > 0)
142 		return;
143 
144 	vrele(cwdi->cwdi_cdir);
145 	if (cwdi->cwdi_rdir)
146 		vrele(cwdi->cwdi_rdir);
147 	if (cwdi->cwdi_edir)
148 		vrele(cwdi->cwdi_edir);
149 	pool_cache_put(cwdi_cache, cwdi);
150 }
151 
152 void
153 cwdexec(struct proc *p)
154 {
155 
156 	cwdunshare(p);
157 
158 	if (p->p_cwdi->cwdi_edir) {
159 		vrele(p->p_cwdi->cwdi_edir);
160 	}
161 }
162 
163 /*
164  * Used when curlwp wants to use or update its cwdinfo, and needs to prevent
165  * concurrent changes.
166  *
167  * "op" is either RW_READER or RW_WRITER indicating the kind of lock
168  * required.  If a read lock on the cwdinfo is requested, then curlwp must
169  * not block while holding the lock, or the cwdinfo could become stale.
170  * It's okay to block while holding a write lock.
171  */
172 struct cwdinfo *
173 cwdenter(krw_t op)
174 {
175 	struct cwdinfo *cwdi = curproc->p_cwdi;
176 
177 	if (__predict_true(op == RW_READER)) {
178 		/*
179 		 * Disable preemption to hold off the writer side's xcall,
180 		 * then observe the lock.  If it's already taken, we need to
181 		 * join in the melee.  Otherwise we're good to go; keeping
182 		 * the xcall at bay with kpreempt_disable() will prevent any
183 		 * changes while the caller is pondering the cwdinfo.
184 		 */
185 		kpreempt_disable();
186 		if (__predict_true(mutex_owner(&cwdi->cwdi_lock) == NULL)) {
187 			membar_consumer();
188 			return cwdi;
189 		}
190 		kpreempt_enable();
191 		mutex_enter(&cwdi->cwdi_lock);
192 	} else {
193 		/*
194 		 * About to make changes.  If there's more than one
195 		 * reference on the cwdinfo, or curproc has more than one
196 		 * LWP, then LWPs other than curlwp can also see the
197 		 * cwdinfo.  Run a cross call to get all LWPs out of the
198 		 * read section.
199 		 */
200 		mutex_enter(&cwdi->cwdi_lock);
201 		if (cwdi->cwdi_refcnt + curproc->p_nlwps > 2)
202 			xc_barrier(0);
203 	}
204 	return cwdi;
205 }
206 
207 /*
208  * Release a lock previously taken with cwdenter().
209  */
210 void
211 cwdexit(struct cwdinfo *cwdi)
212 {
213 	struct lwp *l = curlwp;
214 
215 	KASSERT(cwdi == l->l_proc->p_cwdi);
216 
217 	if (__predict_true(mutex_owner(&cwdi->cwdi_lock) != l))
218 		kpreempt_enable();
219 	else
220 		mutex_exit(&cwdi->cwdi_lock);
221 }
222 
223 /*
224  * Called when there is a need to inspect some other process' cwdinfo.  Used
225  * by procfs and sysctl.  This gets you a read lock; the cwdinfo must NOT be
226  * changed.
227  */
228 const struct cwdinfo *
229 cwdlock(struct proc *p)
230 {
231 	struct cwdinfo *cwdi = p->p_cwdi;
232 
233 	mutex_enter(&cwdi->cwdi_lock);
234 	return cwdi;
235 }
236 
237 /*
238  * Release a lock acquired with cwdlock().
239  */
240 void
241 cwdunlock(struct proc *p)
242 {
243 	struct cwdinfo *cwdi = p->p_cwdi;
244 
245 	mutex_exit(&cwdi->cwdi_lock);
246 }
247 
248 /*
249  * Get a reference to the current working directory and return it.
250  */
251 struct vnode *
252 cwdcdir(void)
253 {
254 	struct cwdinfo *cwdi;
255 	struct vnode *vp;
256 
257 	cwdi = cwdenter(RW_READER);
258 	if ((vp = cwdi->cwdi_cdir) != NULL)
259 		vref(vp);
260 	cwdexit(cwdi);
261 	return vp;
262 }
263 
264 /*
265  * Get a reference to the root directory and return it.
266  */
267 struct vnode *
268 cwdrdir(void)
269 {
270 	struct cwdinfo *cwdi;
271 	struct vnode *vp;
272 
273 	cwdi = cwdenter(RW_READER);
274 	if ((vp = cwdi->cwdi_rdir) != NULL)
275 		vref(vp);
276 	cwdexit(cwdi);
277 	return vp;
278 }
279