Merge "trace: ipc_logging: Release the debugfs SRCU when reading"
diff --git a/kernel/trace/ipc_logging.c b/kernel/trace/ipc_logging.c
index 31e6a8e..ec9bde7 100644
--- a/kernel/trace/ipc_logging.c
+++ b/kernel/trace/ipc_logging.c
@@ -549,6 +549,7 @@
struct decode_context *dctxt);
struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt;
unsigned long flags;
+ int ret;
if (size < MAX_MSG_DECODED_SIZE)
return -EINVAL;
@@ -558,6 +559,11 @@
dctxt.size = size;
read_lock_irqsave(&context_list_lock_lha1, flags);
spin_lock(&ilctxt->context_lock_lhb1);
+ if (ilctxt->destroyed) {
+ ret = -EIO;
+ goto done;
+ }
+
while (dctxt.size >= MAX_MSG_DECODED_SIZE &&
!is_nd_read_empty(ilctxt)) {
msg_read(ilctxt, &ectxt);
@@ -573,11 +579,17 @@
read_lock_irqsave(&context_list_lock_lha1, flags);
spin_lock(&ilctxt->context_lock_lhb1);
}
- if ((size - dctxt.size) == 0)
- reinit_completion(&ilctxt->read_avail);
+ ret = size - dctxt.size;
+ if (ret == 0) {
+ if (!ilctxt->destroyed)
+ reinit_completion(&ilctxt->read_avail);
+ else
+ ret = -EIO;
+ }
+done:
spin_unlock(&ilctxt->context_lock_lhb1);
read_unlock_irqrestore(&context_list_lock_lha1, flags);
- return size - dctxt.size;
+ return ret;
}
EXPORT_SYMBOL(ipc_log_extract);
@@ -835,6 +847,8 @@
ctxt->nd_read_page = ctxt->first_page;
ctxt->write_avail = max_num_pages * LOG_PAGE_DATA_SIZE;
ctxt->header_size = sizeof(struct ipc_log_page_header);
+ kref_init(&ctxt->refcount);
+ ctxt->destroyed = false;
create_ctx_debugfs(ctxt, mod_name);
/* set magic last to signal context init is complete */
@@ -857,6 +871,21 @@
}
EXPORT_SYMBOL(ipc_log_context_create);
+void ipc_log_context_free(struct kref *kref)
+{
+ struct ipc_log_context *ilctxt = container_of(kref,
+ struct ipc_log_context, refcount);
+ struct ipc_log_page *pg = NULL;
+
+ while (!list_empty(&ilctxt->page_list)) {
+ pg = get_first_page(ilctxt);
+ list_del(&pg->hdr.list);
+ kfree(pg);
+ }
+
+ kfree(ilctxt);
+}
+
/*
* Destroy debug log context
*
@@ -865,25 +894,24 @@
int ipc_log_context_destroy(void *ctxt)
{
struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt;
- struct ipc_log_page *pg = NULL;
unsigned long flags;
if (!ilctxt)
return 0;
- while (!list_empty(&ilctxt->page_list)) {
- pg = get_first_page(ctxt);
- list_del(&pg->hdr.list);
- kfree(pg);
- }
+ debugfs_remove_recursive(ilctxt->dent);
+
+ spin_lock(&ilctxt->context_lock_lhb1);
+ ilctxt->destroyed = true;
+ complete_all(&ilctxt->read_avail);
+ spin_unlock(&ilctxt->context_lock_lhb1);
write_lock_irqsave(&context_list_lock_lha1, flags);
list_del(&ilctxt->list);
write_unlock_irqrestore(&context_list_lock_lha1, flags);
- debugfs_remove_recursive(ilctxt->dent);
+ ipc_log_context_put(ilctxt);
- kfree(ilctxt);
return 0;
}
EXPORT_SYMBOL(ipc_log_context_destroy);
diff --git a/kernel/trace/ipc_logging_debug.c b/kernel/trace/ipc_logging_debug.c
index a545387..d733724 100644
--- a/kernel/trace/ipc_logging_debug.c
+++ b/kernel/trace/ipc_logging_debug.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -74,23 +74,42 @@
static ssize_t debug_read_helper(struct file *file, char __user *buff,
size_t count, loff_t *ppos, int cont)
{
- struct ipc_log_context *ilctxt = file->private_data;
+ struct ipc_log_context *ilctxt;
+ struct dentry *d = file->f_path.dentry;
char *buffer;
int bsize;
+ int srcu_idx;
+ int r;
+
+ r = debugfs_use_file_start(d, &srcu_idx);
+ if (!r) {
+ ilctxt = file->private_data;
+ r = kref_get_unless_zero(&ilctxt->refcount) ? 0 : -EIO;
+ }
+ debugfs_use_file_finish(srcu_idx);
+ if (r)
+ return r;
buffer = kmalloc(count, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
+ if (!buffer) {
+ bsize = -ENOMEM;
+ goto done;
+ }
bsize = debug_log(ilctxt, buffer, count, cont);
+
if (bsize > 0) {
if (copy_to_user(buff, buffer, bsize)) {
+ bsize = -EFAULT;
kfree(buffer);
- return -EFAULT;
+ goto done;
}
*ppos += bsize;
}
kfree(buffer);
+
+done:
+ ipc_log_context_put(ilctxt);
return bsize;
}
@@ -127,7 +146,7 @@
struct ipc_log_context *ilctxt,
const struct file_operations *fops)
{
- debugfs_create_file(name, mode, dent, ilctxt, fops);
+ debugfs_create_file_unsafe(name, mode, dent, ilctxt, fops);
}
static void dfunc_string(struct encode_context *ectxt,
diff --git a/kernel/trace/ipc_logging_private.h b/kernel/trace/ipc_logging_private.h
index 594027a..47c41e9 100644
--- a/kernel/trace/ipc_logging_private.h
+++ b/kernel/trace/ipc_logging_private.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -119,6 +119,8 @@
struct list_head dfunc_info_list;
spinlock_t context_lock_lhb1;
struct completion read_avail;
+ struct kref refcount;
+ bool destroyed;
};
struct dfunc_info {
@@ -147,6 +149,13 @@
((x) < TSV_TYPE_MSG_END))
#define MAX_MSG_DECODED_SIZE (MAX_MSG_SIZE*4)
+void ipc_log_context_free(struct kref *kref);
+
+static inline void ipc_log_context_put(struct ipc_log_context *ilctxt)
+{
+ kref_put(&ilctxt->refcount, ipc_log_context_free);
+}
+
#if (defined(CONFIG_DEBUG_FS))
void check_and_create_debugfs(void);