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_ustat32(dev_t
, struct ustat32
*);
80 utssys32(void *buf
, int arg
, int type
, void *outbp
)
89 error
= utssys_ustat32(expldev((dev32_t
)arg
), buf
);
92 error
= uts_fusers(buf
, arg
, (intptr_t)outbp
);
99 return (error
== 0 ? rv
.r_vals
: (int64_t)set_errno(error
));
103 utssys_ustat32(dev_t dev
, struct ustat32
*cbuf
)
105 struct ustat32 ust32
;
106 struct statvfs64 stvfs
;
111 if ((error
= _statvfs64_by_dev(dev
, &stvfs
)) != 0)
114 fsbc64
= stvfs
.f_bfree
* (stvfs
.f_frsize
/ 512);
116 * Check to see if the number of free blocks can be expressed
117 * in 31 bits or whether the number of free files is more than
118 * can be expressed in 32 bits and is not -1 (UINT64_MAX). NFS
119 * Version 2 does not support the number of free files and
120 * hence will return -1. -1, when translated from a 32 bit
121 * quantity to an unsigned 64 bit quantity, turns into UINT64_MAX.
123 if (fsbc64
> INT32_MAX
||
124 (stvfs
.f_ffree
> UINT32_MAX
&& stvfs
.f_ffree
!= UINT64_MAX
))
127 ust32
.f_tfree
= (daddr32_t
)fsbc64
;
128 ust32
.f_tinode
= (ino32_t
)stvfs
.f_ffree
;
133 while (i
++ < sizeof (ust32
.f_fname
))
138 while (*cp
!= '\0' &&
139 (i
++ < sizeof (stvfs
.f_fstr
) - sizeof (ust32
.f_fpack
)))
141 (void) strncpy(ust32
.f_fpack
, cp
+ 1, sizeof (ust32
.f_fpack
));
143 if (copyout(&ust32
, cbuf
, sizeof (ust32
)))
148 #endif /* _ILP32 || _SYSCALL32_IMPL */
152 static int uts_ustat64(dev_t
, struct ustat
*);
155 utssys64(void *buf
, long arg
, int type
, void *outbp
)
164 error
= uts_ustat64((dev_t
)arg
, buf
);
167 error
= uts_fusers(buf
, (int)arg
, (intptr_t)outbp
);
174 return (error
== 0 ? rv
.r_vals
: (int64_t)set_errno(error
));
178 uts_ustat64(dev_t dev
, struct ustat
*cbuf
)
181 struct statvfs64 stvfs
;
186 if ((error
= _statvfs64_by_dev(dev
, &stvfs
)) != 0)
189 fsbc64
= stvfs
.f_bfree
* (stvfs
.f_frsize
/ 512);
190 ust
.f_tfree
= (daddr_t
)fsbc64
;
191 ust
.f_tinode
= (ino_t
)stvfs
.f_ffree
;
196 while (i
++ < sizeof (ust
.f_fname
))
201 while (*cp
!= '\0' &&
202 (i
++ < sizeof (stvfs
.f_fstr
) - sizeof (ust
.f_fpack
)))
204 (void) strncpy(ust
.f_fpack
, cp
+ 1, sizeof (ust
.f_fpack
));
206 if (copyout(&ust
, cbuf
, sizeof (ust
)))
214 * Utility routine for the ustat implementations.
215 * (If it wasn't for the 'find-by-dev_t' semantic of ustat(2), we could push
216 * this all out into userland, sigh.)
219 _statvfs64_by_dev(dev_t dev
, struct statvfs64
*svp
)
224 if ((vfsp
= vfs_dev2vfsp(dev
)) == NULL
) {
226 * See if it's the root of our zone.
228 vfsp
= curproc
->p_zone
->zone_rootvp
->v_vfsp
;
229 if (vfsp
->vfs_dev
== dev
) {
237 error
= VFS_STATVFS(vfsp
, svp
);
243 * Check if this pid has an NBMAND lock or share reservation
244 * on this vp. llp is a snapshoted list of all NBMAND locks
245 * set by this pid. Return 1 if there is an NBMAND lock else
249 proc_has_nbmand_on_vp(vnode_t
*vp
, pid_t pid
, locklist_t
*llp
)
252 * Any NBMAND lock held by the process on this vp?
255 if (llp
->ll_vp
== vp
) {
261 * Any NBMAND share reservation on the vp for this process?
263 return (proc_has_nbmand_share_on_vp(vp
, pid
));
267 dofusers(vnode_t
*fvp
, int flags
)
272 pid_t npids
, pidx
, *pidlist
;
273 int v_proc
= v
.v_proc
; /* max # of procs */
275 int contained
= (flags
& F_CONTAINED
);
276 int nbmandonly
= (flags
& F_NBMANDLIST
);
277 int dip_usage
= (flags
& F_DEVINFO
);
278 int fvp_isdev
= vn_matchops(fvp
, spec_getvnodeops());
279 zone_t
*zone
= curproc
->p_zone
;
280 int inglobal
= INGLOBALZONE(curproc
);
282 /* get a pointer to the file system containing this vnode */
286 /* allocate the data structure to return our results in */
287 fu_data
= kmem_alloc(fu_data_size(v_proc
), KM_SLEEP
);
288 fu_data
->fud_user_max
= v_proc
;
289 fu_data
->fud_user_count
= 0;
291 /* get a snapshot of all the pids we're going to check out */
292 pidlist
= kmem_alloc(v_proc
* sizeof (pid_t
), KM_SLEEP
);
293 mutex_enter(&pidlock
);
294 for (npids
= 0, prp
= practive
; prp
!= NULL
; prp
= prp
->p_next
) {
295 if (inglobal
|| prp
->p_zone
== zone
)
296 pidlist
[npids
++] = prp
->p_pid
;
298 mutex_exit(&pidlock
);
300 /* grab each process and check its file usage */
301 for (pidx
= 0; pidx
< npids
; pidx
++) {
302 locklist_t
*llp
= NULL
;
308 pid_t pid
= pidlist
[pidx
];
312 * grab prp->p_lock using sprlock()
313 * if sprlock() fails the process does not exists anymore
319 /* get the processes credential info in case we need it */
320 mutex_enter(&prp
->p_crlock
);
321 uid
= crgetruid(prp
->p_cred
);
322 mutex_exit(&prp
->p_crlock
);
325 * it's safe to drop p_lock here because we
326 * called sprlock() before and it set the SPRLOCK
327 * flag for the process so it won't go away.
329 mutex_exit(&prp
->p_lock
);
332 * now we want to walk a processes open file descriptors
333 * to do this we need to grab the fip->fi_lock. (you
334 * can't hold p_lock when grabbing the fip->fi_lock.)
337 mutex_enter(&fip
->fi_lock
);
340 * Snapshot nbmand locks for pid
342 llp
= flk_active_nbmand_locks(prp
->p_pid
);
343 for (i
= 0; i
< fip
->fi_nfiles
; i
++) {
347 UF_ENTER(ufp
, fip
, i
);
348 if (((fp
= ufp
->uf_file
) == NULL
) ||
349 ((vp
= fp
->f_vnode
) == NULL
)) {
355 * if the target file (fvp) is not a device
356 * and corrosponds to the root of a filesystem
357 * (cvfsp), then check if it contains the file
358 * is use by this process (vp).
360 if (contained
&& (vp
->v_vfsp
== cvfsp
))
364 * if the target file (fvp) is not a device,
365 * then check if it matches the file in use
366 * by this process (vp).
368 if (!fvp_isdev
&& VN_CMP(fvp
, vp
))
372 * if the target file (fvp) is a device,
373 * then check if the current file in use
374 * by this process (vp) maps to the same device
378 vn_matchops(vp
, spec_getvnodeops()) &&
379 (fvp
->v_rdev
== vp
->v_rdev
))
383 * if the target file (fvp) is a device,
384 * and we're checking for device instance
385 * usage, then check if the current file in use
386 * by this process (vp) maps to the same device
390 vn_matchops(vp
, spec_getvnodeops()) &&
391 (VTOCS(fvp
)->s_dip
== VTOCS(vp
)->s_dip
))
395 * if the current file in use by this process (vp)
396 * doesn't match what we're looking for, move on
397 * to the next file in the process.
399 if ((use_flag
& F_OPEN
) == 0) {
404 if (proc_has_nbmand_on_vp(vp
, prp
->p_pid
, llp
)) {
405 /* A nbmand found so we're done. */
413 flk_free_locklist(llp
);
415 mutex_exit(&fip
->fi_lock
);
418 * If nbmand usage tracking is desired and no nbmand was
419 * found for this process, then no need to do further
420 * usage tracking for this process.
422 if (nbmandonly
&& (!(use_flag
& F_NBM
))) {
424 * grab the process lock again, clear the SPRLOCK
425 * flag, release the process, and continue.
427 mutex_enter(&prp
->p_lock
);
433 * All other types of usage.
434 * For the next few checks we need to hold p_lock.
436 mutex_enter(&prp
->p_lock
);
440 * if the target file (fvp) is a device
441 * then check if it matches the processes tty
443 * we grab s_lock to protect ourselves against
444 * freectty() freeing the vnode out from under us.
447 mutex_enter(&sp
->s_lock
);
448 vp
= prp
->p_sessp
->s_vp
;
450 if (fvp
->v_rdev
== vp
->v_rdev
)
454 (VTOCS(fvp
)->s_dip
== VTOCS(vp
)->s_dip
))
457 mutex_exit(&sp
->s_lock
);
459 /* check the processes current working directory */
461 (VN_CMP(fvp
, up
->u_cdir
) ||
462 (contained
&& (up
->u_cdir
->v_vfsp
== cvfsp
))))
465 /* check the processes root directory */
467 (VN_CMP(fvp
, up
->u_rdir
) ||
468 (contained
&& (up
->u_rdir
->v_vfsp
== cvfsp
))))
471 /* check the program text vnode */
473 (VN_CMP(fvp
, prp
->p_exec
) ||
474 (contained
&& (prp
->p_exec
->v_vfsp
== cvfsp
))))
478 /* Now we can drop p_lock again */
479 mutex_exit(&prp
->p_lock
);
482 * now we want to walk a processes memory mappings.
483 * to do this we need to grab the prp->p_as lock. (you
484 * can't hold p_lock when grabbing the prp->p_as lock.)
486 if (prp
->p_as
!= &kas
) {
488 struct as
*as
= prp
->p_as
;
490 AS_LOCK_ENTER(as
, RW_READER
);
491 for (seg
= AS_SEGFIRST(as
); seg
;
492 seg
= AS_SEGNEXT(as
, seg
)) {
494 * if we can't get a backing vnode for this
495 * segment then skip it
498 if ((segop_getvp(seg
, seg
->s_base
, &vp
)) ||
503 * if the target file (fvp) is not a device
504 * and corrosponds to the root of a filesystem
505 * (cvfsp), then check if it contains the
506 * vnode backing this segment (vp).
508 if (contained
&& (vp
->v_vfsp
== cvfsp
)) {
514 * if the target file (fvp) is not a device,
515 * check if it matches the the vnode backing
518 if (!fvp_isdev
&& VN_CMP(fvp
, vp
)) {
524 * if the target file (fvp) isn't a device,
525 * or the the vnode backing this segment (vp)
526 * isn't a device then continue.
529 !vn_matchops(vp
, spec_getvnodeops()))
533 * check if the vnode backing this segment
534 * (vp) maps to the same device minor node
535 * as the target device (fvp)
537 if (fvp
->v_rdev
== vp
->v_rdev
) {
543 * if we're checking for device instance
544 * usage, then check if the vnode backing
545 * this segment (vp) maps to the same device
546 * instance as the target device (fvp).
549 (VTOCS(fvp
)->s_dip
== VTOCS(vp
)->s_dip
)) {
558 ASSERT(pcnt
< fu_data
->fud_user_max
);
559 fu_data
->fud_user
[pcnt
].fu_flags
= use_flag
;
560 fu_data
->fud_user
[pcnt
].fu_pid
= pid
;
561 fu_data
->fud_user
[pcnt
].fu_uid
= uid
;
566 * grab the process lock again, clear the SPRLOCK
567 * flag, release the process, and continue.
569 mutex_enter(&prp
->p_lock
);
573 kmem_free(pidlist
, v_proc
* sizeof (pid_t
));
575 fu_data
->fud_user_count
= pcnt
;
579 typedef struct dofkusers_arg
{
587 dofkusers_walker(const ldi_usage_t
*ldi_usage
, void *arg
)
589 dofkusers_arg_t
*dofkusers_arg
= (dofkusers_arg_t
*)arg
;
591 vnode_t
*fvp
= dofkusers_arg
->fvp
;
592 int flags
= dofkusers_arg
->flags
;
593 int *error
= dofkusers_arg
->error
;
594 fu_data_t
*fu_data
= dofkusers_arg
->fu_data
;
599 int dip_usage
= (flags
& F_DEVINFO
);
602 ASSERT(vn_matchops(fvp
, spec_getvnodeops()));
605 * check if the dev_t of the target device matches the dev_t
606 * of the device we're trying to find usage info for.
608 if (fvp
->v_rdev
!= ldi_usage
->tgt_devt
) {
611 * if the dev_ts don't match and we're not trying
612 * to find usage information for device instances
616 return (LDI_USAGE_CONTINUE
);
620 * we're trying to find usage information for an
621 * device instance instead of just a minor node.
623 * check if the dip for the target device matches the
624 * dip of the device we're trying to find usage info for.
626 if (VTOCS(fvp
)->s_dip
!= ldi_usage
->tgt_dip
)
627 return (LDI_USAGE_CONTINUE
);
630 if (fu_data
->fud_user_count
>= fu_data
->fud_user_max
) {
632 return (LDI_USAGE_TERMINATE
);
635 /* get the device vnode user information */
636 modid
= ldi_usage
->src_modid
;
639 minor
= instance
= -1;
640 if (ldi_usage
->src_dip
!= NULL
) {
641 instance
= DEVI(ldi_usage
->src_dip
)->devi_instance
;
643 if (ldi_usage
->src_devt
!= DDI_DEV_T_NONE
) {
644 minor
= getminor(ldi_usage
->src_devt
);
647 /* set the device vnode user information */
648 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_flags
= F_KERNEL
;
649 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_modid
= modid
;
650 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_instance
= instance
;
651 fu_data
->fud_user
[fu_data
->fud_user_count
].fu_minor
= minor
;
653 fu_data
->fud_user_count
++;
655 return (LDI_USAGE_CONTINUE
);
659 f_user_cmp(const void *arg1
, const void *arg2
)
661 f_user_t
*f_user1
= (f_user_t
*)arg1
;
662 f_user_t
*f_user2
= (f_user_t
*)arg2
;
665 * we should only be called for f_user_t entires that represent
666 * a kernel file consumer
668 ASSERT(f_user1
->fu_flags
& F_KERNEL
);
669 ASSERT(f_user2
->fu_flags
& F_KERNEL
);
671 if (f_user1
->fu_modid
!= f_user2
->fu_modid
)
672 return ((f_user1
->fu_modid
< f_user2
->fu_modid
) ? -1 : 1);
674 if (f_user1
->fu_instance
!= f_user2
->fu_instance
)
675 return ((f_user1
->fu_instance
< f_user2
->fu_instance
) ? -1 : 1);
677 if (f_user1
->fu_minor
!= f_user2
->fu_minor
)
678 return ((f_user1
->fu_minor
< f_user2
->fu_minor
) ? -1 : 1);
684 dofkusers(vnode_t
*fvp
, int flags
, int *error
)
686 dofkusers_arg_t dofkusers_arg
;
691 * we only keep track of kernel device consumers, so if the
692 * target vnode isn't a device then there's nothing to do here
694 if (!vn_matchops(fvp
, spec_getvnodeops()))
697 /* allocate the data structure to return our results in */
698 user_max
= ldi_usage_count();
699 fu_data
= kmem_alloc(fu_data_size(user_max
), KM_SLEEP
);
700 fu_data
->fud_user_max
= user_max
;
701 fu_data
->fud_user_count
= 0;
703 /* invoke the callback to collect device usage information */
704 dofkusers_arg
.fvp
= fvp
;
705 dofkusers_arg
.flags
= flags
;
706 dofkusers_arg
.error
= error
;
707 dofkusers_arg
.fu_data
= fu_data
;
708 ldi_usage_walker(&dofkusers_arg
, dofkusers_walker
);
710 /* check for errors */
714 /* if there aren't any file consumers then return */
715 if (fu_data
->fud_user_count
== 0)
719 * since we ignore the spec_type of the target we're trying to
720 * access it's possible that we could have duplicates entries in
721 * the list of consumers.
723 * we don't want to check for duplicate in the callback because
724 * we're holding locks in the ldi when the callback is invoked.
726 * so here we need to go through the array of file consumers
727 * and remove duplicate entries.
730 /* first sort the array of file consumers */
731 qsort((caddr_t
)fu_data
->fud_user
, fu_data
->fud_user_count
,
732 sizeof (f_user_t
), f_user_cmp
);
734 /* then remove any duplicate entires */
736 while (i
< fu_data
->fud_user_count
) {
738 if (f_user_cmp(&fu_data
->fud_user
[i
],
739 &fu_data
->fud_user
[i
- 1]) != 0) {
741 * the current element is unique, move onto
749 * this entry is a duplicate so if it's not the last
750 * entry in the array then remove it.
752 fu_data
->fud_user_count
--;
753 if (i
== fu_data
->fud_user_count
)
756 bcopy(&fu_data
->fud_user
[i
+ 1], &fu_data
->fud_user
[i
],
757 sizeof (f_user_t
) * (fu_data
->fud_user_count
- i
));
764 * Determine the ways in which processes and the kernel are using a named
765 * file or mounted file system (path). Normally return 0. In case of an
766 * error appropriate errno will be returned.
768 * Upon success, uts_fusers will also copyout the file usage information
769 * in the form of an array of f_user_t's that are contained within an
770 * fu_data_t pointed to by userbp.
773 uts_fusers(char *path
, int flags
, intptr_t userbp
)
775 fu_data_t
*fu_data
= NULL
, *fuk_data
= NULL
;
780 int total_max
, total_out
;
781 int contained
= (flags
& F_CONTAINED
);
782 int dip_usage
= (flags
& F_DEVINFO
);
786 /* figure out how man f_user_t's we can safetly copy out */
787 if (copyin((const void *)userbp
, &total_max
, sizeof (total_max
)))
791 * check if we only want a count of how many kernel device
794 if (flags
& F_KINFO_COUNT
) {
795 fu_header
.fud_user_max
= total_max
;
796 fu_header
.fud_user_count
= ldi_usage_count();
797 bcount
= fu_data_size(0);
798 if (copyout(&fu_header
, (void *)userbp
, bcount
))
803 /* get the vnode for the file we want to look up usage for */
804 error
= lookupname(path
, UIO_USERSPACE
, FOLLOW
, NULLVPP
, &fvp
);
808 fvp_isdev
= vn_matchops(fvp
, spec_getvnodeops());
811 * if we want to report usage for all files contained within a
812 * file system then the target file better correspond to the
813 * root node of a mounted file system, or the root of a zone.
815 if (contained
&& !(fvp
->v_flag
& VROOT
) &&
816 fvp
!= curproc
->p_zone
->zone_rootvp
) {
822 * if we want to report usage for all files contained within a
823 * file system then the target file better not be a device.
825 if (contained
&& fvp_isdev
) {
831 * if we want to report usage for a device instance then the
832 * target file better corrospond to a device
834 if (dip_usage
&& !fvp_isdev
) {
840 * if the target vnode isn't a device and it has a reference count
841 * of one then no one else is going to have it open so we don't
842 * have any work to do.
844 if (!fvp_isdev
&& (fvp
->v_count
== 1)) {
848 /* look up usage information for this vnode */
849 fu_data
= dofusers(fvp
, flags
);
850 fuk_data
= dofkusers(fvp
, flags
, &error
);
854 /* get a count of the number of f_user_t's we need to copy out */
857 total_out
+= fu_data
->fud_user_count
;
859 total_out
+= fuk_data
->fud_user_count
;
861 /* check if there is enough space to copyout all results */
862 if (total_out
> total_max
) {
867 /* copyout file usage info counts */
868 fu_header
.fud_user_max
= total_max
;
869 fu_header
.fud_user_count
= total_out
;
870 bcount
= fu_data_size(0);
871 if (copyout(&fu_header
, (void *)userbp
, bcount
)) {
876 /* copyout userland process file usage info */
877 if ((fu_data
!= NULL
) && (fu_data
->fud_user_count
> 0)) {
879 bcount
= fu_data
->fud_user_count
* sizeof (f_user_t
);
880 if (copyout(fu_data
->fud_user
, (void *)userbp
, bcount
)) {
886 /* copyout kernel file usage info */
887 if ((fuk_data
!= NULL
) && (fuk_data
->fud_user_count
> 0)) {
889 bcount
= fuk_data
->fud_user_count
* sizeof (f_user_t
);
890 if (copyout(fuk_data
->fud_user
, (void *)userbp
, bcount
)) {
897 /* release the vnode that we were looking up usage for */
900 /* release any allocated memory */
902 kmem_free(fu_data
, fu_data_size(fu_data
->fud_user_max
));
904 kmem_free(fuk_data
, fu_data_size(fuk_data
->fud_user_max
));