vfs_cwd.c revision 1.5 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