2019-08-27 20:44:27

by Hridya Valsaraju

[permalink] [raw]
Subject: [PATCH 0/4] Add binder state and statistics to binderfs

Currently, the only way to access binder state and
statistics is through debugfs. We need a way to
access the same even when debugfs is not mounted.
These patches add a mount option to make this
information available in binderfs without affecting
its presence in debugfs. The following debugfs nodes
will be made available in a binderfs instance when
mounted with the mount option 'stats=global' or 'stats=local'.

/sys/kernel/debug/binder/failed_transaction_log
/sys/kernel/debug/binder/proc
/sys/kernel/debug/binder/state
/sys/kernel/debug/binder/stats
/sys/kernel/debug/binder/transaction_log
/sys/kernel/debug/binder/transactions

Hridya Valsaraju (4):
binder: add a mount option to show global stats
binder: Add stats, state and transactions files
binder: Make transaction_log available in binderfs
binder: Add binder_proc logging to binderfs

drivers/android/binder.c | 87 +++++-----
drivers/android/binder_internal.h | 84 ++++++++++
drivers/android/binderfs.c | 256 ++++++++++++++++++++++++++----
3 files changed, 355 insertions(+), 72 deletions(-)

--
2.23.0.187.g17f5b7556c-goog


2019-08-27 20:44:53

by Hridya Valsaraju

[permalink] [raw]
Subject: [PATCH 1/4] binder: add a mount option to show global stats

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 <[email protected]>
---
drivers/android/binderfs.c | 47 ++++++++++++++++++++++++++++++++++++--
1 file changed, 45 insertions(+), 2 deletions(-)

diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
index cc2e71576396..d95d179aec58 100644
--- a/drivers/android/binderfs.c
+++ b/drivers/android/binderfs.c
@@ -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,24 @@ static int binderfs_parse_mount_opts(char *data,

opts->max = max_devices;
break;
+ case Opt_stats_mode:
+ stats = match_strdup(&args[0]);
+ if (!stats)
+ return -ENOMEM;
+
+ if (strcmp(stats, "global") != 0) {
+ kfree(stats);
+ return -EINVAL;
+ }
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ kfree(stats);
+ return -EINVAL;
+ }
+
+ opts->stats_mode = STATS_GLOBAL;
+ kfree(stats);
+ break;
default:
pr_err("Invalid mount options\n");
return -EINVAL;
@@ -322,8 +350,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_info("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 +374,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;
}
--
2.23.0.187.g17f5b7556c-goog

2019-08-27 20:45:10

by Hridya Valsaraju

[permalink] [raw]
Subject: [PATCH 4/4] binder: Add binder_proc logging to binderfs

Currently /sys/kernel/debug/binder/proc contains
the debug data for every binder_proc instance.
This patch makes this information also available
in a binderfs instance mounted with a mount option
"stats=global" in addition to debugfs. The patch does
not affect the presence of the file in debugfs.

If a binderfs instance is mounted at path /dev/binderfs,
this file would be present at /dev/binderfs/binder_logs/proc.
This change provides an alternate way to access this file when debugfs
is not mounted.

Signed-off-by: Hridya Valsaraju <[email protected]>
---
drivers/android/binder.c | 38 ++++++++++++++++++-
drivers/android/binder_internal.h | 46 ++++++++++++++++++++++
drivers/android/binderfs.c | 63 ++++++++++++++-----------------
3 files changed, 111 insertions(+), 36 deletions(-)

diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index bed217310197..37189838f32a 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -481,6 +481,7 @@ struct binder_priority {
* @inner_lock: can nest under outer_lock and/or node lock
* @outer_lock: no nesting under innor or node lock
* Lock order: 1) outer, 2) node, 3) inner
+ * @binderfs_entry: process-specific binderfs log file
*
* Bookkeeping structure for binder processes
*/
@@ -510,6 +511,7 @@ struct binder_proc {
struct binder_context *context;
spinlock_t inner_lock;
spinlock_t outer_lock;
+ struct dentry *binderfs_entry;
};

enum {
@@ -5347,6 +5349,8 @@ static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
struct binder_device *binder_dev;
+ struct binderfs_info *info;
+ struct dentry *binder_binderfs_dir_entry_proc = NULL;

binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__,
current->group_leader->pid, current->pid);
@@ -5368,11 +5372,14 @@ static int binder_open(struct inode *nodp, struct file *filp)
}

/* binderfs stashes devices in i_private */
- if (is_binderfs_device(nodp))
+ if (is_binderfs_device(nodp)) {
binder_dev = nodp->i_private;
- else
+ info = nodp->i_sb->s_fs_info;
+ binder_binderfs_dir_entry_proc = info->proc_log_dir;
+ } else {
binder_dev = container_of(filp->private_data,
struct binder_device, miscdev);
+ }
proc->context = &binder_dev->context;
binder_alloc_init(&proc->alloc);

@@ -5403,6 +5410,27 @@ static int binder_open(struct inode *nodp, struct file *filp)
&proc_fops);
}

+ if (binder_binderfs_dir_entry_proc) {
+ char strbuf[11];
+ struct dentry *binderfs_entry;
+
+ snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
+ /*
+ * Similar to debugfs, the process specific log file is shared
+ * between contexts. If the file has already been created for a
+ * process, the following binderfs_create_file() call will
+ * fail if another context of the same process invoked
+ * binder_open(). This is ok since same as debugfs,
+ * the log file will contain information on all contexts of a
+ * given PID.
+ */
+ binderfs_entry = binderfs_create_file(binder_binderfs_dir_entry_proc,
+ strbuf, &proc_fops, (void *)(unsigned long)proc->pid);
+ if (!IS_ERR(binderfs_entry))
+ proc->binderfs_entry = binderfs_entry;
+
+ }
+
return 0;
}

@@ -5442,6 +5470,12 @@ static int binder_release(struct inode *nodp, struct file *filp)
struct binder_proc *proc = filp->private_data;

debugfs_remove(proc->debugfs_entry);
+
+ if (proc->binderfs_entry) {
+ binderfs_remove_file(proc->binderfs_entry);
+ proc->binderfs_entry = NULL;
+ }
+
binder_defer_work(proc, BINDER_DEFERRED_RELEASE);

