kernel - lwkt_token revamp
[dragonfly.git] / sys / emulation / linux / i386 / linprocfs / linprocfs_subr.c
CommitLineData
984263bc
MD
1/*
2 * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
3 * Copyright (c) 1999 Pierre Beyssac
4 * Copyright (c) 1993 Jan-Simon Pendry
5 * Copyright (c) 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 * @(#)procfs_subr.c 8.6 (Berkeley) 5/14/95
40 *
41 * $FreeBSD: src/sys/i386/linux/linprocfs/linprocfs_subr.c,v 1.3.2.4 2001/06/25 19:46:47 pirzyk Exp $
9d044741 42 * $DragonFly: src/sys/emulation/linux/i386/linprocfs/linprocfs_subr.c,v 1.23 2007/08/25 23:27:02 corecode Exp $
984263bc
MD
43 */
44
45#include <sys/param.h>
46#include <sys/systm.h>
47#include <sys/proc.h>
48#include <sys/vnode.h>
49#include <sys/malloc.h>
0961aa92 50#include <sys/mount.h>
1f2de5d4 51#include "linprocfs.h"
984263bc 52
cef48461
MD
53#define PFSHSIZE 256
54#define PFSHMASK (PFSHSIZE - 1)
55
56static struct pfsnode *pfshead[PFSHSIZE];
0bfc853c 57static struct lwkt_token pfs_token;
984263bc
MD
58static int pfsvplock;
59
9d044741 60extern int procfs_domem (struct proc *, struct lwp *, struct pfsnode *pfsp, struct uio *uio);
984263bc
MD
61
62/*
63 * allocate a pfsnode/vnode pair. the vnode is
64 * referenced, but not locked.
65 *
66 * the pid, pfs_type, and mount point uniquely
67 * identify a pfsnode. the mount point is needed
68 * because someone might mount this filesystem
69 * twice.
70 *
71 * all pfsnodes are maintained on a singly-linked
72 * list. new nodes are only allocated when they cannot
73 * be found on this list. entries on the list are
74 * removed when the vfs reclaim entry is called.
75 *
76 * a single lock is kept for the entire list. this is
77 * needed because the getnewvnode() function can block
78 * waiting for a vnode to become free, in which case there
79 * may be more than one process trying to get the same
80 * vnode. this lock is only taken if we are going to
81 * call getnewvnode, since the kernel itself is single-threaded.
82 *
83 * if an entry is found on the list, then call vget() to
84 * take a reference. this is done because there may be
85 * zero references to it and so it needs to removed from
86 * the vnode free list.
87 */
88int
2da2a8af
SW
89linprocfs_allocvp(struct mount *mp, struct vnode **vpp, long pid,
90 pfstype pfs_type)
984263bc 91{
984263bc
MD
92 struct pfsnode *pfs;
93 struct vnode *vp;
94 struct pfsnode **pp;
95 int error;
96
3b998fa9 97 lwkt_gettoken(&pfs_token);
984263bc 98loop:
cef48461 99 for (pfs = pfshead[pid & PFSHMASK]; pfs; pfs = pfs->pfs_next) {
984263bc
MD
100 vp = PFSTOV(pfs);
101 if (pfs->pfs_pid == pid &&
102 pfs->pfs_type == pfs_type &&
103 vp->v_mount == mp) {
87de5057 104 if (vget(vp, LK_EXCLUSIVE|LK_SLEEPFAIL))
984263bc
MD
105 goto loop;
106 *vpp = vp;
3b998fa9 107 lwkt_reltoken(&pfs_token);
984263bc
MD
108 return (0);
109 }
110 }
111
112 /*
113 * otherwise lock the vp list while we call getnewvnode
114 * since that can block.
115 */
116 if (pfsvplock & PROCFS_LOCKED) {
117 pfsvplock |= PROCFS_WANT;
377d4740 118 (void) tsleep((caddr_t) &pfsvplock, 0, "pfsavp", 0);
984263bc
MD
119 goto loop;
120 }
121 pfsvplock |= PROCFS_LOCKED;
122
123 /*
124 * Do the MALLOC before the getnewvnode since doing so afterward
125 * might cause a bogus v_data pointer to get dereferenced
126 * elsewhere if MALLOC should block.
127 */
128 MALLOC(pfs, struct pfsnode *, sizeof(struct pfsnode), M_TEMP, M_WAITOK);
129
6ddb7618 130 error = getnewvnode(VT_PROCFS, mp, vpp, 0, 0);
3446c007 131 if (error) {
984263bc
MD
132 FREE(pfs, M_TEMP);
133 goto out;
134 }
135 vp = *vpp;
136
137 vp->v_data = pfs;
138
139 pfs->pfs_next = 0;
140 pfs->pfs_pid = (pid_t) pid;
141 pfs->pfs_type = pfs_type;
142 pfs->pfs_vnode = vp;
143 pfs->pfs_flags = 0;
dadab5e9 144 pfs->pfs_lockowner = NULL;
984263bc
MD
145 pfs->pfs_fileno = PROCFS_FILENO(pid, pfs_type);
146
147 switch (pfs_type) {
148 case Proot: /* /proc = dr-xr-xr-x */
a1f82243
AH
149 vsetflags(vp, VROOT);
150 /* fallthrough */
151 case Pnet:
152 case Psys:
153 case Psyskernel:
984263bc
MD
154 pfs->pfs_mode = (VREAD|VEXEC) |
155 (VREAD|VEXEC) >> 3 |
156 (VREAD|VEXEC) >> 6;
157 vp->v_type = VDIR;
984263bc
MD
158 break;
159
160 case Pself: /* /proc/self = lr--r--r-- */
161 pfs->pfs_mode = (VREAD) |
162 (VREAD >> 3) |
163 (VREAD >> 6);
164 vp->v_type = VLNK;
165 break;
166
167 case Pproc:
168 pfs->pfs_mode = (VREAD|VEXEC) |
169 (VREAD|VEXEC) >> 3 |
170 (VREAD|VEXEC) >> 6;
171 vp->v_type = VDIR;
172 break;
173
174 case Pexe:
a1f82243
AH
175 case Pcwd:
176 case Pprocroot:
177 case Pfd:
984263bc
MD
178 pfs->pfs_mode = (VREAD|VEXEC) |
179 (VREAD|VEXEC) >> 3 |
180 (VREAD|VEXEC) >> 6;
181 vp->v_type = VLNK;
182 break;
183
184 case Pmem:
185 pfs->pfs_mode = (VREAD|VWRITE) |
fc6d0222 186 (VREAD) >> 3;
984263bc
MD
187 vp->v_type = VREG;
188 break;
189
190 case Pprocstat:
191 case Pprocstatus:
a1f82243
AH
192 case Pcmdline:
193 case Penviron:
194 case Pstatm:
984263bc 195 /* fallthrough */
a1f82243 196 case Pmaps:
984263bc
MD
197 case Pmeminfo:
198 case Pcpuinfo:
a3c5067f 199 case Pmounts:
984263bc
MD
200 case Pstat:
201 case Puptime:
202 case Pversion:
203 case Ploadavg:
a1f82243
AH
204 case Pdevices:
205 case Pnetdev:
206 case Posrelease:
207 case Postype:
208 case Ppidmax:
984263bc
MD
209 pfs->pfs_mode = (VREAD) |
210 (VREAD >> 3) |
211 (VREAD >> 6);
212 vp->v_type = VREG;
213 break;
214
215 default:
216 panic("linprocfs_allocvp");
217 }
218
219 /* add to procfs vnode list */
cef48461 220 for (pp = &pfshead[pid & PFSHMASK]; *pp; pp = &(*pp)->pfs_next)
984263bc
MD
221 continue;
222 *pp = pfs;
5fd012e0
MD
223
224 vx_unlock(vp); /* vnode ready to roll! */
984263bc
MD
225
226out:
227 pfsvplock &= ~PROCFS_LOCKED;
228
229 if (pfsvplock & PROCFS_WANT) {
230 pfsvplock &= ~PROCFS_WANT;
231 wakeup((caddr_t) &pfsvplock);
232 }
3b998fa9 233 lwkt_reltoken(&pfs_token);
984263bc
MD
234
235 return (error);
236}
237
238int
2da2a8af 239linprocfs_freevp(struct vnode *vp)
984263bc
MD
240{
241 struct pfsnode **pfspp;
242 struct pfsnode *pfs = VTOPFS(vp);
243
3b998fa9 244 lwkt_gettoken(&pfs_token);
cef48461
MD
245 pfspp = &pfshead[pfs->pfs_pid & PFSHMASK];
246 while (*pfspp != pfs) {
247 KKASSERT(*pfspp != NULL);
248 pfspp = &(*pfspp)->pfs_next;
984263bc 249 }
cef48461 250 *pfspp = pfs->pfs_next;
3b998fa9 251 lwkt_reltoken(&pfs_token);
984263bc 252 FREE(vp->v_data, M_TEMP);
cef48461 253 vp->v_data = NULL;
984263bc
MD
254 return (0);
255}
256
257int
2da2a8af 258linprocfs_rw(struct vop_read_args *ap)
984263bc
MD
259{
260 struct vnode *vp = ap->a_vp;
261 struct uio *uio = ap->a_uio;
dadab5e9 262 struct thread *td = uio->uio_td;
984263bc
MD
263 struct pfsnode *pfs = VTOPFS(vp);
264 struct proc *p;
dadab5e9 265 struct proc *curp;
9d044741 266 struct lwp *lp;
984263bc
MD
267 int rtval;
268
dadab5e9
MD
269 curp = td->td_proc;
270 KKASSERT(curp);
271
984263bc
MD
272 p = PFIND(pfs->pfs_pid);
273 if (p == 0)
274 return (EINVAL);
275 if (p->p_pid == 1 && securelevel > 0 && uio->uio_rw == UIO_WRITE)
276 return (EACCES);
9d044741
SS
277 lp = FIRST_LWP_IN_PROC(p);
278 LWPHOLD(lp);
984263bc
MD
279
280 while (pfs->pfs_lockowner) {
377d4740 281 tsleep(&pfs->pfs_lockowner, 0, "pfslck", 0);
984263bc 282 }
dadab5e9 283 pfs->pfs_lockowner = curthread;
984263bc
MD
284 switch (pfs->pfs_type) {
285 case Pmem:
9d044741 286 rtval = procfs_domem(curp, lp, pfs, uio);
984263bc
MD
287 break;
288 case Pprocstat:
289 rtval = linprocfs_doprocstat(curp, p, pfs, uio);
290 break;
291 case Pprocstatus:
292 rtval = linprocfs_doprocstatus(curp, p, pfs, uio);
293 break;
294 case Pmeminfo:
295 rtval = linprocfs_domeminfo(curp, p, pfs, uio);
296 break;
297 case Pcpuinfo:
298 rtval = linprocfs_docpuinfo(curp, p, pfs, uio);
299 break;
a3c5067f
AH
300 case Pmounts:
301 rtval = linprocfs_domounts(curp, p, pfs, uio);
302 break;
984263bc
MD
303 case Pstat:
304 rtval = linprocfs_dostat(curp, p, pfs, uio);
305 break;
306 case Puptime:
307 rtval = linprocfs_douptime(curp, p, pfs, uio);
308 break;
309 case Pversion:
310 rtval = linprocfs_doversion(curp, p, pfs, uio);
311 break;
312 case Ploadavg:
313 rtval = linprocfs_doloadavg(curp, p, pfs, uio);
314 break;
a1f82243
AH
315 case Pnetdev:
316 rtval = linprocfs_donetdev(curp, p, pfs, uio);
317 break;
318 case Pdevices:
319 rtval = linprocfs_dodevices(curp, p, pfs, uio);
320 break;
321 case Posrelease:
322 rtval = linprocfs_doosrelease(curp, p, pfs, uio);
323 break;
324 case Postype:
325 rtval = linprocfs_doostype(curp, p, pfs, uio);
326 break;
327 case Ppidmax:
328 rtval = linprocfs_dopidmax(curp, p, pfs, uio);
329 break;
330 case Pmaps:
331 rtval = linprocfs_domaps(curp, p, pfs, uio);
332 break;
333 case Pstatm:
334 rtval = linprocfs_dostatm(curp, p, pfs, uio);
335 break;
984263bc
MD
336 default:
337 rtval = EOPNOTSUPP;
338 break;
339 }
9d044741 340 LWPRELE(lp);
dadab5e9 341 pfs->pfs_lockowner = NULL;
984263bc
MD
342 wakeup(&pfs->pfs_lockowner);
343 return rtval;
344}
345
346#if 0
347/*
348 * Get a string from userland into (buf). Strip a trailing
349 * nl character (to allow easy access from the shell).
350 * The buffer should be *buflenp + 1 chars long. vfs_getuserstr
351 * will automatically add a nul char at the end.
352 *
353 * Returns 0 on success or the following errors
354 *
355 * EINVAL: file offset is non-zero.
356 * EMSGSIZE: message is longer than kernel buffer
357 * EFAULT: user i/o buffer is not addressable
358 */
359int
2da2a8af 360vfs_getuserstr(struct uio *uio, char *buf, int *buflenp)
984263bc
MD
361{
362 int xlen;
363 int error;
364
365 if (uio->uio_offset != 0)
366 return (EINVAL);
367
368 xlen = *buflenp;
369
370 /* must be able to read the whole string in one go */
371 if (xlen < uio->uio_resid)
372 return (EMSGSIZE);
373 xlen = uio->uio_resid;
374
375 if ((error = uiomove(buf, xlen, uio)) != 0)
376 return (error);
377
378 /* allow multiple writes without seeks */
379 uio->uio_offset = 0;
380
381 /* cleanup string and remove trailing newline */
382 buf[xlen] = '\0';
383 xlen = strlen(buf);
384 if (xlen > 0 && buf[xlen-1] == '\n')
385 buf[--xlen] = '\0';
386 *buflenp = xlen;
387
388 return (0);
389}
390
391vfs_namemap_t *
2da2a8af 392vfs_findname(vfs_namemap_t *nm, char *buf, int buflen)
984263bc
MD
393{
394
395 for (; nm->nm_name; nm++)
396 if (bcmp(buf, nm->nm_name, buflen+1) == 0)
397 return (nm);
398
399 return (0);
400}
401#endif
402
403void
0bfc853c
MD
404linprocfs_init(void)
405{
3b998fa9 406 lwkt_token_init(&pfs_token, 1);
a1f82243 407}
0bfc853c
MD
408
409void
dadab5e9 410linprocfs_exit(struct thread *td)
984263bc
MD
411{
412 struct pfsnode *pfs;
cef48461
MD
413 struct vnode *vp;
414 pid_t pid;
415
416 KKASSERT(td->td_proc);
417 pid = td->td_proc->p_pid;
984263bc
MD
418
419 /*
cef48461 420 * Remove all the procfs vnodes associated with an exiting process.
984263bc 421 */
3b998fa9 422 lwkt_gettoken(&pfs_token);
cef48461
MD
423restart:
424 for (pfs = pfshead[pid & PFSHMASK]; pfs; pfs = pfs->pfs_next) {
984263bc 425 if (pfs->pfs_pid == pid) {
cef48461 426 vp = PFSTOV(pfs);
e3332475 427 vx_get(vp);
3c37c940 428 vgone_vxlocked(vp);
e3332475 429 vx_put(vp);
cef48461
MD
430 goto restart;
431 }
984263bc 432 }
3b998fa9 433 lwkt_reltoken(&pfs_token);
0bfc853c 434 lwkt_token_uninit(&pfs_token);
984263bc 435}
cef48461 436