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 #pragma ident "%Z%%M% %I% %E% SMI"
34 #include <sys/param.h>
35 #include <sys/inttypes.h>
36 #include <sys/types.h>
37 #include <sys/sysmacros.h>
38 #include <sys/systm.h>
40 #include <sys/errno.h>
42 #include <sys/vnode.h>
45 #include <sys/session.h>
47 #include <sys/utsname.h>
48 #include <sys/utssys.h>
49 #include <sys/ustat.h>
50 #include <sys/statvfs.h>
52 #include <sys/debug.h>
53 #include <sys/pathname.h>
54 #include <sys/modctl.h>
55 #include <sys/fs/snode.h>
56 #include <sys/sunldi_impl.h>
58 #include <sys/sunddi.h>
59 #include <sys/cmn_err.h>
60 #include <sys/ddipropdefs.h>
61 #include <sys/ddi_impldefs.h>
62 #include <sys/modctl.h>
63 #include <sys/flock.h>
64 #include <sys/share.h>
67 #include <vm/seg_vn.h>
68 #include <util/qsort.h>
74 static int uts_fusers(char *, int, intptr_t);
75 static int _statvfs64_by_dev(dev_t
, struct statvfs64
*);
77 #if defined(_ILP32) || defined(_SYSCALL32_IMPL)
79 static int utssys_uname32(caddr_t
, rval_t
*);
80 static int utssys_ustat32(dev_t
, struct ustat32
*);
83 utssys32(void *buf
, int arg
, int type
, void *outbp
)
93 * This is an obsolete way to get the utsname structure
94 * (it only gives you the first 8 characters of each field!)
95 * uname(2) is the preferred and better interface.
97 error
= utssys_uname32(buf
, &rv
);
100 error
= utssys_ustat32(expldev((dev32_t
)arg
), buf
);
103 error
= uts_fusers(buf
, arg
, (intptr_t)outbp
);
110 return (error
== 0 ? rv
.r_vals
: (int64_t)set_errno(error
));
114 utssys_uname32(caddr_t buf
, rval_t
*rvp
)
116 if (copyout(utsname
.sysname
, buf
, 8))
119 if (subyte(buf
, 0) < 0)
122 if (copyout(uts_nodename(), buf
, 8))
125 if (subyte(buf
, 0) < 0)
128 if (copyout(utsname
.release
, buf
, 8))
131 if (subyte(buf
, 0) < 0)
134 if (copyout(utsname
.version
, buf
, 8))
137 if (subyte(buf
, 0) < 0)
140 if (copyout(utsname
.machine
, buf
, 8))
143 if (subyte(buf
, 0) < 0)
150 utssys_ustat32(dev_t dev
, struct ustat32
*cbuf
)
152 struct ustat32 ust32
;
153 struct statvfs64 stvfs
;
158 if ((error
= _statvfs64_by_dev(dev
, &stvfs
)) != 0)
161 fsbc64
= stvfs
.f_bfree
* (stvfs
.f_frsize
/ 512);
163 * Check to see if the number of free blocks can be expressed
164 * in 31 bits or whether the number of free files is more than
165 * can be expressed in 32 bits and is not -1 (UINT64_MAX). NFS
166 * Version 2 does not support the number of free files and
167 * hence will return -1. -1, when translated from a 32 bit
168 * quantity to an unsigned 64 bit quantity, turns into UINT64_MAX.
170 if (fsbc64
> INT32_MAX
||
171 (stvfs
.f_ffree
> UINT32_MAX
&& stvfs
.f_ffree
!= UINT64_MAX
))
174 ust32
.f_tfree
= (daddr32_t
)fsbc64
;
175 ust32
.f_tinode
= (ino32_t
)stvfs
.f_ffree
;
180 while (i
++ < sizeof (ust32
.f_fname
))
185 while (*cp
!= '\0' &&
186 (i
++ < sizeof (stvfs
.f_fstr
) - sizeof (ust32
.f_fpack
)))
188 (void) strncpy(ust32
.f_fpack
, cp
+ 1, sizeof (ust32
.f_fpack
));
190 if (copyout(&ust32
, cbuf
, sizeof (ust32
)))
195 #endif /* _ILP32 || _SYSCALL32_IMPL */
199 static int uts_ustat64(dev_t
, struct ustat
*);
202 utssys64(void *buf
, long arg
, int type
, void *outbp
)
211 error
= uts_ustat64((dev_t
)arg
, buf
);
214 error
= uts_fusers(buf
, (int)arg
, (intptr_t)outbp
);
221 return (error
== 0 ? rv
.r_vals
: (int64_t)set_errno(error
));
225 uts_ustat64(dev_t dev
, struct ustat
*cbuf
)
228 struct statvfs64 stvfs
;
233 if ((error
= _statvfs64_by_dev(dev
, &stvfs
)) != 0)
236 fsbc64
= stvfs
.f_bfree
* (stvfs
.f_frsize
/ 512);
237 ust
.f_tfree
= (daddr_t
)fsbc64
;
238 ust
.f_tinode
= (ino_t
)stvfs
.f_ffree
;
243 while (i
++ < sizeof (ust
.f_fname
))
248 while (*cp
!= '\0' &&
249 (i
++ < sizeof (stvfs
.f_fstr
) - sizeof (ust
.f_fpack
)))
251 (void) strncpy(ust
.f_fpack
, cp
+ 1, sizeof (ust
.f_fpack
));
253 if (copyout(&ust
, cbuf
, sizeof (ust
)))
261 * Utility routine for the ustat implementations.
262 * (If it wasn't for the 'find-by-dev_t' semantic of ustat(2), we could push
263 * this all out into userland, sigh.)
266 _statvfs64_by_dev(dev_t dev
, struct statvfs64
*svp
)
271 if ((vfsp
= vfs_dev2vfsp(dev
)) == NULL
) {
273 * See if it's the root of our zone.
275 vfsp
= curproc
->p_zone
->zone_rootvp
->v_vfsp
;
276 if (vfsp
->vfs_dev
== dev
) {
284 error
= VFS_STATVFS(vfsp
, svp
);
290 * Check if this pid has an NBMAND lock or share reservation
291 * on this vp. llp is a snapshoted list of all NBMAND locks
292 * set by this pid. Return 1 if there is an NBMAND lock else
296 proc_has_nbmand_on_vp(vnode_t
*vp
, pid_t pid
, locklist_t
*llp
)
299 * Any NBMAND lock held by the process on this vp?
302 if (llp
->ll_vp
== vp
) {
308 * Any NBMAND share reservation on the vp for this process?
310 return (proc_has_nbmand_share_on_vp(vp
, pid
));
314 dofusers(vnode_t
*fvp
, int flags
)
319 pid_t npids
, pidx
, *pidlist
;
320 int v_proc
= v
.v_proc
; /* max # of procs */
322 int contained
= (flags
& F_CONTAINED
);
323 int nbmandonly
= (flags
& F_NBMANDLIST
);
324 int dip_usage
= (flags
& F_DEVINFO
);
325 int fvp_isdev
= vn_matchops(fvp
, spec_getvnodeops());
326 zone_t
*zone
= curproc
->p_zone
;
327 int inglobal
= INGLOBALZONE(curproc
);
329 /* get a pointer to the file system containing this vnode */
333 /* allocate the data structure to return our results in */
334 fu_data
= kmem_alloc(fu_data_size(v_proc
), KM_SLEEP
);
335 fu_data
->fud_user_max
= v_proc
;
336 fu_data
->fud_user_count
= 0;
338 /* get a snapshot of all the pids we're going to check out */
339 pidlist
= kmem_alloc(v_proc
* sizeof (pid_t
), KM_SLEEP
);
340 mutex_enter(&pidlock
);
341 for (npids
= 0, prp
= practive
; prp
!= NULL
; prp
= prp
->p_next
) {
342 if (inglobal
|| prp
->p_zone
== zone
)
343 pidlist
[npids
++] = prp
->p_pid
;
345 mutex_exit(&pidlock
);
347 /* grab each process and check its file usage */
348 for (pidx
= 0; pidx
< npids
; pidx
++) {
349 locklist_t
*llp
= NULL
;
355 pid_t pid
= pidlist
[pidx
];
359 * grab prp->p_lock using sprlock()
360 * if sprlock() fails the process does not exists anymore
366 /* get the processes credential info in case we need it */
367 mutex_enter(&prp
->p_crlock
);
368 uid
= crgetruid(prp
->p_cred
);
369 mutex_exit(&prp
->p_crlock
);
372 * it's safe to drop p_lock here because we
373 * called sprlock() before and it set the SPRLOCK
374 * flag for the process so it won't go away.
376 mutex_exit(&prp
->p_lock
);
379 * now we want to walk a processes open file descriptors
380 * to do this we need to grab the fip->fi_lock. (you
381 * can't hold p_lock when grabbing the fip->fi_lock.)
384 mutex_enter(&fip
->fi_lock
);
387 * Snapshot nbmand locks for pid
389 llp
= flk_active_nbmand_locks(prp
->p_pid
);
390 for (i
= 0; i
< fip
->fi_nfiles
; i
++) {
394 UF_ENTER(ufp
, fip
, i
);
395 if (((fp
= ufp
->uf_file
) == NULL
) ||
396 ((vp
= fp
->f_vnode
) == NULL
)) {
402 * if the target file (fvp) is not a device
403 * and corrosponds to the root of a filesystem
404 * (cvfsp), then check if it contains the file
405 * is use by this process (vp).
407 if (contained
&& (vp
->v_vfsp
== cvfsp
))
411 * if the target file (fvp) is not a device,
412 * then check if it matches the file in use
413 * by this process (vp).
415 if (!fvp_isdev
&& VN_CMP(fvp
, vp
))
419 * if the target file (fvp) is a device,
420 * then check if the current file in use
421 * by this process (vp) maps to the same device
425 vn_matchops(vp
, spec_getvnodeops()) &&
426 (fvp
->v_rdev
== vp
->v_rdev
))
430 * if the target file (fvp) is a device,
431 * and we're checking for device instance
432 * usage, then check if the current file in use
433 * by this process (vp) maps to the same device
437 vn_matchops(vp
, spec_getvnodeops()) &&
438 (VTOCS(fvp
)->s_dip
== VTOCS(vp
)->s_dip
))
442 * if the current file in use by this process (vp)
443 * doesn't match what we're looking for, move on
444 * to the next file in the process.
446 if ((use_flag
& F_OPEN
) == 0) {
451 if (proc_has_nbmand_on_vp(vp
, prp
->p_pid
, llp
)) {
452 /* A nbmand found so we're done. */
460 flk_free_locklist(llp
);
462 mutex_exit(&fip
->fi_lock
);
465 * If nbmand usage tracking is desired and no nbmand was
466 * found for this process, then no need to do further
467 * usage tracking for this process.
469 if (nbmandonly
&& (!(use_flag
& F_NBM
))) {
471 * grab the process lock again, clear the SPRLOCK
472 * flag, release the process, and continue.
474 mutex_enter(&prp
->p_lock
);
480 * All other types of usage.
481 * For the next few checks we need to hold p_lock.
483 mutex_enter(&prp
->p_lock
);
487 * if the target file (fvp) is a device
488 * then check if it matches the processes tty
490 * we grab s_lock to protect ourselves against
491 * freectty() freeing the vnode out from under us.
494 mutex_enter(&sp
->s_lock
);
495 vp
= prp
->p_sessp
->s_vp
;
497 if (fvp
->v_rdev
== vp
->v_rdev
)
501 (VTOCS(fvp
)->s_dip
== VTOCS(vp
)->s_dip
))
504 mutex_exit(&sp
->s_lock
);
506 /* check the processes current working directory */
508 (VN_CMP(fvp
, up
->u_cdir
) ||
509 (contained
&& (up
->u_cdir
->v_vfsp
== cvfsp
))))
512 /* check the processes root directory */
514 (VN_CMP(fvp
, up
->u_rdir
) ||
515 (contained
&& (up
->u_rdir
->v_vfsp
== cvfsp
))))
518 /* check the program text vnode */
520 (VN_CMP(fvp
, prp
->p_exec
) ||
521 (contained
&& (prp
->p_exec
->v_vfsp
== cvfsp
))))
525 /* Now we can drop p_lock again */
526 mutex_exit(&prp
->p_lock
);
529 * now we want to walk a processes memory mappings.
530 * to do this we need to grab the prp->p_as lock. (you
531 * can't hold p_lock when grabbing the prp->p_as lock.)
533 if (prp
->p_as
!= &kas
) {
535 struct as
*as
= prp
->p_as
;
537 AS_LOCK_ENTER(as
, &as
->a_lock
, RW_READER
);
538 for (seg
= AS_SEGFIRST(as
); seg
;
539 seg
= AS_SEGNEXT(as
, seg
)) {
541 * if we can't get a backing vnode for this
542 * segment then skip it
545 if ((SEGOP_GETVP(seg
, seg
->s_base
, &vp
)) ||
550 * if the target file (fvp) is not a device
551 * and corrosponds to the root of a filesystem
552 * (cvfsp), then check if it contains the
553 * vnode backing this segment (vp).
555 if (contained
&& (vp
->v_vfsp
== cvfsp
)) {
561 * if the target file (fvp) is not a device,
562 * check if it matches the the vnode backing
565 if (!fvp_isdev
&& VN_CMP(fvp
, vp
)) {
571 * if the target file (fvp) isn't a device,
572 * or the the vnode backing this segment (vp)
573 * isn't a device then continue.
576 !vn_matchops(vp
, spec_getvnodeops()))
580 * check if the vnode backing this segment
581 * (vp) maps to the same device minor node
582 * as the target device (fvp)
584 if (fvp
->v_rdev
== vp
->v_rdev
) {
590 * if we're checking for device instance
591 * usage, then check if the vnode backing
592 * this segment (vp) maps to the same device
593 * instance as the target device (fvp).
596 (VTOCS(fvp
)->s_dip
== VTOCS(vp
)->s_dip
)) {
601 AS_LOCK_EXIT(as
, &as
->a_lock
);
605 ASSERT(pcnt
< fu_data
->fud_user_max
);
606 fu_data
->fud_user
[pcnt
].fu_flags
= use_flag
;
607 fu_data
->fud_user
[pcnt
].fu_pid
= pid
;
608 fu_data
->fud_user
[pcnt
].fu_uid
= uid
;
613 * grab the process lock again, clear the SPRLOCK
614 * flag, release the process, and continue.
616 mutex_enter(&prp
->p_lock
);
620 kmem_free(pidlist
, v_proc
* sizeof (pid_t
));
622 fu_data
->fud_user_count
= pcnt
;
626 typedef struct dofkusers_arg
{
634 dofkusers_walker(const ldi_usage_t
*ldi_usage
, void *arg
)
636 dofkusers_arg_t
*dofkusers_arg
= (dofkusers_arg_t
*)arg
;
638 vnode_t
*fvp
= dofkusers_arg
->fvp
;
639 int flags
= dofkusers_arg
->flags
;
640 int *error
= dofkusers_arg
->error
;
641 fu_data_t
*fu_data
= dofkusers_arg
->fu_data
;
646 int dip_usage
= (flags
& F_DEVINFO
);
649 ASSERT(vn_matchops(fvp
, spec_getvnodeops()));
652 * check if the dev_t of the target device matches the dev_t
653 * of the device we're trying to find usage info for.
655 if (fvp
->v_rdev
!= ldi_usage
->tgt_devt
) {
658 * if the dev_ts don't match and we're not trying
659 * to find usage information for device instances
663 return (LDI_USAGE_CONTINUE
);
667 * we're trying to find usage information for an
668 * device instance instead of just a minor node.
670 * check if the dip for the target device matches the
671 * dip of the device we're trying to find usage info for.
673 if (VTOCS(fvp
)->s_dip
!= ldi_usage
->tgt_dip
)
674 return (LDI_USAGE_CONTINUE
);
677 if (fu_data
->fud_user_count
>= fu_data
->fud_user_max
) {
679 return (LDI_USAGE_TERMINATE
);
682 /* get the device vnode user information */
683 modid
= ldi_usage
->src_modid
;
686 minor
= instance
= -1;
687 if (ldi_usage
->src_dip
!= NULL
) {
688 instance
= DEVI(ldi_usage
->src_dip
)->devi_instance
;
690 if (ldi_usage
->src_devt
!= DDI_DEV_T_NONE
) {
691 minor
= getminor(ldi_usage
->src_devt
);
694 /* set the device vnode user information */
695 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_flags
= F_KERNEL
;
696 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_modid
= modid
;
697 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_instance
= instance
;
698 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_minor
= minor
;
700 fu_data
->fud_user_count
++;
702 return (LDI_USAGE_CONTINUE
);
706 f_user_cmp(const void *arg1
, const void *arg2
)
708 f_user_t
*f_user1
= (f_user_t
*)arg1
;
709 f_user_t
*f_user2
= (f_user_t
*)arg2
;
712 * we should only be called for f_user_t entires that represent
713 * a kernel file consumer
715 ASSERT(f_user1
->fu_flags
& F_KERNEL
);
716 ASSERT(f_user2
->fu_flags
& F_KERNEL
);
718 if (f_user1
->fu_modid
!= f_user2
->fu_modid
)
719 return ((f_user1
->fu_modid
< f_user2
->fu_modid
) ? -1 : 1);
721 if (f_user1
->fu_instance
!= f_user2
->fu_instance
)
722 return ((f_user1
->fu_instance
< f_user2
->fu_instance
) ? -1 : 1);
724 if (f_user1
->fu_minor
!= f_user2
->fu_minor
)
725 return ((f_user1
->fu_minor
< f_user2
->fu_minor
) ? -1 : 1);
731 dofkusers(vnode_t
*fvp
, int flags
, int *error
)
733 dofkusers_arg_t dofkusers_arg
;
738 * we only keep track of kernel device consumers, so if the
739 * target vnode isn't a device then there's nothing to do here
741 if (!vn_matchops(fvp
, spec_getvnodeops()))
744 /* allocate the data structure to return our results in */
745 user_max
= ldi_usage_count();
746 fu_data
= kmem_alloc(fu_data_size(user_max
), KM_SLEEP
);
747 fu_data
->fud_user_max
= user_max
;
748 fu_data
->fud_user_count
= 0;
750 /* invoke the callback to collect device usage information */
751 dofkusers_arg
.fvp
= fvp
;
752 dofkusers_arg
.flags
= flags
;
753 dofkusers_arg
.error
= error
;
754 dofkusers_arg
.fu_data
= fu_data
;
755 ldi_usage_walker(&dofkusers_arg
, dofkusers_walker
);
757 /* check for errors */
761 /* if there aren't any file consumers then return */
762 if (fu_data
->fud_user_count
== 0)
766 * since we ignore the spec_type of the target we're trying to
767 * access it's possible that we could have duplicates entries in
768 * the list of consumers.
770 * we don't want to check for duplicate in the callback because
771 * we're holding locks in the ldi when the callback is invoked.
773 * so here we need to go through the array of file consumers
774 * and remove duplicate entries.
777 /* first sort the array of file consumers */
778 qsort((caddr_t
)fu_data
->fud_user
, fu_data
->fud_user_count
,
779 sizeof (f_user_t
), f_user_cmp
);
781 /* then remove any duplicate entires */
783 while (i
< fu_data
->fud_user_count
) {
785 if (f_user_cmp(&fu_data
->fud_user
[i
],
786 &fu_data
->fud_user
[i
- 1]) != 0) {
788 * the current element is unique, move onto
796 * this entry is a duplicate so if it's not the last
797 * entry in the array then remove it.
799 fu_data
->fud_user_count
--;
800 if (i
== fu_data
->fud_user_count
)
803 bcopy(&fu_data
->fud_user
[i
+ 1], &fu_data
->fud_user
[i
],
804 sizeof (f_user_t
) * (fu_data
->fud_user_count
- i
));
811 * Determine the ways in which processes and the kernel are using a named
812 * file or mounted file system (path). Normally return 0. In case of an
813 * error appropriate errno will be returned.
815 * Upon success, uts_fusers will also copyout the file usage information
816 * in the form of an array of f_user_t's that are contained within an
817 * fu_data_t pointed to by userbp.
820 uts_fusers(char *path
, int flags
, intptr_t userbp
)
822 fu_data_t
*fu_data
= NULL
, *fuk_data
= NULL
;
827 int total_max
, total_out
;
828 int contained
= (flags
& F_CONTAINED
);
829 int dip_usage
= (flags
& F_DEVINFO
);
833 /* figure out how man f_user_t's we can safetly copy out */
834 if (copyin((const void *)userbp
, &total_max
, sizeof (total_max
)))
838 * check if we only want a count of how many kernel device
841 if (flags
& F_KINFO_COUNT
) {
842 fu_header
.fud_user_max
= total_max
;
843 fu_header
.fud_user_count
= ldi_usage_count();
844 bcount
= fu_data_size(0);
845 if (copyout(&fu_header
, (void *)userbp
, bcount
))
850 /* get the vnode for the file we want to look up usage for */
851 error
= lookupname(path
, UIO_USERSPACE
, FOLLOW
, NULLVPP
, &fvp
);
855 fvp_isdev
= vn_matchops(fvp
, spec_getvnodeops());
858 * if we want to report usage for all files contained within a
859 * file system then the target file better correspond to the
860 * root node of a mounted file system, or the root of a zone.
862 if (contained
&& !(fvp
->v_flag
& VROOT
) &&
863 fvp
!= curproc
->p_zone
->zone_rootvp
) {
869 * if we want to report usage for all files contained within a
870 * file system then the target file better not be a device.
872 if (contained
&& fvp_isdev
) {
878 * if we want to report usage for a device instance then the
879 * target file better corrospond to a device
881 if (dip_usage
&& !fvp_isdev
) {
887 * if the target vnode isn't a device and it has a reference count
888 * of one then no one else is going to have it open so we don't
889 * have any work to do.
891 if (!fvp_isdev
&& (fvp
->v_count
== 1)) {
895 /* look up usage information for this vnode */
896 fu_data
= dofusers(fvp
, flags
);
897 fuk_data
= dofkusers(fvp
, flags
, &error
);
901 /* get a count of the number of f_user_t's we need to copy out */
904 total_out
+= fu_data
->fud_user_count
;
906 total_out
+= fuk_data
->fud_user_count
;
908 /* check if there is enough space to copyout all results */
909 if (total_out
> total_max
) {
914 /* copyout file usage info counts */
915 fu_header
.fud_user_max
= total_max
;
916 fu_header
.fud_user_count
= total_out
;
917 bcount
= fu_data_size(0);
918 if (copyout(&fu_header
, (void *)userbp
, bcount
)) {
923 /* copyout userland process file usage info */
924 if ((fu_data
!= NULL
) && (fu_data
->fud_user_count
> 0)) {
926 bcount
= fu_data
->fud_user_count
* sizeof (f_user_t
);
927 if (copyout(fu_data
->fud_user
, (void *)userbp
, bcount
)) {
933 /* copyout kernel file usage info */
934 if ((fuk_data
!= NULL
) && (fuk_data
->fud_user_count
> 0)) {
936 bcount
= fuk_data
->fud_user_count
* sizeof (f_user_t
);
937 if (copyout(fuk_data
->fud_user
, (void *)userbp
, bcount
)) {
944 /* release the vnode that we were looking up usage for */
947 /* release any allocated memory */
949 kmem_free(fu_data
, fu_data_size(fu_data
->fud_user_max
));
951 kmem_free(fuk_data
, fu_data_size(fuk_data
->fud_user_max
));