return 0;
diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h
index b9be42d9464c..bd47f7f72075 100644
--- a/drivers/android/binder_internal.h
+++ b/drivers/android/binder_internal.h
@@ -35,17 +35,63 @@ struct binder_device {
struct inode *binderfs_inode;
};

+/**
+ * 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;
+};
+
+/**
+ * binderfs_info - information about a binderfs mount
+ * @ipc_ns: The ipc namespace the binderfs mount belongs to.
+ * @control_dentry: This records the dentry of this binderfs mount
+ * binder-control device.
+ * @root_uid: uid that needs to be used when a new binder device is
+ * created.
+ * @root_gid: gid that needs to be used when a new binder device is
+ * created.
+ * @mount_opts: The mount options in use.
+ * @device_count: The current number of allocated binder devices.
+ * @proc_log_dir: Pointer to the directory dentry containing process-specific
+ * logs.
+ */
+struct binderfs_info {
+ struct ipc_namespace *ipc_ns;
+ struct dentry *control_dentry;
+ kuid_t root_uid;
+ kgid_t root_gid;
+ struct binderfs_mount_opts mount_opts;
+ int device_count;
+ struct dentry *proc_log_dir;
+};
+
extern const struct file_operations binder_fops;

extern char *binder_devices_param;

#ifdef CONFIG_ANDROID_BINDERFS
extern bool is_binderfs_device(const struct inode *inode);
+extern struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
+ const struct file_operations *fops,
+ void *data);
+extern void binderfs_remove_file(struct dentry *dentry);
#else
static inline bool is_binderfs_device(const struct inode *inode)
{
return false;
}
+static inline struct dentry *binderfs_create_file(struct dentry *dir,
+ const char *name,
+ const struct file_operations *fops,
+ void *data)
+{
+ return NULL;
+}
+static inline void binderfs_remove_file(struct dentry *dentry) {}
#endif

#ifdef CONFIG_ANDROID_BINDERFS
diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
index dc25a7d759c9..c386a3738290 100644
--- a/drivers/android/binderfs.c
+++ b/drivers/android/binderfs.c
@@ -48,16 +48,6 @@ static dev_t binderfs_dev;
static DEFINE_MUTEX(binderfs_minors_mutex);
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,
@@ -75,27 +65,6 @@ static const match_table_t tokens = {
{ Opt_err, NULL }
};

