./usr/man/man2/getsockopt.2 minix-sys
./usr/man/man2/gettimeofday.2 minix-sys
./usr/man/man2/getuid.2 minix-sys
+./usr/man/man2/getvfsstat.2 minix-sys
./usr/man/man2/intro.2 minix-sys
./usr/man/man2/ioctl.2 minix-sys
./usr/man/man2/kill.2 minix-sys
#define SYSUNAME 78
#define GETDENTS_321 80 /* to VFS */
#define LLSEEK 81 /* to VFS */
+#define GETVFSSTAT 82 /* to VFS */
#define STATVFS 83 /* to VFS */
#define FSTATVFS 84 /* to VFS */
#define SELECT 85 /* to VFS */
#define SEL_ERRORFDS m8_p3
#define SEL_TIMEOUT m8_p4
+/* Field names for the getvfsstat(2) call. */
+#define VFS_GETVFSSTAT_BUF m1_p1
+#define VFS_GETVFSSTAT_SIZE m1_i1
+#define VFS_GETVFSSTAT_FLAGS m1_i2
+
/* Field names for the fstatvfs call */
#define FSTATVFS_FD m1_i1
#define FSTATVFS_BUF m1_p1
#define SI_DATA_STORE 5 /* get copy of data store mappings */
#define SI_CALL_STATS 9 /* system call statistics */
#define SI_PROCPUB_TAB 11 /* copy of public entries of process table */
-#define SI_VMNT_TAB 12 /* get vmnt table */
#endif
#define getttyent _getttyent
#define getttynam _getttynam
#define getusershell _getusershell
+#define getvfsstat _getvfsstat
#define glob _glob
#define globfree _globfree
#define gmtime_r _gmtime_r
setrlimit
getrusage
getsid
-getvfsstat
issetugid /* WARNING: Always returns 0 in this impl. */
kevent
kqueue
getgroups.c getitimer.c setitimer.c __getlogin.c getpeername.c \
getpgrp.c getpid.c getppid.c priority.c getrlimit.c getsockname.c \
getsockopt.c setsockopt.c gettimeofday.c geteuid.c getuid.c \
+ getvfsstat.c \
ioctl.c issetugid.c kill.c link.c listen.c loadname.c lseek.c \
minix_rs.c mkdir.c mkfifo.c mknod.c mmap.c mount.c nanosleep.c \
open.c pathconf.c pipe.c poll.c pread.c ptrace.c pwrite.c \
--- /dev/null
+#include <sys/cdefs.h>
+#include <lib.h>
+#include "namespace.h"
+
+#include <sys/statvfs.h>
+
+#ifdef __weak_alias
+__weak_alias(getvfsstat, _getvfsstat)
+#endif
+
+int getvfsstat(struct statvfs *buf, size_t bufsize, int flags)
+{
+ message m;
+
+ m.VFS_GETVFSSTAT_BUF = (char *) buf;
+ m.VFS_GETVFSSTAT_SIZE = bufsize;
+ m.VFS_GETVFSSTAT_FLAGS = flags;
+ return(_syscall(VFS_PROC_NR, GETVFSSTAT, &m));
+}
connect.2 dup.2 execve.2 _exit.2 extattr_get_file.2 \
fcntl.2 fdatasync.2 fhopen.2 \
flock.2 fork.2 fsync.2 getcontext.2 getdents.2 \
- getfh.2 getvfsstat.2 getgid.2 getgroups.2 \
+ getfh.2 getgid.2 getgroups.2 \
getitimer.2 getlogin.2 getpeername.2 getpgrp.2 getpid.2 \
getpriority.2 getrlimit.2 getsid.2 getsockname.2 \
getsockopt.2 gettimeofday.2 getuid.2 intro.2 ioctl.2 issetugid.2 \
mprotect.2 mremap.2 msgctl.2 msgget.2 msgrcv.2 msgsnd.2 msync.2 \
munmap.2 nanosleep.2 nfssvc.2 ntp_adjtime.2 open.2 pathconf.2 pipe.2
.else
-MAN+= adjtime.2 clock_settime.2 pipe.2 getrusage.2
+MAN+= adjtime.2 clock_settime.2 getvfsstat.2 pipe.2 getrusage.2
.endif # !defined(__MINIX)
.if !defined(__MINIX)
MAN+= pmc_control.2 poll.2 posix_fadvise.2 profil.2 ptrace.2 __quotactl.2 \
# Import from sys-minix
.for i in access.c brk.c close.c environ.c execve.c fork.c \
- getgid.c getpid.c geteuid.c getuid.c gettimeofday.c loadname.c \
- link.c _mcontext.c mknod.c mmap.c nanosleep.c open.c \
+ getgid.c getpid.c geteuid.c getuid.c gettimeofday.c getvfsstat.c \
+ link.c loadname.c _mcontext.c mknod.c mmap.c nanosleep.c open.c \
read.c reboot.c sbrk.c select.c setuid.c sigprocmask.c stat.c \
stime.c syscall.c _ucontext.c umask.c unlink.c waitpid.c \
brksize.S _ipc.S _senda.S ucontext.S mmap.c init.c
no_sys, /* 79 = unused */
no_sys, /* 80 = (getdents) */
no_sys, /* 81 = unused */
- no_sys, /* 82 = unused */
+ no_sys, /* 82 = (getvfsstat) */
no_sys, /* 83 = unused */
no_sys, /* 84 = unused */
no_sys, /* 85 = (select) */
.include <bsd.own.mk>
PROG= procfs
-SRCS= buf.c main.c pid.c root.c tree.c util.c cpuinfo.c mounts.c
+SRCS= buf.c main.c pid.c root.c tree.c util.c cpuinfo.c
CPPFLAGS+= -I${NETBSDSRCDIR} -I${NETBSDSRCDIR}/servers
+++ /dev/null
-#include "inc.h"
-#include "vfs/vmnt.h"
-
-extern struct mproc mproc[NR_PROCS];
-
-/*===========================================================================*
- * root_mtab *
- *===========================================================================*/
-void
-root_mounts(void)
-{
- struct vmnt vmnt[NR_MNTS];
- struct vmnt *vmp;
- struct mproc *rmp;
- int slot;
-
- if (getsysinfo(VFS_PROC_NR, SI_VMNT_TAB, vmnt, sizeof(vmnt)) != OK)
- return;
-
- for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) {
- if (vmp->m_dev == NO_DEV)
- continue;
- if (vmp->m_fs_e == PFS_PROC_NR)
- continue; /* Skip (special case) */
-
- slot = _ENDPOINT_P(vmp->m_fs_e);
- if (slot < 0 || slot >= NR_PROCS)
- continue;
- rmp = &mproc[slot];
- buf_printf("%s on %s type %s (%s)\n", vmp->m_mount_dev,
- vmp->m_mount_path, rmp->mp_name,
- (vmp->m_flags & VMNT_READONLY) ? "ro" : "rw");
- }
-}
+++ /dev/null
-#ifndef __PROCFS_MOUNTS_H
-#define __PROCFS_MOUNTS_H__
-
-void root_mounts(void);
-
-#endif /* __PROCFS_MOUNTS_H__ */
#endif
#include <minix/dmap.h>
#include "cpuinfo.h"
-#include "mounts.h"
static void root_hz(void);
static void root_uptime(void);
#endif
static void root_dmap(void);
static void root_ipcvecs(void);
+static void root_mounts(void);
struct file root_files[] = {
{ "hz", REG_ALL_MODE, (data_t) root_hz },
PRINT_ENTRYPOINT(do_kernel_call);
}
+/*===========================================================================*
+ * root_mounts *
+ *===========================================================================*/
+static void
+root_mounts(void)
+{
+ struct statvfs buf[NR_MNTS];
+ int i, count;
+
+ if ((count = getvfsstat(buf, sizeof(buf), ST_NOWAIT)) < 0)
+ return;
+
+ for (i = 0; i < count; i++) {
+ buf_printf("%s on %s type %s (%s)\n", buf[i].f_mntfromname,
+ buf[i].f_mntonname, buf[i].f_fstypename,
+ (buf[i].f_flag & ST_RDONLY) ? "ro" : "rw");
+ }
+}
handled by VFS:
access, chdir, chmod, chown, chroot, close, creat, fchdir, fcntl, fstat,
-fstatvfs, fsync, ftruncate getdents, ioctl, link, llseek, lseek,
+fstatvfs, fsync, ftruncate, getdents, getvfsstat, ioctl, link, llseek, lseek,
lstat, mkdir, mknod, mount, open, pipe, read, readlink, rename, rmdir, select,
stat, statvfs, symlink, sync, truncate, umask, umount, unlink, utime, write.
| a file descriptor | getdents, ioctl, llseek, pipe, read, select, write |
| argument | |
+-------------------+---------------------------------------------------------+
-| System calls with | fsync++, sync, umask |
+| System calls with | fsync++, getvfsstat, sync, umask |
| other or no | |
| arguments | |
-------------------------------------------------------------------------------
| File rename | rename | VMNT_EXCL | Identical to file unlink |
| ops. | | | operations |
+-------------+--------------+------------+-----------------------------------+
-| Non-file | sync, umask | VMNT_READ | umask does not involve the file |
-| ops. | | or none | system, so it does not need |
+| Non-file | sync, umask, | VMNT_READ | umask does not involve the file |
+| ops. | getvfsstat | or none | system, so it does not need |
| | | | locks. sync does not alter state |
| | | | in VFS and is atomic at the FS |
-| | | | level |
+| | | | level. getvfsstat caches stats |
+| | | | only and requires no exclusion. |
-------------------------------------------------------------------------------
}}}
Table 5: System call without file descriptor argument sub-categorization
+++ /dev/null
-#ifndef __VFS_COMM_H__
-#define __VFS_COMM_H__
-
-/* VFS<->FS communication */
-
-typedef struct {
- int c_max_reqs; /* Max requests an FS can handle simultaneously */
- int c_cur_reqs; /* Number of requests the FS is currently handling */
- struct worker_thread *c_req_queue;/* Queue of procs waiting to send a message */
-} comm_t;
-
-#endif
#include "proto.h"
#include "threads.h"
#include "glo.h"
-#include "comm.h"
+#include "type.h"
#include "vmnt.h"
#endif
len = sizeof(calls_stats);
break;
#endif
- case SI_VMNT_TAB:
- fetch_vmnt_paths();
- src_addr = (vir_bytes) vmnt;
- len = sizeof(struct vmnt) * NR_MNTS;
- break;
default:
return(EINVAL);
}
char *label;
struct node_details res;
struct lookup resolve;
+ struct statvfs statvfs_buf;
/* Look up block device driver label when dev is not a pseudo-device */
label = "";
new_vmp->m_haspeek = 1;
}
+ /* Fill the statvfs cache with initial values. */
+ if (r == OK)
+ r = update_statvfs(new_vmp, &statvfs_buf);
+
if (r != OK) {
mark_vmnt_free(new_vmp);
unlock_vnode(root_node);
new_vmp->m_comm.c_max_reqs = VFS_FS_PROTO_CONREQS(new_vmp->m_proto);
new_vmp->m_comm.c_cur_reqs = 0;
+ /* No more blocking operations, so we can now report on this file system. */
+ new_vmp->m_flags |= VMNT_CANSTAT;
+
if (mount_root) {
/* Superblock and root node already read.
* Nothing else can go wrong. Perform the mount. */
return(EBUSY); /* can't umount a busy file system */
}
+ /* This FS will now disappear, so stop listing it in statistics. */
+ vmp->m_flags &= ~VMNT_CANSTAT;
+
/* Tell FS to drop all inode references for root inode except 1. */
vnode_clean_refs(vmp->m_root_node);
int do_stat(message *m_out);
int do_statvfs(message *m_out);
int do_fstatvfs(message *m_out);
+int do_getvfsstat(message *m_out);
int do_rdlink(message *m_out);
int do_lstat(message *m_out);
+int update_statvfs(struct vmnt *vmp, struct statvfs *buf);
/* time.c */
int do_utime(message *);
* do_fstat: perform the FSTAT system call
* do_statvfs: perform the STATVFS system call
* do_fstatvfs: perform the FSTATVFS system call
+ * do_getvfsstat: perform the GETVFSSTAT system call
*/
#include "fs.h"
return(r);
}
+/*===========================================================================*
+ * update_statvfs *
+ *===========================================================================*/
+int update_statvfs(struct vmnt *vmp, struct statvfs *buf)
+{
+/* Get statistics from a file system, and cache part of the results. */
+ int r;
+
+ if ((r = req_statvfs(vmp->m_fs_e, buf)) != OK)
+ return r;
+
+ vmp->m_stats.f_flag = buf->f_flag;
+ vmp->m_stats.f_bsize = buf->f_bsize;
+ vmp->m_stats.f_frsize = buf->f_frsize;
+ vmp->m_stats.f_iosize = buf->f_iosize;
+
+ vmp->m_stats.f_blocks = buf->f_blocks;
+ vmp->m_stats.f_bfree = buf->f_bfree;
+ vmp->m_stats.f_bavail = buf->f_bavail;
+ vmp->m_stats.f_bresvd = buf->f_bresvd;
+
+ vmp->m_stats.f_files = buf->f_files;
+ vmp->m_stats.f_ffree = buf->f_ffree;
+ vmp->m_stats.f_favail = buf->f_favail;
+ vmp->m_stats.f_fresvd = buf->f_fresvd;
+
+ vmp->m_stats.f_syncreads = buf->f_syncreads;
+ vmp->m_stats.f_syncwrites = buf->f_syncwrites;
+
+ vmp->m_stats.f_asyncreads = buf->f_asyncreads;
+ vmp->m_stats.f_asyncwrites = buf->f_asyncwrites;
+
+ vmp->m_stats.f_namemax = buf->f_namemax;
+
+ return OK;
+}
+
/*===========================================================================*
* fill_statvfs *
*===========================================================================*/
-static int fill_statvfs(struct vnode *vp, endpoint_t endpt, vir_bytes buf_addr)
+static int fill_statvfs(struct vmnt *vmp, endpoint_t endpt, vir_bytes buf_addr,
+ int flags)
{
/* Fill a statvfs structure in a userspace process. First let the target file
- * server (as identified by the given vnode) fill in most fields. Then fill in
- * some remaining fields with local information. Finally, copy the result to
- * user space.
+ * server fill in most fields, or use the cached copy if ST_NOWAIT is given.
+ * Then fill in some remaining fields with local information. Finally, copy
+ * the result to user space.
*/
struct statvfs buf;
- struct vmnt *vmp;
- int r;
- vmp = vp->v_vmnt;
+ if (!(flags & ST_NOWAIT)) {
+ /* Get fresh statistics from the file system. */
+ if (update_statvfs(vmp, &buf) != OK)
+ return EIO;
+ } else {
+ /* Use the cached statistics. */
+ memset(&buf, 0, sizeof(buf));
- if ((r = req_statvfs(vp->v_fs_e, &buf)) != OK)
- return r;
+ buf.f_flag = vmp->m_stats.f_flag;
+ buf.f_bsize = vmp->m_stats.f_bsize;
+ buf.f_frsize = vmp->m_stats.f_frsize;
+ buf.f_iosize = vmp->m_stats.f_iosize;
+
+ buf.f_blocks = vmp->m_stats.f_blocks;
+ buf.f_bfree = vmp->m_stats.f_bfree;
+ buf.f_bavail = vmp->m_stats.f_bavail;
+ buf.f_bresvd = vmp->m_stats.f_bresvd;
+
+ buf.f_files = vmp->m_stats.f_files;
+ buf.f_ffree = vmp->m_stats.f_ffree;
+ buf.f_favail = vmp->m_stats.f_favail;
+ buf.f_fresvd = vmp->m_stats.f_fresvd;
+
+ buf.f_syncreads = vmp->m_stats.f_syncreads;
+ buf.f_syncwrites = vmp->m_stats.f_syncwrites;
+
+ buf.f_asyncreads = vmp->m_stats.f_asyncreads;
+ buf.f_asyncwrites = vmp->m_stats.f_asyncwrites;
+
+ buf.f_namemax = vmp->m_stats.f_namemax;
+ }
if (vmp->m_flags & VMNT_READONLY)
buf.f_flag |= ST_RDONLY;
if (fetch_name(vname1, vname1_length, fullpath) != OK) return(err_code);
if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code);
- r = fill_statvfs(vp, who_e, statbuf);
+ r = fill_statvfs(vp->v_vmnt, who_e, statbuf, ST_WAIT);
unlock_vnode(vp);
unlock_vmnt(vmp);
/* Is the file descriptor valid? */
if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code);
- r = fill_statvfs(rfilp->filp_vno, who_e, statbuf);
+ r = fill_statvfs(rfilp->filp_vno->v_vmnt, who_e, statbuf, ST_WAIT);
unlock_filp(rfilp);
return(r);
}
+/*===========================================================================*
+ * do_getvfsstat *
+ *===========================================================================*/
+int do_getvfsstat(message *UNUSED(m_out))
+{
+/* Perform the getvfsstat(buf, bufsize, flags) system call. */
+ struct vmnt *vmp;
+ vir_bytes buf;
+ size_t bufsize;
+ int r, flags, count, do_lock;
+
+ buf = (vir_bytes) job_m_in.VFS_GETVFSSTAT_BUF;
+ bufsize = job_m_in.VFS_GETVFSSTAT_SIZE;
+ flags = job_m_in.VFS_GETVFSSTAT_FLAGS;
+
+ count = 0;
+
+ if (buf != 0) {
+ /* We only need to lock target file systems if we are going to query
+ * them. This will only happen if ST_NOWAIT is not given. If we do
+ * not lock, we rely on the VMNT_CANSTAT flag to protect us from
+ * concurrent (un)mount operations. Note that procfs relies on
+ * ST_NOWAIT calls being lock free, as it is a file system itself.
+ */
+ do_lock = !(flags & ST_NOWAIT);
+
+ for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) {
+ /* If there is no more space, return the count so far. */
+ if (bufsize < sizeof(struct statvfs))
+ break;
+
+ /* Lock the file system before checking any fields. */
+ if (do_lock && (r = lock_vmnt(vmp, VMNT_READ)) != OK)
+ return r;
+
+ /* Obtain information for this file system, if it is in use and
+ * can be reported. File systems that are being (un)mounted
+ * are skipped, as is PFS. The fill call will block only if
+ * ST_NOWAIT was not given.
+ */
+ if (vmp->m_dev != NO_DEV && (vmp->m_flags & VMNT_CANSTAT)) {
+ if ((r = fill_statvfs(vmp, who_e, buf, flags)) != OK) {
+ if (do_lock)
+ unlock_vmnt(vmp);
+
+ return r;
+ }
+
+ count++;
+ buf += sizeof(struct statvfs);
+ bufsize -= sizeof(struct statvfs);
+ }
+
+ if (do_lock)
+ unlock_vmnt(vmp);
+ }
+ } else {
+ /* Just report a file system count. No need to lock, as above. */
+ for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; vmp++) {
+ if (vmp->m_dev != NO_DEV && (vmp->m_flags & VMNT_CANSTAT))
+ count++;
+ }
+ }
+
+ return count;
+}
+
/*===========================================================================*
* do_lstat *
*===========================================================================*/
no_sys, /* 79 = unused */
do_getdents, /* 80 = getdents_321 (to be phased out) */
do_lseek, /* 81 = llseek */
- no_sys, /* 82 = unused */
+ do_getvfsstat, /* 82 = getvfsstat */
do_statvfs, /* 83 = fstatvfs */
do_fstatvfs, /* 84 = statvfs */
do_select, /* 85 = select */
--- /dev/null
+#ifndef __VFS_TYPE_H__
+#define __VFS_TYPE_H__
+
+/* VFS<->FS communication */
+
+typedef struct {
+ int c_max_reqs; /* Max requests an FS can handle simultaneously */
+ int c_cur_reqs; /* Number of requests the FS is currently handling */
+ struct worker_thread *c_req_queue;/* Queue of procs waiting to send a message */
+} comm_t;
+
+/*
+ * Cached statvfs fields. We are not using struct statvfs itself because that
+ * would add over 2K of unused memory per mount table entry.
+ */
+struct statvfs_cache {
+ unsigned long f_flag; /* copy of mount exported flags */
+ unsigned long f_bsize; /* file system block size */
+ unsigned long f_frsize; /* fundamental file system block size */
+ unsigned long f_iosize; /* optimal file system block size */
+
+ fsblkcnt_t f_blocks; /* number of blocks in file system, */
+ fsblkcnt_t f_bfree; /* free blocks avail in file system */
+ fsblkcnt_t f_bavail; /* free blocks avail to non-root */
+ fsblkcnt_t f_bresvd; /* blocks reserved for root */
+
+ fsfilcnt_t f_files; /* total file nodes in file system */
+ fsfilcnt_t f_ffree; /* free file nodes in file system */
+ fsfilcnt_t f_favail; /* free file nodes avail to non-root */
+ fsfilcnt_t f_fresvd; /* file nodes reserved for root */
+
+ uint64_t f_syncreads; /* count of sync reads since mount */
+ uint64_t f_syncwrites; /* count of sync writes since mount */
+
+ uint64_t f_asyncreads; /* count of async reads since mount */
+ uint64_t f_asyncwrites; /* count of async writes since mount */
+
+ unsigned long f_namemax; /* maximum filename length */
+};
+
+#endif
#define __VFS_VMNT_H__
#include "tll.h"
-#include "comm.h"
+#include "type.h"
EXTERN struct vmnt {
int m_fs_e; /* FS process' kernel endpoint */
char m_mount_dev[PATH_MAX]; /* device from which vmnt is mounted */
char m_fstype[FSTYPE_MAX]; /* file system type */
int m_haspeek; /* supports REQ_PEEK, REQ_BPEEK */
+ struct statvfs_cache m_stats; /* cached file system statistics */
} vmnt[NR_MNTS];
/* vmnt flags */
#define VMNT_CALLBACK 02 /* FS did back call */
#define VMNT_MOUNTING 04 /* Device is being mounted */
#define VMNT_FORCEROOTBSF 010 /* Force usage of none-device */
+#define VMNT_CANSTAT 020 /* Include FS in getvfsstat output */
/* vmnt lock types mapping */
#define VMNT_READ TLL_READ
#define ST_NOTRUNC __MNT_UNUSED1
#endif /* !__minix*/
+#define ST_WAIT MNT_WAIT
+#define ST_NOWAIT MNT_NOWAIT
+
__BEGIN_DECLS
int statvfs(const char *__restrict, struct statvfs *__restrict);
int fstatvfs(int, struct statvfs *);
+int getvfsstat(struct statvfs *, size_t, int);
__END_DECLS
#endif /* !_SYS_STATVFS_H_ */