binder: add a mount option to show global stats
authorHridya Valsaraju <hridya@google.com>
Tue, 3 Sep 2019 16:16:52 +0000 (09:16 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Sep 2019 11:31:13 +0000 (13:31 +0200)
Currently, all binder state and statistics live in debugfs.
We need this information even when debugfs is not mounted.
This patch adds the mount option 'stats' to enable a binderfs
instance to have binder debug information present in the same.
'stats=global' will enable the global binder statistics. In
the future, 'stats=local' will enable binder statistics local
to the binderfs instance. The two modes 'global' and 'local'
will be mutually exclusive. 'stats=global' option is only available
for a binderfs instance mounted in the initial user namespace.
An attempt to use the option to mount a binderfs instance in
another user namespace will return an EPERM error.

Signed-off-by: Hridya Valsaraju <hridya@google.com>
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
Link: https://lore.kernel.org/r/20190903161655.107408-2-hridya@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/android/binderfs.c

index 55c5adb..e54c99b 100644 (file)
@@ -51,18 +51,27 @@ static DEFINE_IDA(binderfs_minors);
 /**
  * binderfs_mount_opts - mount options for binderfs
  * @max: maximum number of allocatable binderfs binder devices
+ * @stats_mode: enable binder stats in binderfs.
  */
 struct binderfs_mount_opts {
        int max;
+       int stats_mode;
 };
 
 enum {
        Opt_max,
+       Opt_stats_mode,
        Opt_err
 };
 
+enum binderfs_stats_mode {
+       STATS_NONE,
+       STATS_GLOBAL,
+};
+
 static const match_table_t tokens = {
        { Opt_max, "max=%d" },
+       { Opt_stats_mode, "stats=%s" },
        { Opt_err, NULL     }
 };
 
@@ -290,8 +299,9 @@ static void binderfs_evict_inode(struct inode *inode)
 static int binderfs_parse_mount_opts(char *data,
                                     struct binderfs_mount_opts *opts)
 {
-       char *p;
+       char *p, *stats;
        opts->max = BINDERFS_MAX_MINOR;
+       opts->stats_mode = STATS_NONE;
 
        while ((p = strsep(&data, ",")) != NULL) {
                substring_t args[MAX_OPT_ARGS];
@@ -311,6 +321,22 @@ static int binderfs_parse_mount_opts(char *data,
 
                        opts->max = max_devices;
                        break;
+               case Opt_stats_mode:
+                       if (!capable(CAP_SYS_ADMIN))
+                               return -EINVAL;
+
+                       stats = match_strdup(&args[0]);
+                       if (!stats)
+                               return -ENOMEM;
+
+                       if (strcmp(stats, "global") != 0) {
+                               kfree(stats);
+                               return -EINVAL;
+                       }
+
+                       opts->stats_mode = STATS_GLOBAL;
+                       kfree(stats);
+                       break;
                default:
                        pr_err("Invalid mount options\n");
                        return -EINVAL;
@@ -322,8 +348,21 @@ static int binderfs_parse_mount_opts(char *data,
 
 static int binderfs_remount(struct super_block *sb, int *flags, char *data)
 {
+       int prev_stats_mode, ret;
        struct binderfs_info *info = sb->s_fs_info;
-       return binderfs_parse_mount_opts(data, &info->mount_opts);
+
+       prev_stats_mode = info->mount_opts.stats_mode;
+       ret = binderfs_parse_mount_opts(data, &info->mount_opts);
+       if (ret)
+               return ret;
+
+       if (prev_stats_mode != info->mount_opts.stats_mode) {
+               pr_err("Binderfs stats mode cannot be changed during a remount\n");
+               info->mount_opts.stats_mode = prev_stats_mode;
+               return -EINVAL;
+       }
+
+       return 0;
 }
 
 static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root)
@@ -333,6 +372,8 @@ static int binderfs_show_mount_opts(struct seq_file *seq, struct dentry *root)
        info = root->d_sb->s_fs_info;
        if (info->mount_opts.max <= BINDERFS_MAX_MINOR)
                seq_printf(seq, ",max=%d", info->mount_opts.max);
+       if (info->mount_opts.stats_mode == STATS_GLOBAL)
+               seq_printf(seq, ",stats=global");
 
        return 0;
 }