-/**
- * binderfs_info - information about a binderfs mount
- * @ipc_ns: The ipc namespace the binderfs mount belongs to.
- * @control_dentry: This records the dentry of this binderfs mount
- * binder-control device.
- * @root_uid: uid that needs to be used when a new binder device is
- * created.
- * @root_gid: gid that needs to be used when a new binder device is
- * created.
- * @mount_opts: The mount options in use.
- * @device_count: The current number of allocated binder devices.
- */
-struct binderfs_info {
- struct ipc_namespace *ipc_ns;
- struct dentry *control_dentry;
- kuid_t root_uid;
- kgid_t root_gid;
- struct binderfs_mount_opts mount_opts;
- int device_count;
-};
-
static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
{
return inode->i_sb->s_fs_info;
@@ -535,7 +504,22 @@ static struct dentry *binderfs_create_dentry(struct dentry *dir,
return dentry;
}

-static struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
+void binderfs_remove_file(struct dentry *dentry)
+{
+ struct inode *dir;
+
+ dir = d_inode(dentry->d_parent);
+ inode_lock(dir);
+ if (simple_positive(dentry)) {
+ dget(dentry);
+ simple_unlink(dir, dentry);
+ d_delete(dentry);
+ dput(dentry);
+ }
+ inode_unlock(dir);
+}
+
+struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
const struct file_operations *fops,
void *data)
{
@@ -604,7 +588,8 @@ static struct dentry *binderfs_create_dir(struct dentry *parent,

static int init_binder_logs(struct super_block *sb)
{
- struct dentry *binder_logs_root_dir, *file_dentry;
+ struct dentry *binder_logs_root_dir, *file_dentry, *proc_log_dir;
+ struct binderfs_info *info;
int ret = 0;

binder_logs_root_dir = binderfs_create_dir(sb->s_root,
@@ -648,8 +633,18 @@ static int init_binder_logs(struct super_block *sb)
"failed_transaction_log",
&binder_transaction_log_fops,
&binder_transaction_log_failed);
- if (IS_ERR(file_dentry))
+ if (IS_ERR(file_dentry)) {
ret = PTR_ERR(file_dentry);
+ goto out;
+ }
+
+ proc_log_dir = binderfs_create_dir(binder_logs_root_dir, "proc");
+ if (IS_ERR(proc_log_dir)) {
+ ret = PTR_ERR(proc_log_dir);
+ goto out;
+ }
+ info = sb->s_fs_info;
+ info->proc_log_dir = proc_log_dir;

out:
return ret;
--
2.23.0.187.g17f5b7556c-goog

2019-08-28 09:25:25

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 1/4] binder: add a mount option to show global stats

On Tue, Aug 27, 2019 at 01:41:49PM -0700, Hridya Valsaraju wrote:
> 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 <[email protected]>
> ---
> drivers/android/binderfs.c | 47 ++++++++++++++++++++++++++++++++++++--
> 1 file changed, 45 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
> index cc2e71576396..d95d179aec58 100644
> --- a/drivers/android/binderfs.c
> +++ b/drivers/android/binderfs.c
> @@ -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,24 @@ static int binderfs_parse_mount_opts(char *data,
>
> opts->max = max_devices;
> break;
> + case Opt_stats_mode:
> + stats = match_strdup(&args[0]);
> + if (!stats)
> + return -ENOMEM;
> +
> + if (strcmp(stats, "global") != 0) {
> + kfree(stats);
> + return -EINVAL;
> + }
> +
> + if (!capable(CAP_SYS_ADMIN)) {
> + kfree(stats);
> + return -EINVAL;

Can a non-CAP_SYS_ADMIN task even call this function? Anyway, if it
can, put the check at the top of the case, and just return early before
doing any extra work like checking values or allocating memory.

> + }
> +
> + opts->stats_mode = STATS_GLOBAL;
> + kfree(stats);
> + break;
> default:
> pr_err("Invalid mount options\n");
> return -EINVAL;
> @@ -322,8 +350,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_info("Binderfs stats mode cannot be changed during a remount\n");

pr_err()?

thanks,

greg k-h

2019-08-28 12:41:31

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH 1/4] binder: add a mount option to show global stats

On Wed, Aug 28, 2019 at 11:22:37AM +0200, Greg Kroah-Hartman wrote:
> On Tue, Aug 27, 2019 at 01:41:49PM -0700, Hridya Valsaraju wrote:
> > 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 <[email protected]>
> > ---
> > drivers/android/binderfs.c | 47 ++++++++++++++++++++++++++++++++++++--
> > 1 file changed, 45 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
> > index cc2e71576396..d95d179aec58 100644
> > --- a/drivers/android/binderfs.c
> > +++ b/drivers/android/binderfs.c
> > @@ -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,24 @@ static int binderfs_parse_mount_opts(char *data,
> >
> > opts->max = max_devices;
> > break;
> > + case Opt_stats_mode:
> > + stats = match_strdup(&args[0]);
> > + if (!stats)
> > + return -ENOMEM;
> > +
> > + if (strcmp(stats, "global") != 0) {
> > + kfree(stats);
> > + return -EINVAL;
> > + }
> > +
> > + if (!capable(CAP_SYS_ADMIN)) {
> > + kfree(stats);
> > + return -EINVAL;
>
> Can a non-CAP_SYS_ADMIN task even call this function? Anyway, if it

It can. A task that has CAP_SYS_ADMIN in the userns the corresponding
binderfs mount has been created in can change the max=<nr> mount option.
Only stats=global currently requires capable(CAP_SYS_ADMIN) aka
CAP_SYS_ADMIN in the initial userns to prevent non-initial userns from
snooping at global statistics.

> can, put the check at the top of the case, and just return early before
> doing any extra work like checking values or allocating memory.
>
> > + }
> > +
> > + opts->stats_mode = STATS_GLOBAL;
> > + kfree(stats);
> > + break;
> > default:
> > pr_err("Invalid mount options\n");
> > return -EINVAL;
> > @@ -322,8 +350,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_info("Binderfs stats mode cannot be changed during a remount\n");
>
> pr_err()?
>
> thanks,
>
> greg k-h

2019-08-28 13:11:48

by Christian Brauner

[permalink] [raw]
Subject: Re: [PATCH 4/4] binder: Add binder_proc logging to binderfs

On Tue, Aug 27, 2019 at 01:41:52PM -0700, Hridya Valsaraju wrote:
> Currently /sys/kernel/debug/binder/proc contains
> the debug data for every binder_proc instance.
> This patch makes this information also available
> in a binderfs instance mounted with a mount option
> "stats=global" in addition to debugfs. The patch does
> not affect the presence of the file in debugfs.
>
> If a binderfs instance is mounted at path /dev/binderfs,
> this file would be present at /dev/binderfs/binder_logs/proc.
> This change provides an alternate way to access this file when debugfs
> is not mounted.
>
> Signed-off-by: Hridya Valsaraju <[email protected]>

I'm still wondering whether there's a nicer way to create those debuf
files per-process without doing it in binder_open() but it has worked
fine for a long time with debugfs.

Also, one minor question below. Otherwise

Acked-by: Christian Brauner <[email protected]>

> ---
> drivers/android/binder.c | 38 ++++++++++++++++++-
> drivers/android/binder_internal.h | 46 ++++++++++++++++++++++
> drivers/android/binderfs.c | 63 ++++++++++++++-----------------
> 3 files changed, 111 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/android/binder.c b/drivers/android/binder.c
> index bed217310197..37189838f32a 100644
> --- a/drivers/android/binder.c
> +++ b/drivers/android/binder.c
> @@ -481,6 +481,7 @@ struct binder_priority {
> * @inner_lock: can nest under outer_lock and/or node lock
> * @outer_lock: no nesting under innor or node lock
> * Lock order: 1) outer, 2) node, 3) inner
> + * @binderfs_entry: process-specific binderfs log file
> *
> * Bookkeeping structure for binder processes
> */
> @@ -510,6 +511,7 @@ struct binder_proc {
> struct binder_context *context;
> spinlock_t inner_lock;
> spinlock_t outer_lock;
> + struct dentry *binderfs_entry;
> };
>
> enum {
> @@ -5347,6 +5349,8 @@ static int binder_open(struct inode *nodp, struct file *filp)
> {
> struct binder_proc *proc;
> struct binder_device *binder_dev;
> + struct binderfs_info *info;
> + struct dentry *binder_binderfs_dir_entry_proc = NULL;
>
> binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__,
> current->group_leader->pid, current->pid);
> @@ -5368,11 +5372,14 @@ static int binder_open(struct inode *nodp, struct file *filp)
> }
>
> /* binderfs stashes devices in i_private */
> - if (is_binderfs_device(nodp))
> + if (is_binderfs_device(nodp)) {
> binder_dev = nodp->i_private;
> - else
> + info = nodp->i_sb->s_fs_info;
> + binder_binderfs_dir_entry_proc = info->proc_log_dir;
> + } else {
> binder_dev = container_of(filp->private_data,
> struct binder_device, miscdev);
> + }
> proc->context = &binder_dev->context;
> binder_alloc_init(&proc->alloc);
>
> @@ -5403,6 +5410,27 @@ static int binder_open(struct inode *nodp, struct file *filp)
> &proc_fops);
> }
>
> + if (binder_binderfs_dir_entry_proc) {
> + char strbuf[11];
> + struct dentry *binderfs_entry;
> +
> + snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
> + /*
> + * Similar to debugfs, the process specific log file is shared
> + * between contexts. If the file has already been created for a
> + * process, the following binderfs_create_file() call will
> + * fail if another context of the same process invoked
> + * binder_open(). This is ok since same as debugfs,
> + * the log file will contain information on all contexts of a
> + * given PID.
> + */
> + binderfs_entry = binderfs_create_file(binder_binderfs_dir_entry_proc,
> + strbuf, &proc_fops, (void *)(unsigned long)proc->pid);
> + if (!IS_ERR(binderfs_entry))
> + proc->binderfs_entry = binderfs_entry;

You are sure that you don't want to fail the open, when the debug file
cannot be created in the binderfs instance? I'm not objecting at all, I
just want to make sure that this is the semantics you want because it
would be easy to differentiate between the non-fail-debugfs and the new
binderfs case.

> +
> + }
> +
> return 0;
> }
>
> @@ -5442,6 +5470,12 @@ static int binder_release(struct inode *nodp, struct file *filp)
> struct binder_proc *proc = filp->private_data;
>
> debugfs_remove(proc->debugfs_entry);
> +
> + if (proc->binderfs_entry) {
> + binderfs_remove_file(proc->binderfs_entry);
> + proc->binderfs_entry = NULL;
> + }
> +
> binder_defer_work(proc, BINDER_DEFERRED_RELEASE);
>
> return 0;
> diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h
> index b9be42d9464c..bd47f7f72075 100644
> --- a/drivers/android/binder_internal.h
> +++ b/drivers/android/binder_internal.h
> @@ -35,17 +35,63 @@ struct binder_device {
> struct inode *binderfs_inode;
> };
>
> +/**
> + * 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;
> +};
> +
> +/**
> + * binderfs_info - information about a binderfs mount
> + * @ipc_ns: The ipc namespace the binderfs mount belongs to.
> + * @control_dentry: This records the dentry of this binderfs mount
> + * binder-control device.
> + * @root_uid: uid that needs to be used when a new binder device is
> + * created.
> + * @root_gid: gid that needs to be used when a new binder device is
> + * created.
> + * @mount_opts: The mount options in use.
> + * @device_count: The current number of allocated binder devices.
> + * @proc_log_dir: Pointer to the directory dentry containing process-specific
> + * logs.
> + */
> +struct binderfs_info {
> + struct ipc_namespace *ipc_ns;
> + struct dentry *control_dentry;
> + kuid_t root_uid;
> + kgid_t root_gid;
> + struct binderfs_mount_opts mount_opts;
> + int device_count;
> + struct dentry *proc_log_dir;
> +};
> +
> extern const struct file_operations binder_fops;
>
> extern char *binder_devices_param;
>
> #ifdef CONFIG_ANDROID_BINDERFS
> extern bool is_binderfs_device(const struct inode *inode);
> +extern struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
> + const struct file_operations *fops,
> + void *data);
> +extern void binderfs_remove_file(struct dentry *dentry);
> #else
> static inline bool is_binderfs_device(const struct inode *inode)
> {
> return false;
> }
> +static inline struct dentry *binderfs_create_file(struct dentry *dir,
> + const char *name,
> + const struct file_operations *fops,
> + void *data)
> +{
> + return NULL;
> +}
> +static inline void binderfs_remove_file(struct dentry *dentry) {}
> #endif
>
> #ifdef CONFIG_ANDROID_BINDERFS
> diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
> index dc25a7d759c9..c386a3738290 100644
> --- a/drivers/android/binderfs.c
> +++ b/drivers/android/binderfs.c
> @@ -48,16 +48,6 @@ static dev_t binderfs_dev;
> static DEFINE_MUTEX(binderfs_minors_mutex);
> 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,
> @@ -75,27 +65,6 @@ static const match_table_t tokens = {
> { Opt_err, NULL }
> };
>
> -/**
> - * binderfs_info - information about a binderfs mount
> - * @ipc_ns: The ipc namespace the binderfs mount belongs to.
> - * @control_dentry: This records the dentry of this binderfs mount
> - * binder-control device.
> - * @root_uid: uid that needs to be used when a new binder device is
> - * created.
> - * @root_gid: gid that needs to be used when a new binder device is
> - * created.
> - * @mount_opts: The mount options in use.
> - * @device_count: The current number of allocated binder devices.
> - */
> -struct binderfs_info {
> - struct ipc_namespace *ipc_ns;
> - struct dentry *control_dentry;
> - kuid_t root_uid;
> - kgid_t root_gid;
> - struct binderfs_mount_opts mount_opts;
> - int device_count;
> -};
> -
> static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
> {
> return inode->i_sb->s_fs_info;
> @@ -535,7 +504,22 @@ static struct dentry *binderfs_create_dentry(struct dentry *dir,
> return dentry;
> }
>
> -static struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
> +void binderfs_remove_file(struct dentry *dentry)
> +{
> + struct inode *dir;
> +
> + dir = d_inode(dentry->d_parent);
> + inode_lock(dir);
> + if (simple_positive(dentry)) {
> + dget(dentry);
> + simple_unlink(dir, dentry);
> + d_delete(dentry);
> + dput(dentry);
> + }
> + inode_unlock(dir);
> +}
> +
> +struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
> const struct file_operations *fops,
> void *data)
> {
> @@ -604,7 +588,8 @@ static struct dentry *binderfs_create_dir(struct dentry *parent,
>
> static int init_binder_logs(struct super_block *sb)
> {
> - struct dentry *binder_logs_root_dir, *file_dentry;
> + struct dentry *binder_logs_root_dir, *file_dentry, *proc_log_dir;
> + struct binderfs_info *info;
> int ret = 0;
>
> binder_logs_root_dir = binderfs_create_dir(sb->s_root,
> @@ -648,8 +633,18 @@ static int init_binder_logs(struct super_block *sb)
> "failed_transaction_log",
> &binder_transaction_log_fops,
> &binder_transaction_log_failed);
> - if (IS_ERR(file_dentry))
> + if (IS_ERR(file_dentry)) {
> ret = PTR_ERR(file_dentry);
> + goto out;
> + }
> +
> + proc_log_dir = binderfs_create_dir(binder_logs_root_dir, "proc");
> + if (IS_ERR(proc_log_dir)) {
> + ret = PTR_ERR(proc_log_dir);
> + goto out;
> + }
> + info = sb->s_fs_info;
> + info->proc_log_dir = proc_log_dir;
>
> out:
> return ret;
> --
> 2.23.0.187.g17f5b7556c-goog
>

2019-08-28 16:24:15

by Todd Kjos

[permalink] [raw]
Subject: Re: [PATCH 4/4] binder: Add binder_proc logging to binderfs

On Wed, Aug 28, 2019 at 6:08 AM Christian Brauner
<[email protected]> wrote:
>
> On Tue, Aug 27, 2019 at 01:41:52PM -0700, Hridya Valsaraju wrote:
> > Currently /sys/kernel/debug/binder/proc contains
> > the debug data for every binder_proc instance.
> > This patch makes this information also available
> > in a binderfs instance mounted with a mount option
> > "stats=global" in addition to debugfs. The patch does
> > not affect the presence of the file in debugfs.
> >
> > If a binderfs instance is mounted at path /dev/binderfs,
> > this file would be present at /dev/binderfs/binder_logs/proc.
> > This change provides an alternate way to access this file when debugfs
> > is not mounted.
> >
> > Signed-off-by: Hridya Valsaraju <[email protected]>
>
> I'm still wondering whether there's a nicer way to create those debuf
> files per-process without doing it in binder_open() but it has worked
> fine for a long time with debugfs.
>
> Also, one minor question below. Otherwise
>
> Acked-by: Christian Brauner <[email protected]>

Acked-by: Todd Kjos <[email protected]>

>
> > ---
> > drivers/android/binder.c | 38 ++++++++++++++++++-
> > drivers/android/binder_internal.h | 46 ++++++++++++++++++++++
> > drivers/android/binderfs.c | 63 ++++++++++++++-----------------
> > 3 files changed, 111 insertions(+), 36 deletions(-)
> >
> > diff --git a/drivers/android/binder.c b/drivers/android/binder.c
> > index bed217310197..37189838f32a 100644
> > --- a/drivers/android/binder.c
> > +++ b/drivers/android/binder.c
> > @@ -481,6 +481,7 @@ struct binder_priority {
> > * @inner_lock: can nest under outer_lock and/or node lock
> > * @outer_lock: no nesting under innor or node lock
> > * Lock order: 1) outer, 2) node, 3) inner
> > + * @binderfs_entry: process-specific binderfs log file
> > *
> > * Bookkeeping structure for binder processes
> > */
> > @@ -510,6 +511,7 @@ struct binder_proc {
> > struct binder_context *context;
> > spinlock_t inner_lock;
> > spinlock_t outer_lock;
> > + struct dentry *binderfs_entry;
> > };
> >
> > enum {
> > @@ -5347,6 +5349,8 @@ static int binder_open(struct inode *nodp, struct file *filp)
> > {
> > struct binder_proc *proc;
> > struct binder_device *binder_dev;
> > + struct binderfs_info *info;
> > + struct dentry *binder_binderfs_dir_entry_proc = NULL;
> >
> > binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__,
> > current->group_leader->pid, current->pid);
> > @@ -5368,11 +5372,14 @@ static int binder_open(struct inode *nodp, struct file *filp)
> > }
> >
> > /* binderfs stashes devices in i_private */
> > - if (is_binderfs_device(nodp))
> > + if (is_binderfs_device(nodp)) {
> > binder_dev = nodp->i_private;
> > - else
> > + info = nodp->i_sb->s_fs_info;
> > + binder_binderfs_dir_entry_proc = info->proc_log_dir;
> > + } else {
> > binder_dev = container_of(filp->private_data,
> > struct binder_device, miscdev);
> > + }
> > proc->context = &binder_dev->context;
> > binder_alloc_init(&proc->alloc);
> >
> > @@ -5403,6 +5410,27 @@ static int binder_open(struct inode *nodp, struct file *filp)
> > &proc_fops);
> > }
> >
> > + if (binder_binderfs_dir_entry_proc) {
> > + char strbuf[11];
> > + struct dentry *binderfs_entry;
> > +
> > + snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
> > + /*
> > + * Similar to debugfs, the process specific log file is shared
> > + * between contexts. If the file has already been created for a
> > + * process, the following binderfs_create_file() call will
> > + * fail if another context of the same process invoked
> > + * binder_open(). This is ok since same as debugfs,
> > + * the log file will contain information on all contexts of a
> > + * given PID.
> > + */
> > + binderfs_entry = binderfs_create_file(binder_binderfs_dir_entry_proc,
> > + strbuf, &proc_fops, (void *)(unsigned long)proc->pid);
> > + if (!IS_ERR(binderfs_entry))
> > + proc->binderfs_entry = binderfs_entry;
>
> You are sure that you don't want to fail the open, when the debug file
> cannot be created in the binderfs instance? I'm not objecting at all, I
> just want to make sure that this is the semantics you want because it
> would be easy to differentiate between the non-fail-debugfs and the new
> binderfs case.

I don't think we should fail the open, but it would be nice to have
some indication that it failed -- maybe a pr_warn() ?
>
> > +
> > + }
> > +
> > return 0;
> > }
> >
> > @@ -5442,6 +5470,12 @@ static int binder_release(struct inode *nodp, struct file *filp)
> > struct binder_proc *proc = filp->private_data;
> >
> > debugfs_remove(proc->debugfs_entry);
> > +
> > + if (proc->binderfs_entry) {
> > + binderfs_remove_file(proc->binderfs_entry);
> > + proc->binderfs_entry = NULL;
> > + }
> > +
> > binder_defer_work(proc, BINDER_DEFERRED_RELEASE);
> >
> > return 0;
> > diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h
> > index b9be42d9464c..bd47f7f72075 100644
> > --- a/drivers/android/binder_internal.h
> > +++ b/drivers/android/binder_internal.h
> > @@ -35,17 +35,63 @@ struct binder_device {
> > struct inode *binderfs_inode;
> > };
> >
> > +/**
> > + * 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;
> > +};
> > +
> > +/**
> > + * binderfs_info - information about a binderfs mount
> > + * @ipc_ns: The ipc namespace the binderfs mount belongs to.
> > + * @control_dentry: This records the dentry of this binderfs mount
> > + * binder-control device.
> > + * @root_uid: uid that needs to be used when a new binder device is
> > + * created.
> > + * @root_gid: gid that needs to be used when a new binder device is
> > + * created.
> > + * @mount_opts: The mount options in use.
> > + * @device_count: The current number of allocated binder devices.
> > + * @proc_log_dir: Pointer to the directory dentry containing process-specific
> > + * logs.
> > + */
> > +struct binderfs_info {
> > + struct ipc_namespace *ipc_ns;
> > + struct dentry *control_dentry;
> > + kuid_t root_uid;
> > + kgid_t root_gid;
> > + struct binderfs_mount_opts mount_opts;
> > + int device_count;
> > + struct dentry *proc_log_dir;
> > +};
> > +
> > extern const struct file_operations binder_fops;
> >
> > extern char *binder_devices_param;
> >
> > #ifdef CONFIG_ANDROID_BINDERFS
> > extern bool is_binderfs_device(const struct inode *inode);
> > +extern struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
> > + const struct file_operations *fops,
> > + void *data);
> > +extern void binderfs_remove_file(struct dentry *dentry);
> > #else
> > static inline bool is_binderfs_device(const struct inode *inode)
> > {
> > return false;
> > }
> > +static inline struct dentry *binderfs_create_file(struct dentry *dir,
> > + const char *name,
> > + const struct file_operations *fops,
> > + void *data)
> > +{
> > + return NULL;
> > +}
> > +static inline void binderfs_remove_file(struct dentry *dentry) {}
> > #endif
> >
> > #ifdef CONFIG_ANDROID_BINDERFS
> > diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
> > index dc25a7d759c9..c386a3738290 100644
> > --- a/drivers/android/binderfs.c
> > +++ b/drivers/android/binderfs.c
> > @@ -48,16 +48,6 @@ static dev_t binderfs_dev;
> > static DEFINE_MUTEX(binderfs_minors_mutex);
> > 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,
> > @@ -75,27 +65,6 @@ static const match_table_t tokens = {
> > { Opt_err, NULL }
> > };
> >
> > -/**
> > - * binderfs_info - information about a binderfs mount
> > - * @ipc_ns: The ipc namespace the binderfs mount belongs to.
> > - * @control_dentry: This records the dentry of this binderfs mount
> > - * binder-control device.
> > - * @root_uid: uid that needs to be used when a new binder device is
> > - * created.
> > - * @root_gid: gid that needs to be used when a new binder device is
> > - * created.
> > - * @mount_opts: The mount options in use.
> > - * @device_count: The current number of allocated binder devices.
> > - */
> > -struct binderfs_info {
> > - struct ipc_namespace *ipc_ns;
> > - struct dentry *control_dentry;
> > - kuid_t root_uid;
> > - kgid_t root_gid;
> > - struct binderfs_mount_opts mount_opts;
> > - int device_count;
> > -};
> > -
> > static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
> > {
> > return inode->i_sb->s_fs_info;
> > @@ -535,7 +504,22 @@ static struct dentry *binderfs_create_dentry(struct dentry *dir,
> > return dentry;
> > }
> >
> > -static struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
> > +void binderfs_remove_file(struct dentry *dentry)
> > +{
> > + struct inode *dir;
> > +
> > + dir = d_inode(dentry->d_parent);
> > + inode_lock(dir);
> > + if (simple_positive(dentry)) {
> > + dget(dentry);
> > + simple_unlink(dir, dentry);
> > + d_delete(dentry);
> > + dput(dentry);
> > + }
> > + inode_unlock(dir);
> > +}
> > +
> > +struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
> > const struct file_operations *fops,
> > void *data)
> > {
> > @@ -604,7 +588,8 @@ static struct dentry *binderfs_create_dir(struct dentry *parent,
> >
> > static int init_binder_logs(struct super_block *sb)
> > {
> > - struct dentry *binder_logs_root_dir, *file_dentry;
> > + struct dentry *binder_logs_root_dir, *file_dentry, *proc_log_dir;
> > + struct binderfs_info *info;
> > int ret = 0;
> >
> > binder_logs_root_dir = binderfs_create_dir(sb->s_root,
> > @@ -648,8 +633,18 @@ static int init_binder_logs(struct super_block *sb)
> > "failed_transaction_log",
> > &binder_transaction_log_fops,
> > &binder_transaction_log_failed);
> > - if (IS_ERR(file_dentry))
> > + if (IS_ERR(file_dentry)) {
> > ret = PTR_ERR(file_dentry);
> > + goto out;
> > + }
> > +
> > + proc_log_dir = binderfs_create_dir(binder_logs_root_dir, "proc");
> > + if (IS_ERR(proc_log_dir)) {
> > + ret = PTR_ERR(proc_log_dir);
> > + goto out;
> > + }
> > + info = sb->s_fs_info;
> > + info->proc_log_dir = proc_log_dir;
> >
> > out:
> > return ret;
> > --
> > 2.23.0.187.g17f5b7556c-goog
> >
>
> --
> To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
>

2019-08-28 17:23:28

by Hridya Valsaraju

[permalink] [raw]
Subject: Re: [PATCH 1/4] binder: add a mount option to show global stats

On Wed, Aug 28, 2019 at 5:39 AM Christian Brauner
<[email protected]> wrote:
>
> On Wed, Aug 28, 2019 at 11:22:37AM +0200, Greg Kroah-Hartman wrote:
> > On Tue, Aug 27, 2019 at 01:41:49PM -0700, Hridya Valsaraju wrote:
> > > 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 <[email protected]>
> > > ---
> > > drivers/android/binderfs.c | 47 ++++++++++++++++++++++++++++++++++++--
> > > 1 file changed, 45 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
> > > index cc2e71576396..d95d179aec58 100644
> > > --- a/drivers/android/binderfs.c
> > > +++ b/drivers/android/binderfs.c
> > > @@ -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,24 @@ static int binderfs_parse_mount_opts(char *data,
> > >
> > > opts->max = max_devices;
> > > break;
> > > + case Opt_stats_mode:
> > > + stats = match_strdup(&args[0]);
> > > + if (!stats)
> > > + return -ENOMEM;
> > > +
> > > + if (strcmp(stats, "global") != 0) {
> > > + kfree(stats);
> > > + return -EINVAL;
> > > + }
> > > +
> > > + if (!capable(CAP_SYS_ADMIN)) {
> > > + kfree(stats);
> > > + return -EINVAL;
> >
> > Can a non-CAP_SYS_ADMIN task even call this function? Anyway, if it
>
> It can. A task that has CAP_SYS_ADMIN in the userns the corresponding
> binderfs mount has been created in can change the max=<nr> mount option.
> Only stats=global currently requires capable(CAP_SYS_ADMIN) aka
> CAP_SYS_ADMIN in the initial userns to prevent non-initial userns from
> snooping at global statistics.
>
> > can, put the check at the top of the case, and just return early before
> > doing any extra work like checking values or allocating memory.

Thank you Greg and Christian for reviewing the patch! That makes
sense, I will make the change and send out v2 soon.

> >
> > > + }
> > > +
> > > + opts->stats_mode = STATS_GLOBAL;
> > > + kfree(stats);
> > > + break;
> > > default:
> > > pr_err("Invalid mount options\n");
> > > return -EINVAL;
> > > @@ -322,8 +350,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_info("Binderfs stats mode cannot be changed during a remount\n");
> >
> > pr_err()?
> >
> > thanks,
> >
> > greg k-h

2019-08-28 20:44:27

by Hridya Valsaraju

[permalink] [raw]
Subject: Re: [PATCH 4/4] binder: Add binder_proc logging to binderfs

On Wed, Aug 28, 2019 at 9:21 AM Todd Kjos <[email protected]> wrote:
>
> On Wed, Aug 28, 2019 at 6:08 AM Christian Brauner
> <[email protected]> wrote:
> >
> > On Tue, Aug 27, 2019 at 01:41:52PM -0700, Hridya Valsaraju wrote:
> > > Currently /sys/kernel/debug/binder/proc contains
> > > the debug data for every binder_proc instance.
> > > This patch makes this information also available
> > > in a binderfs instance mounted with a mount option
> > > "stats=global" in addition to debugfs. The patch does
> > > not affect the presence of the file in debugfs.
> > >
> > > If a binderfs instance is mounted at path /dev/binderfs,
> > > this file would be present at /dev/binderfs/binder_logs/proc.
> > > This change provides an alternate way to access this file when debugfs
> > > is not mounted.
> > >
> > > Signed-off-by: Hridya Valsaraju <[email protected]>
> >
> > I'm still wondering whether there's a nicer way to create those debuf
> > files per-process without doing it in binder_open() but it has worked
> > fine for a long time with debugfs.
> >
> > Also, one minor question below. Otherwise
> >
> > Acked-by: Christian Brauner <[email protected]>
>
> Acked-by: Todd Kjos <[email protected]>
>
> >
> > > ---
> > > drivers/android/binder.c | 38 ++++++++++++++++++-
> > > drivers/android/binder_internal.h | 46 ++++++++++++++++++++++
> > > drivers/android/binderfs.c | 63 ++++++++++++++-----------------
> > > 3 files changed, 111 insertions(+), 36 deletions(-)
> > >
> > > diff --git a/drivers/android/binder.c b/drivers/android/binder.c
> > > index bed217310197..37189838f32a 100644
> > > --- a/drivers/android/binder.c
> > > +++ b/drivers/android/binder.c
> > > @@ -481,6 +481,7 @@ struct binder_priority {
> > > * @inner_lock: can nest under outer_lock and/or node lock
> > > * @outer_lock: no nesting under innor or node lock
> > > * Lock order: 1) outer, 2) node, 3) inner
> > > + * @binderfs_entry: process-specific binderfs log file
> > > *
> > > * Bookkeeping structure for binder processes
> > > */
> > > @@ -510,6 +511,7 @@ struct binder_proc {
> > > struct binder_context *context;
> > > spinlock_t inner_lock;
> > > spinlock_t outer_lock;
> > > + struct dentry *binderfs_entry;
> > > };
> > >
> > > enum {
> > > @@ -5347,6 +5349,8 @@ static int binder_open(struct inode *nodp, struct file *filp)
> > > {
> > > struct binder_proc *proc;
> > > struct binder_device *binder_dev;
> > > + struct binderfs_info *info;
> > > + struct dentry *binder_binderfs_dir_entry_proc = NULL;
> > >
> > > binder_debug(BINDER_DEBUG_OPEN_CLOSE, "%s: %d:%d\n", __func__,
> > > current->group_leader->pid, current->pid);
> > > @@ -5368,11 +5372,14 @@ static int binder_open(struct inode *nodp, struct file *filp)
> > > }
> > >
> > > /* binderfs stashes devices in i_private */
> > > - if (is_binderfs_device(nodp))
> > > + if (is_binderfs_device(nodp)) {
> > > binder_dev = nodp->i_private;
> > > - else
> > > + info = nodp->i_sb->s_fs_info;
> > > + binder_binderfs_dir_entry_proc = info->proc_log_dir;
> > > + } else {
> > > binder_dev = container_of(filp->private_data,
> > > struct binder_device, miscdev);
> > > + }
> > > proc->context = &binder_dev->context;
> > > binder_alloc_init(&proc->alloc);
> > >
> > > @@ -5403,6 +5410,27 @@ static int binder_open(struct inode *nodp, struct file *filp)
> > > &proc_fops);
> > > }
> > >
> > > + if (binder_binderfs_dir_entry_proc) {
> > > + char strbuf[11];
> > > + struct dentry *binderfs_entry;
> > > +
> > > + snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
> > > + /*
> > > + * Similar to debugfs, the process specific log file is shared
> > > + * between contexts. If the file has already been created for a
> > > + * process, the following binderfs_create_file() call will
> > > + * fail if another context of the same process invoked
> > > + * binder_open(). This is ok since same as debugfs,
> > > + * the log file will contain information on all contexts of a
> > > + * given PID.
> > > + */
> > > + binderfs_entry = binderfs_create_file(binder_binderfs_dir_entry_proc,
> > > + strbuf, &proc_fops, (void *)(unsigned long)proc->pid);
> > > + if (!IS_ERR(binderfs_entry))
> > > + proc->binderfs_entry = binderfs_entry;
> >
> > You are sure that you don't want to fail the open, when the debug file
> > cannot be created in the binderfs instance? I'm not objecting at all, I
> > just want to make sure that this is the semantics you want because it
> > would be easy to differentiate between the non-fail-debugfs and the new
> > binderfs case.
>
> I don't think we should fail the open, but it would be nice to have
> some indication that it failed -- maybe a pr_warn() ?

Thanks for taking a look Christian and Todd! The reason I do not fail
binder_open() on an error here is because once the file has been
created for a process, if the same process invokes binder_open() again
from a different context, the binderfs_create_file() call would fail
with the EEXIST error code. This is expected behaviour since the log
file is shared between all contexts of a process(same as the behaviour
of the file in debugfs today). I can add a pr_warn() statement in v2
as Todd suggested so that the failure would be logged.

> >
> > > +
> > > + }
> > > +
> > > return 0;
> > > }
> > >
> > > @@ -5442,6 +5470,12 @@ static int binder_release(struct inode *nodp, struct file *filp)
> > > struct binder_proc *proc = filp->private_data;
> > >
> > > debugfs_remove(proc->debugfs_entry);
> > > +
> > > + if (proc->binderfs_entry) {
> > > + binderfs_remove_file(proc->binderfs_entry);
> > > + proc->binderfs_entry = NULL;
> > > + }
> > > +
> > > binder_defer_work(proc, BINDER_DEFERRED_RELEASE);
> > >
> > > return 0;
> > > diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h
> > > index b9be42d9464c..bd47f7f72075 100644
> > > --- a/drivers/android/binder_internal.h
> > > +++ b/drivers/android/binder_internal.h
> > > @@ -35,17 +35,63 @@ struct binder_device {
> > > struct inode *binderfs_inode;
> > > };
> > >
> > > +/**
> > > + * 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;
> > > +};
> > > +
> > > +/**
> > > + * binderfs_info - information about a binderfs mount
> > > + * @ipc_ns: The ipc namespace the binderfs mount belongs to.
> > > + * @control_dentry: This records the dentry of this binderfs mount
> > > + * binder-control device.
> > > + * @root_uid: uid that needs to be used when a new binder device is
> > > + * created.
> > > + * @root_gid: gid that needs to be used when a new binder device is
> > > + * created.
> > > + * @mount_opts: The mount options in use.
> > > + * @device_count: The current number of allocated binder devices.
> > > + * @proc_log_dir: Pointer to the directory dentry containing process-specific
> > > + * logs.
> > > + */
> > > +struct binderfs_info {
> > > + struct ipc_namespace *ipc_ns;
> > > + struct dentry *control_dentry;
> > > + kuid_t root_uid;
> > > + kgid_t root_gid;
> > > + struct binderfs_mount_opts mount_opts;
> > > + int device_count;
> > > + struct dentry *proc_log_dir;
> > > +};
> > > +
> > > extern const struct file_operations binder_fops;
> > >
> > > extern char *binder_devices_param;
> > >
> > > #ifdef CONFIG_ANDROID_BINDERFS
> > > extern bool is_binderfs_device(const struct inode *inode);
> > > +extern struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
> > > + const struct file_operations *fops,
> > > + void *data);
> > > +extern void binderfs_remove_file(struct dentry *dentry);
> > > #else
> > > static inline bool is_binderfs_device(const struct inode *inode)
> > > {
> > > return false;
> > > }
> > > +static inline struct dentry *binderfs_create_file(struct dentry *dir,
> > > + const char *name,
> > > + const struct file_operations *fops,
> > > + void *data)
> > > +{
> > > + return NULL;
> > > +}
> > > +static inline void binderfs_remove_file(struct dentry *dentry) {}
> > > #endif
> > >
> > > #ifdef CONFIG_ANDROID_BINDERFS
> > > diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c
> > > index dc25a7d759c9..c386a3738290 100644
> > > --- a/drivers/android/binderfs.c
> > > +++ b/drivers/android/binderfs.c
> > > @@ -48,16 +48,6 @@ static dev_t binderfs_dev;
> > > static DEFINE_MUTEX(binderfs_minors_mutex);
> > > 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,
> > > @@ -75,27 +65,6 @@ static const match_table_t tokens = {
> > > { Opt_err, NULL }
> > > };
> > >
> > > -/**
> > > - * binderfs_info - information about a binderfs mount
> > > - * @ipc_ns: The ipc namespace the binderfs mount belongs to.
> > > - * @control_dentry: This records the dentry of this binderfs mount
> > > - * binder-control device.
> > > - * @root_uid: uid that needs to be used when a new binder device is
> > > - * created.
> > > - * @root_gid: gid that needs to be used when a new binder device is
> > > - * created.
> > > - * @mount_opts: The mount options in use.
> > > - * @device_count: The current number of allocated binder devices.
> > > - */
> > > -struct binderfs_info {
> > > - struct ipc_namespace *ipc_ns;
> > > - struct dentry *control_dentry;
> > > - kuid_t root_uid;
> > > - kgid_t root_gid;
> > > - struct binderfs_mount_opts mount_opts;
> > > - int device_count;
> > > -};
> > > -
> > > static inline struct binderfs_info *BINDERFS_I(const struct inode *inode)
> > > {
> > > return inode->i_sb->s_fs_info;
> > > @@ -535,7 +504,22 @@ static struct dentry *binderfs_create_dentry(struct dentry *dir,
> > > return dentry;
> > > }
> > >
> > > -static struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
> > > +void binderfs_remove_file(struct dentry *dentry)
> > > +{
> > > + struct inode *dir;
> > > +
> > > + dir = d_inode(dentry->d_parent);
> > > + inode_lock(dir);
> > > + if (simple_positive(dentry)) {
> > > + dget(dentry);
> > > + simple_unlink(dir, dentry);
> > > + d_delete(dentry);
> > > + dput(dentry);
> > > + }
> > > + inode_unlock(dir);
> > > +}
> > > +
> > > +struct dentry *binderfs_create_file(struct dentry *dir, const char *name,
> > > const struct file_operations *fops,
> > > void *data)
> > > {
> > > @@ -604,7 +588,8 @@ static struct dentry *binderfs_create_dir(struct dentry *parent,
> > >
> > > static int init_binder_logs(struct super_block *sb)
> > > {
> > > - struct dentry *binder_logs_root_dir, *file_dentry;
> > > + struct dentry *binder_logs_root_dir, *file_dentry, *proc_log_dir;
> > > + struct binderfs_info *info;
> > > int ret = 0;
> > >
> > > binder_logs_root_dir = binderfs_create_dir(sb->s_root,
> > > @@ -648,8 +633,18 @@ static int init_binder_logs(struct super_block *sb)
> > > "failed_transaction_log",
> > > &binder_transaction_log_fops,
> > > &binder_transaction_log_failed);
> > > - if (IS_ERR(file_dentry))
> > > + if (IS_ERR(file_dentry)) {
> > > ret = PTR_ERR(file_dentry);
> > > + goto out;
> > > + }
> > > +
> > > + proc_log_dir = binderfs_create_dir(binder_logs_root_dir, "proc");
> > > + if (IS_ERR(proc_log_dir)) {
> > > + ret = PTR_ERR(proc_log_dir);
> > > + goto out;
> > > + }
> > > + info = sb->s_fs_info;
> > > + info->proc_log_dir = proc_log_dir;
> > >
> > > out:
> > > return ret;
> > > --
> > > 2.23.0.187.g17f5b7556c-goog
> > >
> >
> > --
> > To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
> >