4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
32 #include <sys/param.h>
33 #include <sys/inttypes.h>
34 #include <sys/types.h>
35 #include <sys/sysmacros.h>
36 #include <sys/systm.h>
38 #include <sys/errno.h>
40 #include <sys/vnode.h>
43 #include <sys/session.h>
45 #include <sys/utsname.h>
46 #include <sys/utssys.h>
47 #include <sys/ustat.h>
48 #include <sys/statvfs.h>
50 #include <sys/debug.h>
51 #include <sys/pathname.h>
52 #include <sys/modctl.h>
53 #include <sys/fs/snode.h>
54 #include <sys/sunldi_impl.h>
56 #include <sys/sunddi.h>
57 #include <sys/cmn_err.h>
58 #include <sys/ddipropdefs.h>
59 #include <sys/ddi_impldefs.h>
60 #include <sys/modctl.h>
61 #include <sys/flock.h>
62 #include <sys/share.h>
65 #include <vm/seg_vn.h>
66 #include <util/qsort.h>
72 static int uts_fusers(char *, int, intptr_t);
73 static int _statvfs64_by_dev(dev_t
, struct statvfs64
*);
75 #if defined(_ILP32) || defined(_SYSCALL32_IMPL)
77 static int utssys_uname32(caddr_t
, rval_t
*);
78 static int utssys_ustat32(dev_t
, struct ustat32
*);
81 utssys32(void *buf
, int arg
, int type
, void *outbp
)
91 * This is an obsolete way to get the utsname structure
92 * (it only gives you the first 8 characters of each field!)
93 * uname(2) is the preferred and better interface.
95 error
= utssys_uname32(buf
, &rv
);
98 error
= utssys_ustat32(expldev((dev32_t
)arg
), buf
);
101 error
= uts_fusers(buf
, arg
, (intptr_t)outbp
);
108 return (error
== 0 ? rv
.r_vals
: (int64_t)set_errno(error
));
112 utssys_uname32(caddr_t buf
, rval_t
*rvp
)
114 if (copyout(utsname
.sysname
, buf
, 8))
117 if (subyte(buf
, 0) < 0)
120 if (copyout(uts_nodename(), buf
, 8))
123 if (subyte(buf
, 0) < 0)
126 if (copyout(utsname
.release
, buf
, 8))
129 if (subyte(buf
, 0) < 0)
132 if (copyout(utsname
.version
, buf
, 8))
135 if (subyte(buf
, 0) < 0)
138 if (copyout(utsname
.machine
, buf
, 8))
141 if (subyte(buf
, 0) < 0)
148 utssys_ustat32(dev_t dev
, struct ustat32
*cbuf
)
150 struct ustat32 ust32
;
151 struct statvfs64 stvfs
;
156 if ((error
= _statvfs64_by_dev(dev
, &stvfs
)) != 0)
159 fsbc64
= stvfs
.f_bfree
* (stvfs
.f_frsize
/ 512);
161 * Check to see if the number of free blocks can be expressed
162 * in 31 bits or whether the number of free files is more than
163 * can be expressed in 32 bits and is not -1 (UINT64_MAX). NFS
164 * Version 2 does not support the number of free files and
165 * hence will return -1. -1, when translated from a 32 bit
166 * quantity to an unsigned 64 bit quantity, turns into UINT64_MAX.
168 if (fsbc64
> INT32_MAX
||
169 (stvfs
.f_ffree
> UINT32_MAX
&& stvfs
.f_ffree
!= UINT64_MAX
))
172 ust32
.f_tfree
= (daddr32_t
)fsbc64
;
173 ust32
.f_tinode
= (ino32_t
)stvfs
.f_ffree
;
178 while (i
++ < sizeof (ust32
.f_fname
))
183 while (*cp
!= '\0' &&
184 (i
++ < sizeof (stvfs
.f_fstr
) - sizeof (ust32
.f_fpack
)))
186 (void) strncpy(ust32
.f_fpack
, cp
+ 1, sizeof (ust32
.f_fpack
));
188 if (copyout(&ust32
, cbuf
, sizeof (ust32
)))
193 #endif /* _ILP32 || _SYSCALL32_IMPL */
197 static int uts_ustat64(dev_t
, struct ustat
*);
200 utssys64(void *buf
, long arg
, int type
, void *outbp
)
209 error
= uts_ustat64((dev_t
)arg
, buf
);
212 error
= uts_fusers(buf
, (int)arg
, (intptr_t)outbp
);
219 return (error
== 0 ? rv
.r_vals
: (int64_t)set_errno(error
));
223 uts_ustat64(dev_t dev
, struct ustat
*cbuf
)
226 struct statvfs64 stvfs
;
231 if ((error
= _statvfs64_by_dev(dev
, &stvfs
)) != 0)
234 fsbc64
= stvfs
.f_bfree
* (stvfs
.f_frsize
/ 512);
235 ust
.f_tfree
= (daddr_t
)fsbc64
;
236 ust
.f_tinode
= (ino_t
)stvfs
.f_ffree
;
241 while (i
++ < sizeof (ust
.f_fname
))
246 while (*cp
!= '\0' &&
247 (i
++ < sizeof (stvfs
.f_fstr
) - sizeof (ust
.f_fpack
)))
249 (void) strncpy(ust
.f_fpack
, cp
+ 1, sizeof (ust
.f_fpack
));
251 if (copyout(&ust
, cbuf
, sizeof (ust
)))
259 * Utility routine for the ustat implementations.
260 * (If it wasn't for the 'find-by-dev_t' semantic of ustat(2), we could push
261 * this all out into userland, sigh.)
264 _statvfs64_by_dev(dev_t dev
, struct statvfs64
*svp
)
269 if ((vfsp
= vfs_dev2vfsp(dev
)) == NULL
) {
271 * See if it's the root of our zone.
273 vfsp
= curproc
->p_zone
->zone_rootvp
->v_vfsp
;
274 if (vfsp
->vfs_dev
== dev
) {
282 error
= VFS_STATVFS(vfsp
, svp
);
288 * Check if this pid has an NBMAND lock or share reservation
289 * on this vp. llp is a snapshoted list of all NBMAND locks
290 * set by this pid. Return 1 if there is an NBMAND lock else
294 proc_has_nbmand_on_vp(vnode_t
*vp
, pid_t pid
, locklist_t
*llp
)
297 * Any NBMAND lock held by the process on this vp?
300 if (llp
->ll_vp
== vp
) {
306 * Any NBMAND share reservation on the vp for this process?
308 return (proc_has_nbmand_share_on_vp(vp
, pid
));
312 dofusers(vnode_t
*fvp
, int flags
)
317 pid_t npids
, pidx
, *pidlist
;
318 int v_proc
= v
.v_proc
; /* max # of procs */
320 int contained
= (flags
& F_CONTAINED
);
321 int nbmandonly
= (flags
& F_NBMANDLIST
);
322 int dip_usage
= (flags
& F_DEVINFO
);
323 int fvp_isdev
= vn_matchops(fvp
, spec_getvnodeops());
324 zone_t
*zone
= curproc
->p_zone
;
325 int inglobal
= INGLOBALZONE(curproc
);
327 /* get a pointer to the file system containing this vnode */
331 /* allocate the data structure to return our results in */
332 fu_data
= kmem_alloc(fu_data_size(v_proc
), KM_SLEEP
);
333 fu_data
->fud_user_max
= v_proc
;
334 fu_data
->fud_user_count
= 0;
336 /* get a snapshot of all the pids we're going to check out */
337 pidlist
= kmem_alloc(v_proc
* sizeof (pid_t
), KM_SLEEP
);
338 mutex_enter(&pidlock
);
339 for (npids
= 0, prp
= practive
; prp
!= NULL
; prp
= prp
->p_next
) {
340 if (inglobal
|| prp
->p_zone
== zone
)
341 pidlist
[npids
++] = prp
->p_pid
;
343 mutex_exit(&pidlock
);
345 /* grab each process and check its file usage */
346 for (pidx
= 0; pidx
< npids
; pidx
++) {
347 locklist_t
*llp
= NULL
;
353 pid_t pid
= pidlist
[pidx
];
357 * grab prp->p_lock using sprlock()
358 * if sprlock() fails the process does not exists anymore
364 /* get the processes credential info in case we need it */
365 mutex_enter(&prp
->p_crlock
);
366 uid
= crgetruid(prp
->p_cred
);
367 mutex_exit(&prp
->p_crlock
);
370 * it's safe to drop p_lock here because we
371 * called sprlock() before and it set the SPRLOCK
372 * flag for the process so it won't go away.
374 mutex_exit(&prp
->p_lock
);
377 * now we want to walk a processes open file descriptors
378 * to do this we need to grab the fip->fi_lock. (you
379 * can't hold p_lock when grabbing the fip->fi_lock.)
382 mutex_enter(&fip
->fi_lock
);
385 * Snapshot nbmand locks for pid
387 llp
= flk_active_nbmand_locks(prp
->p_pid
);
388 for (i
= 0; i
< fip
->fi_nfiles
; i
++) {
392 UF_ENTER(ufp
, fip
, i
);
393 if (((fp
= ufp
->uf_file
) == NULL
) ||
394 ((vp
= fp
->f_vnode
) == NULL
)) {
400 * if the target file (fvp) is not a device
401 * and corrosponds to the root of a filesystem
402 * (cvfsp), then check if it contains the file
403 * is use by this process (vp).
405 if (contained
&& (vp
->v_vfsp
== cvfsp
))
409 * if the target file (fvp) is not a device,
410 * then check if it matches the file in use
411 * by this process (vp).
413 if (!fvp_isdev
&& VN_CMP(fvp
, vp
))
417 * if the target file (fvp) is a device,
418 * then check if the current file in use
419 * by this process (vp) maps to the same device
423 vn_matchops(vp
, spec_getvnodeops()) &&
424 (fvp
->v_rdev
== vp
->v_rdev
))
428 * if the target file (fvp) is a device,
429 * and we're checking for device instance
430 * usage, then check if the current file in use
431 * by this process (vp) maps to the same device
435 vn_matchops(vp
, spec_getvnodeops()) &&
436 (VTOCS(fvp
)->s_dip
== VTOCS(vp
)->s_dip
))
440 * if the current file in use by this process (vp)
441 * doesn't match what we're looking for, move on
442 * to the next file in the process.
444 if ((use_flag
& F_OPEN
) == 0) {
449 if (proc_has_nbmand_on_vp(vp
, prp
->p_pid
, llp
)) {
450 /* A nbmand found so we're done. */
458 flk_free_locklist(llp
);
460 mutex_exit(&fip
->fi_lock
);
463 * If nbmand usage tracking is desired and no nbmand was
464 * found for this process, then no need to do further
465 * usage tracking for this process.
467 if (nbmandonly
&& (!(use_flag
& F_NBM
))) {
469 * grab the process lock again, clear the SPRLOCK
470 * flag, release the process, and continue.
472 mutex_enter(&prp
->p_lock
);
478 * All other types of usage.
479 * For the next few checks we need to hold p_lock.
481 mutex_enter(&prp
->p_lock
);
485 * if the target file (fvp) is a device
486 * then check if it matches the processes tty
488 * we grab s_lock to protect ourselves against
489 * freectty() freeing the vnode out from under us.
492 mutex_enter(&sp
->s_lock
);
493 vp
= prp
->p_sessp
->s_vp
;
495 if (fvp
->v_rdev
== vp
->v_rdev
)
499 (VTOCS(fvp
)->s_dip
== VTOCS(vp
)->s_dip
))
502 mutex_exit(&sp
->s_lock
);
504 /* check the processes current working directory */
506 (VN_CMP(fvp
, up
->u_cdir
) ||
507 (contained
&& (up
->u_cdir
->v_vfsp
== cvfsp
))))
510 /* check the processes root directory */
512 (VN_CMP(fvp
, up
->u_rdir
) ||
513 (contained
&& (up
->u_rdir
->v_vfsp
== cvfsp
))))
516 /* check the program text vnode */
518 (VN_CMP(fvp
, prp
->p_exec
) ||
519 (contained
&& (prp
->p_exec
->v_vfsp
== cvfsp
))))
523 /* Now we can drop p_lock again */
524 mutex_exit(&prp
->p_lock
);
527 * now we want to walk a processes memory mappings.
528 * to do this we need to grab the prp->p_as lock. (you
529 * can't hold p_lock when grabbing the prp->p_as lock.)
531 if (prp
->p_as
!= &kas
) {
533 struct as
*as
= prp
->p_as
;
535 AS_LOCK_ENTER(as
, RW_READER
);
536 for (seg
= AS_SEGFIRST(as
); seg
;
537 seg
= AS_SEGNEXT(as
, seg
)) {
539 * if we can't get a backing vnode for this
540 * segment then skip it
543 if ((SEGOP_GETVP(seg
, seg
->s_base
, &vp
)) ||
548 * if the target file (fvp) is not a device
549 * and corrosponds to the root of a filesystem
550 * (cvfsp), then check if it contains the
551 * vnode backing this segment (vp).
553 if (contained
&& (vp
->v_vfsp
== cvfsp
)) {
559 * if the target file (fvp) is not a device,
560 * check if it matches the the vnode backing
563 if (!fvp_isdev
&& VN_CMP(fvp
, vp
)) {
569 * if the target file (fvp) isn't a device,
570 * or the the vnode backing this segment (vp)
571 * isn't a device then continue.
574 !vn_matchops(vp
, spec_getvnodeops()))
578 * check if the vnode backing this segment
579 * (vp) maps to the same device minor node
580 * as the target device (fvp)
582 if (fvp
->v_rdev
== vp
->v_rdev
) {
588 * if we're checking for device instance
589 * usage, then check if the vnode backing
590 * this segment (vp) maps to the same device
591 * instance as the target device (fvp).
594 (VTOCS(fvp
)->s_dip
== VTOCS(vp
)->s_dip
)) {
603 ASSERT(pcnt
< fu_data
->fud_user_max
);
604 fu_data
->fud_user
[pcnt
].fu_flags
= use_flag
;
605 fu_data
->fud_user
[pcnt
].fu_pid
= pid
;
606 fu_data
->fud_user
[pcnt
].fu_uid
= uid
;
611 * grab the process lock again, clear the SPRLOCK
612 * flag, release the process, and continue.
614 mutex_enter(&prp
->p_lock
);
618 kmem_free(pidlist
, v_proc
* sizeof (pid_t
));
620 fu_data
->fud_user_count
= pcnt
;
624 typedef struct dofkusers_arg
{
632 dofkusers_walker(const ldi_usage_t
*ldi_usage
, void *arg
)
634 dofkusers_arg_t
*dofkusers_arg
= (dofkusers_arg_t
*)arg
;
636 vnode_t
*fvp
= dofkusers_arg
->fvp
;
637 int flags
= dofkusers_arg
->flags
;
638 int *error
= dofkusers_arg
->error
;
639 fu_data_t
*fu_data
= dofkusers_arg
->fu_data
;
644 int dip_usage
= (flags
& F_DEVINFO
);
647 ASSERT(vn_matchops(fvp
, spec_getvnodeops()));
650 * check if the dev_t of the target device matches the dev_t
651 * of the device we're trying to find usage info for.
653 if (fvp
->v_rdev
!= ldi_usage
->tgt_devt
) {
656 * if the dev_ts don't match and we're not trying
657 * to find usage information for device instances
661 return (LDI_USAGE_CONTINUE
);
665 * we're trying to find usage information for an
666 * device instance instead of just a minor node.
668 * check if the dip for the target device matches the
669 * dip of the device we're trying to find usage info for.
671 if (VTOCS(fvp
)->s_dip
!= ldi_usage
->tgt_dip
)
672 return (LDI_USAGE_CONTINUE
);
675 if (fu_data
->fud_user_count
>= fu_data
->fud_user_max
) {
677 return (LDI_USAGE_TERMINATE
);
680 /* get the device vnode user information */
681 modid
= ldi_usage
->src_modid
;
684 minor
= instance
= -1;
685 if (ldi_usage
->src_dip
!= NULL
) {
686 instance
= DEVI(ldi_usage
->src_dip
)->devi_instance
;
688 if (ldi_usage
->src_devt
!= DDI_DEV_T_NONE
) {
689 minor
= getminor(ldi_usage
->src_devt
);
692 /* set the device vnode user information */
693 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_flags
= F_KERNEL
;
694 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_modid
= modid
;
695 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_instance
= instance
;
696 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_minor
= minor
;
698 fu_data
->fud_user_count
++;
700 return (LDI_USAGE_CONTINUE
);
704 f_user_cmp(const void *arg1
, const void *arg2
)
706 f_user_t
*f_user1
= (f_user_t
*)arg1
;
707 f_user_t
*f_user2
= (f_user_t
*)arg2
;
710 * we should only be called for f_user_t entires that represent
711 * a kernel file consumer
713 ASSERT(f_user1
->fu_flags
& F_KERNEL
);
714 ASSERT(f_user2
->fu_flags
& F_KERNEL
);
716 if (f_user1
->fu_modid
!= f_user2
->fu_modid
)
717 return ((f_user1
->fu_modid
< f_user2
->fu_modid
) ? -1 : 1);
719 if (f_user1
->fu_instance
!= f_user2
->fu_instance
)
720 return ((f_user1
->fu_instance
< f_user2
->fu_instance
) ? -1 : 1);
722 if (f_user1
->fu_minor
!= f_user2
->fu_minor
)
723 return ((f_user1
->fu_minor
< f_user2
->fu_minor
) ? -1 : 1);
729 dofkusers(vnode_t
*fvp
, int flags
, int *error
)
731 dofkusers_arg_t dofkusers_arg
;
736 * we only keep track of kernel device consumers, so if the
737 * target vnode isn't a device then there's nothing to do here
739 if (!vn_matchops(fvp
, spec_getvnodeops()))
742 /* allocate the data structure to return our results in */
743 user_max
= ldi_usage_count();
744 fu_data
= kmem_alloc(fu_data_size(user_max
), KM_SLEEP
);
745 fu_data
->fud_user_max
= user_max
;
746 fu_data
->fud_user_count
= 0;
748 /* invoke the callback to collect device usage information */
749 dofkusers_arg
.fvp
= fvp
;
750 dofkusers_arg
.flags
= flags
;
751 dofkusers_arg
.error
= error
;
752 dofkusers_arg
.fu_data
= fu_data
;
753 ldi_usage_walker(&dofkusers_arg
, dofkusers_walker
);
755 /* check for errors */
759 /* if there aren't any file consumers then return */
760 if (fu_data
->fud_user_count
== 0)
764 * since we ignore the spec_type of the target we're trying to
765 * access it's possible that we could have duplicates entries in
766 * the list of consumers.
768 * we don't want to check for duplicate in the callback because
769 * we're holding locks in the ldi when the callback is invoked.
771 * so here we need to go through the array of file consumers
772 * and remove duplicate entries.
775 /* first sort the array of file consumers */
776 qsort((caddr_t
)fu_data
->fud_user
, fu_data
->fud_user_count
,
777 sizeof (f_user_t
), f_user_cmp
);
779 /* then remove any duplicate entires */
781 while (i
< fu_data
->fud_user_count
) {
783 if (f_user_cmp(&fu_data
->fud_user
[i
],
784 &fu_data
->fud_user
[i
- 1]) != 0) {
786 * the current element is unique, move onto
794 * this entry is a duplicate so if it's not the last
795 * entry in the array then remove it.
797 fu_data
->fud_user_count
--;
798 if (i
== fu_data
->fud_user_count
)
801 bcopy(&fu_data
->fud_user
[i
+ 1], &fu_data
->fud_user
[i
],
802 sizeof (f_user_t
) * (fu_data
->fud_user_count
- i
));
809 * Determine the ways in which processes and the kernel are using a named
810 * file or mounted file system (path). Normally return 0. In case of an
811 * error appropriate errno will be returned.
813 * Upon success, uts_fusers will also copyout the file usage information
814 * in the form of an array of f_user_t's that are contained within an
815 * fu_data_t pointed to by userbp.
818 uts_fusers(char *path
, int flags
, intptr_t userbp
)
820 fu_data_t
*fu_data
= NULL
, *fuk_data
= NULL
;
825 int total_max
, total_out
;
826 int contained
= (flags
& F_CONTAINED
);
827 int dip_usage
= (flags
& F_DEVINFO
);
831 /* figure out how man f_user_t's we can safetly copy out */
832 if (copyin((const void *)userbp
, &total_max
, sizeof (total_max
)))
836 * check if we only want a count of how many kernel device
839 if (flags
& F_KINFO_COUNT
) {
840 fu_header
.fud_user_max
= total_max
;
841 fu_header
.fud_user_count
= ldi_usage_count();
842 bcount
= fu_data_size(0);
843 if (copyout(&fu_header
, (void *)userbp
, bcount
))
848 /* get the vnode for the file we want to look up usage for */
849 error
= lookupname(path
, UIO_USERSPACE
, FOLLOW
, NULLVPP
, &fvp
);
853 fvp_isdev
= vn_matchops(fvp
, spec_getvnodeops());
856 * if we want to report usage for all files contained within a
857 * file system then the target file better correspond to the
858 * root node of a mounted file system, or the root of a zone.
860 if (contained
&& !(fvp
->v_flag
& VROOT
) &&
861 fvp
!= curproc
->p_zone
->zone_rootvp
) {
867 * if we want to report usage for all files contained within a
868 * file system then the target file better not be a device.
870 if (contained
&& fvp_isdev
) {
876 * if we want to report usage for a device instance then the
877 * target file better corrospond to a device
879 if (dip_usage
&& !fvp_isdev
) {
885 * if the target vnode isn't a device and it has a reference count
886 * of one then no one else is going to have it open so we don't
887 * have any work to do.
889 if (!fvp_isdev
&& (fvp
->v_count
== 1)) {
893 /* look up usage information for this vnode */
894 fu_data
= dofusers(fvp
, flags
);
895 fuk_data
= dofkusers(fvp
, flags
, &error
);
899 /* get a count of the number of f_user_t's we need to copy out */
902 total_out
+= fu_data
->fud_user_count
;
904 total_out
+= fuk_data
->fud_user_count
;
906 /* check if there is enough space to copyout all results */
907 if (total_out
> total_max
) {
912 /* copyout file usage info counts */
913 fu_header
.fud_user_max
= total_max
;
914 fu_header
.fud_user_count
= total_out
;
915 bcount
= fu_data_size(0);
916 if (copyout(&fu_header
, (void *)userbp
, bcount
)) {
921 /* copyout userland process file usage info */
922 if ((fu_data
!= NULL
) && (fu_data
->fud_user_count
> 0)) {
924 bcount
= fu_data
->fud_user_count
* sizeof (f_user_t
);
925 if (copyout(fu_data
->fud_user
, (void *)userbp
, bcount
)) {
931 /* copyout kernel file usage info */
932 if ((fuk_data
!= NULL
) && (fuk_data
->fud_user_count
> 0)) {
934 bcount
= fuk_data
->fud_user_count
* sizeof (f_user_t
);
935 if (copyout(fuk_data
->fud_user
, (void *)userbp
, bcount
)) {
942 /* release the vnode that we were looking up usage for */
945 /* release any allocated memory */
947 kmem_free(fu_data
, fu_data_size(fu_data
->fud_user_max
));
949 kmem_free(fuk_data
, fu_data_size(fuk_data
->fud_user_max
));