From 88c2e66cfe68e219355f6390a9de2190d1f9b3c5 Mon Sep 17 00:00:00 2001 From: Francois Tigeot Date: Thu, 1 Dec 2011 13:43:23 +0100 Subject: [PATCH] VFS accounting: add a sync command * Kernel usage counter are not stored permanently on the filesystem, they need to be properly initialized from userland to return meaningful values --- sbin/vquota/vquota.8 | 8 ++++ sbin/vquota/vquota.c | 110 ++++++++++++++++++++++++++++++++++++++----- sys/kern/vfs_quota.c | 62 +++++++++++++++++++++++- 3 files changed, 167 insertions(+), 13 deletions(-) diff --git a/sbin/vquota/vquota.8 b/sbin/vquota/vquota.8 index e199315457..e01ad195e0 100644 --- a/sbin/vquota/vquota.8 +++ b/sbin/vquota/vquota.8 @@ -72,6 +72,14 @@ List mounted file systems having space accounting enabled Show file size usage for the file system mounted under .Ar mount_point . The information is broken down by uid and gid +. +.\" ==== sync ==== +.It Cm sync Ar mount_point +Initialize in one go the usage counters for the filesystem +mounted under +.Ar mount_point +with real usage data. This command scans an entire filesystem +directory hierarchy and may be slow to run .El . .Sh EXAMPLES diff --git a/sbin/vquota/vquota.c b/sbin/vquota/vquota.c index fa5c7561f2..eaf6358874 100644 --- a/sbin/vquota/vquota.c +++ b/sbin/vquota/vquota.c @@ -60,6 +60,7 @@ usage(int retcode) fprintf(stderr, "usage: vquota [-Dh] check directory\n"); fprintf(stderr, " vquota [-Dh] lsfs\n"); fprintf(stderr, " vquota [-Dh] show mount_point\n"); + fprintf(stderr, " vquota [-Dh] sync mount_point\n"); exit(retcode); } @@ -164,7 +165,10 @@ hl_register(ino_t inode) return retval; } -/* storage for collected uid numbers */ +/* global variable used by get_dir_size() */ +uint64_t global_size; + +/* storage for collected id numbers */ /* FIXME: same data structures used in kernel, should find a way to * deduplicate this code */ @@ -242,13 +246,18 @@ gnode_insert(gid_t gid) return gnp; } +/* + * get_dirsize(): walks a directory tree in the same filesystem + * output: + * - global rb-trees ac_uroot and ac_groot + * - global variable global_size + */ static int get_dirsize(char* dirname) { FTS *fts; FTSENT *p; char* fts_args[2]; - uint64_t global_size = 0; int retval = 0; /* what we need */ @@ -259,9 +268,6 @@ get_dirsize(char* dirname) struct ac_unode *unp, ufind; struct ac_gnode *gnp, gfind; - int i; - char hbuf[5]; - uint32_t uid, gid; /* TODO: check directory name sanity */ fts_args[0] = dirname; @@ -309,6 +315,20 @@ get_dirsize(char* dirname) } fts_close(fts); + return retval; +} + +static int +cmd_check(char* dirname) +{ + int32_t uid, gid; + char hbuf[5]; + struct ac_unode *unp; + struct ac_gnode *gnp; + int rv, i; + + rv = get_dirsize(dirname); + if (flag_humanize) { humanize_number(hbuf, sizeof(hbuf), global_size, "", HN_AUTOSCALE, HN_NOSPACE); @@ -345,7 +365,7 @@ get_dirsize(char* dirname) } } - return retval; + return rv; } /* print a list of filesystems with accounting enabled */ @@ -375,7 +395,7 @@ get_fslist(void) static bool send_command(const char *path, const char *cmd, - prop_dictionary_t args, prop_dictionary_t *res) + prop_object_t args, prop_dictionary_t *res) { prop_dictionary_t dict; struct plistref pref; @@ -447,7 +467,10 @@ show_mp(char *path) args = prop_dictionary_create(); res = prop_dictionary_create(); if (args == NULL) - printf("couldn't create args dictionary\n"); + printf("show_mp(): couldn't create args dictionary\n"); + res = prop_dictionary_create(); + if (res == NULL) + printf("show_mp(): couldn't create res dictionary\n"); rv = send_command(path, "get usage all", args, &res); if (rv == false) { @@ -455,7 +478,7 @@ show_mp(char *path) goto end; } - reslist = prop_dictionary_get(res, "get usage all"); + reslist = prop_dictionary_get(res, "returned data"); if (reslist == NULL) { printf("show_mp(): failed to get array of results"); rv = false; @@ -476,7 +499,7 @@ show_mp(char *path) else if (prop_dictionary_get_uint32(item, "gid", &id)) printf("gid %u:", id); else - printf("total space used"); + printf("total:"); if (flag_humanize) { humanize_number(hbuf, sizeof(hbuf), space, "", HN_AUTOSCALE, HN_NOSPACE); printf(" %s\n", hbuf); @@ -492,6 +515,66 @@ end: return (rv == true); } +/* sync the in-kernel counters to the actual file system usage */ +static int cmd_sync(char *dirname) +{ + prop_dictionary_t res, item; + prop_array_t args; + struct ac_unode *unp; + struct ac_gnode *gnp; + int rv = 0, i; + + args = prop_array_create(); + if (args == NULL) + printf("cmd_sync(): couldn't create args dictionary\n"); + res = prop_dictionary_create(); + if (res == NULL) + printf("cmd_sync(): couldn't create res dictionary\n"); + + rv = get_dirsize(dirname); + + item = prop_dictionary_create(); + if (item == NULL) + printf("cmd_sync(): couldn't create item dictionary\n"); + (void) prop_dictionary_set_uint64(item, "space used", global_size); + prop_array_add_and_rel(args, item); + + RB_FOREACH(unp, ac_utree, &ac_uroot) { + for (i=0; iuid_chunk[i] != 0) { + item = prop_dictionary_create(); + (void) prop_dictionary_set_uint32(item, "uid", + (unp->left_bits << ACCT_CHUNK_BITS) + i); + (void) prop_dictionary_set_uint64(item, "space used", + unp->uid_chunk[i]); + prop_array_add_and_rel(args, item); + } + } + } + RB_FOREACH(gnp, ac_gtree, &ac_groot) { + for (i=0; igid_chunk[i] != 0) { + item = prop_dictionary_create(); + (void) prop_dictionary_set_uint32(item, "gid", + (gnp->left_bits << ACCT_CHUNK_BITS) + i); + (void) prop_dictionary_set_uint64(item, "space used", + gnp->gid_chunk[i]); + prop_array_add_and_rel(args, item); + } + } + } + + if (send_command(dirname, "set usage all", args, &res) == false) { + printf("Failed to send message to kernel\n"); + rv = 1; + } + + prop_object_release(args); + prop_object_release(res); + + return rv; +} + int main(int argc, char **argv) { @@ -515,7 +598,7 @@ main(int argc, char **argv) if (strcmp(argv[0], "check") == 0) { if (argc != 2) usage(1); - return get_dirsize(argv[1]); + return cmd_check(argv[1]); } if (strcmp(argv[0], "lsfs") == 0) { return get_fslist(); @@ -525,6 +608,11 @@ main(int argc, char **argv) usage(1); return show_mp(argv[1]); } + if (strcmp(argv[0], "sync") == 0) { + if (argc != 2) + usage(1); + return cmd_sync(argv[1]); + } usage(0); } diff --git a/sys/kern/vfs_quota.c b/sys/kern/vfs_quota.c index 37168c9502..43efeffff1 100644 --- a/sys/kern/vfs_quota.c +++ b/sys/kern/vfs_quota.c @@ -210,13 +210,67 @@ cmd_get_usage_all(struct mount *mp, prop_array_t dict_out) } } +static int +cmd_set_usage_all(struct mount *mp, prop_array_t args) +{ + struct ac_unode ufind, *unp; + struct ac_gnode gfind, *gnp; + prop_dictionary_t item; + prop_object_iterator_t iter; + uint32_t id; + uint64_t space; + + spin_lock(&mp->mnt_acct.ac_spin); + /* 0. zero all statistics */ + /* we don't bother to free up memory, most of it would probably be + * re-allocated immediately anyway. just bzeroing the existing nodes + * is fine */ + mp->mnt_acct.ac_bytes = 0; + RB_FOREACH(unp, ac_utree, &mp->mnt_acct.ac_uroot) { + bzero(&unp->uid_chunk, sizeof(unp->uid_chunk)); + } + RB_FOREACH(gnp, ac_gtree, &mp->mnt_acct.ac_groot) { + bzero(&gnp->gid_chunk, sizeof(gnp->gid_chunk)); + } + + /* args contains an array of dict */ + iter = prop_array_iterator(args); + if (iter == NULL) { + kprintf("cmd_set_usage_all(): failed to create iterator\n"); + return 1; + } + while ((item = prop_object_iterator_next(iter)) != NULL) { + prop_dictionary_get_uint64(item, "space used", &space); + if (prop_dictionary_get_uint32(item, "uid", &id)) { + ufind.left_bits = (id >> ACCT_CHUNK_BITS); + unp = RB_FIND(ac_utree, &mp->mnt_acct.ac_uroot, &ufind); + if (unp == NULL) + unp = unode_insert(mp, id); + unp->uid_chunk[(id & ACCT_CHUNK_MASK)] = space; + } else if (prop_dictionary_get_uint32(item, "gid", &id)) { + gfind.left_bits = (id >> ACCT_CHUNK_BITS); + gnp = RB_FIND(ac_gtree, &mp->mnt_acct.ac_groot, &gfind); + if (gnp == NULL) + gnp = gnode_insert(mp, id); + gnp->gid_chunk[(id & ACCT_CHUNK_MASK)] = space; + } else { + mp->mnt_acct.ac_bytes = space; + } + } + prop_object_iterator_release(iter); + + spin_unlock(&mp->mnt_acct.ac_spin); + return 0; +} + int sys_vquotactl(struct vquotactl_args *vqa) /* const char *path, struct plistref *pref */ { const char *path; struct plistref pref; - prop_dictionary_t dict, args; + prop_dictionary_t dict; + prop_object_t args; char *cmd; prop_array_t pa_out; @@ -260,12 +314,16 @@ sys_vquotactl(struct vquotactl_args *vqa) cmd_get_usage_all(mp, pa_out); goto done; } + if (strcmp(cmd, "set usage all") == 0) { + error = cmd_set_usage_all(mp, args); + goto done; + } return EINVAL; done: /* kernel to userland */ dict = prop_dictionary_create(); - error = prop_dictionary_set(dict, "get usage all", pa_out); + error = prop_dictionary_set(dict, "returned data", pa_out); error = prop_dictionary_copyout(&pref, dict); error = copyout(&pref, vqa->pref, sizeof(pref)); -- 2.41.0