VFS accounting: add a sync command
authorFrancois Tigeot <ftigeot@wolfpond.org>
Thu, 1 Dec 2011 12:43:23 +0000 (13:43 +0100)
committerFran├žois Tigeot <ftigeot@wolfpond.org>
Thu, 15 Dec 2011 12:55:32 +0000 (13:55 +0100)
* 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
sbin/vquota/vquota.c
sys/kern/vfs_quota.c

index e199315..e01ad19 100644 (file)
@@ -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
index fa5c756..eaf6358 100644 (file)
@@ -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; i<ACCT_CHUNK_NIDS; i++) {
+               if (unp->uid_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; i<ACCT_CHUNK_NIDS; i++) {
+               if (gnp->gid_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);
 }
index 37168c9..43efeff 100644 (file)
@@ -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));