Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 802d738a authored by Lynus Vaz's avatar Lynus Vaz Committed by Arun Kumar Neelakantam
Browse files

trace: ipc_logging: Release the debugfs SRCU when reading



Global debugfs SRCU lock is acquired while reading log_cont file
and not released until read operation is killed which blocks all
other clients adding and removing debugfs nodes.

Avoid using debugfs global SRCU lock during read operation by using
"debugfs_create_file_unsafe".

CRs-Fixed: 2066241
Change-Id: Ie471ca8f65123f1fd6db80d2006843d1a2250984
Signed-off-by: default avatarArun Kumar Neelakantam <aneela@codeaurora.org>
parent 1a395d00
Loading
Loading
Loading
Loading
+39 −11
Original line number Original line Diff line number Diff line
@@ -549,6 +549,7 @@ int ipc_log_extract(void *ctxt, char *buff, int size)
				 struct decode_context *dctxt);
				 struct decode_context *dctxt);
	struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt;
	struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt;
	unsigned long flags;
	unsigned long flags;
	int ret;


	if (size < MAX_MSG_DECODED_SIZE)
	if (size < MAX_MSG_DECODED_SIZE)
		return -EINVAL;
		return -EINVAL;
@@ -558,6 +559,11 @@ int ipc_log_extract(void *ctxt, char *buff, int size)
	dctxt.size = size;
	dctxt.size = size;
	read_lock_irqsave(&context_list_lock_lha1, flags);
	read_lock_irqsave(&context_list_lock_lha1, flags);
	spin_lock(&ilctxt->context_lock_lhb1);
	spin_lock(&ilctxt->context_lock_lhb1);
	if (ilctxt->destroyed) {
		ret = -EIO;
		goto done;
	}

	while (dctxt.size >= MAX_MSG_DECODED_SIZE &&
	while (dctxt.size >= MAX_MSG_DECODED_SIZE &&
	       !is_nd_read_empty(ilctxt)) {
	       !is_nd_read_empty(ilctxt)) {
		msg_read(ilctxt, &ectxt);
		msg_read(ilctxt, &ectxt);
@@ -573,11 +579,17 @@ int ipc_log_extract(void *ctxt, char *buff, int size)
		read_lock_irqsave(&context_list_lock_lha1, flags);
		read_lock_irqsave(&context_list_lock_lha1, flags);
		spin_lock(&ilctxt->context_lock_lhb1);
		spin_lock(&ilctxt->context_lock_lhb1);
	}
	}
	if ((size - dctxt.size) == 0)
	ret = size - dctxt.size;
	if (ret == 0) {
		if (!ilctxt->destroyed)
			reinit_completion(&ilctxt->read_avail);
			reinit_completion(&ilctxt->read_avail);
		else
			ret = -EIO;
	}
done:
	spin_unlock(&ilctxt->context_lock_lhb1);
	spin_unlock(&ilctxt->context_lock_lhb1);
	read_unlock_irqrestore(&context_list_lock_lha1, flags);
	read_unlock_irqrestore(&context_list_lock_lha1, flags);
	return size - dctxt.size;
	return ret;
}
}
EXPORT_SYMBOL(ipc_log_extract);
EXPORT_SYMBOL(ipc_log_extract);


@@ -835,6 +847,8 @@ void *ipc_log_context_create(int max_num_pages,
	ctxt->nd_read_page = ctxt->first_page;
	ctxt->nd_read_page = ctxt->first_page;
	ctxt->write_avail = max_num_pages * LOG_PAGE_DATA_SIZE;
	ctxt->write_avail = max_num_pages * LOG_PAGE_DATA_SIZE;
	ctxt->header_size = sizeof(struct ipc_log_page_header);
	ctxt->header_size = sizeof(struct ipc_log_page_header);
	kref_init(&ctxt->refcount);
	ctxt->destroyed = false;
	create_ctx_debugfs(ctxt, mod_name);
	create_ctx_debugfs(ctxt, mod_name);


	/* set magic last to signal context init is complete */
	/* set magic last to signal context init is complete */
@@ -857,6 +871,21 @@ void *ipc_log_context_create(int max_num_pages,
}
}
EXPORT_SYMBOL(ipc_log_context_create);
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
 * Destroy debug log context
 *
 *
@@ -865,25 +894,24 @@ EXPORT_SYMBOL(ipc_log_context_create);
int ipc_log_context_destroy(void *ctxt)
int ipc_log_context_destroy(void *ctxt)
{
{
	struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt;
	struct ipc_log_context *ilctxt = (struct ipc_log_context *)ctxt;
	struct ipc_log_page *pg = NULL;
	unsigned long flags;
	unsigned long flags;


	if (!ilctxt)
	if (!ilctxt)
		return 0;
		return 0;


	while (!list_empty(&ilctxt->page_list)) {
	debugfs_remove_recursive(ilctxt->dent);
		pg = get_first_page(ctxt);

		list_del(&pg->hdr.list);
	spin_lock(&ilctxt->context_lock_lhb1);
		kfree(pg);
	ilctxt->destroyed = true;
	}
	complete_all(&ilctxt->read_avail);
	spin_unlock(&ilctxt->context_lock_lhb1);


	write_lock_irqsave(&context_list_lock_lha1, flags);
	write_lock_irqsave(&context_list_lock_lha1, flags);
	list_del(&ilctxt->list);
	list_del(&ilctxt->list);
	write_unlock_irqrestore(&context_list_lock_lha1, flags);
	write_unlock_irqrestore(&context_list_lock_lha1, flags);


	debugfs_remove_recursive(ilctxt->dent);
	ipc_log_context_put(ilctxt);


	kfree(ilctxt);
	return 0;
	return 0;
}
}
EXPORT_SYMBOL(ipc_log_context_destroy);
EXPORT_SYMBOL(ipc_log_context_destroy);
+25 −6
Original line number Original line Diff line number Diff line
/* 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
 * 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
 * it under the terms of the GNU General Public License version 2 and
@@ -74,23 +74,42 @@ static int debug_log(struct ipc_log_context *ilctxt,
static ssize_t debug_read_helper(struct file *file, char __user *buff,
static ssize_t debug_read_helper(struct file *file, char __user *buff,
				 size_t count, loff_t *ppos, int cont)
				 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;
	char *buffer;
	int bsize;
	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);
	buffer = kmalloc(count, GFP_KERNEL);
	if (!buffer)
	if (!buffer) {
		return -ENOMEM;
		bsize = -ENOMEM;
		goto done;
	}


	bsize = debug_log(ilctxt, buffer, count, cont);
	bsize = debug_log(ilctxt, buffer, count, cont);

	if (bsize > 0) {
	if (bsize > 0) {
		if (copy_to_user(buff, buffer, bsize)) {
		if (copy_to_user(buff, buffer, bsize)) {
			bsize = -EFAULT;
			kfree(buffer);
			kfree(buffer);
			return -EFAULT;
			goto done;
		}
		}
		*ppos += bsize;
		*ppos += bsize;
	}
	}
	kfree(buffer);
	kfree(buffer);

done:
	ipc_log_context_put(ilctxt);
	return bsize;
	return bsize;
}
}


@@ -127,7 +146,7 @@ static void debug_create(const char *name, mode_t mode,
			 struct ipc_log_context *ilctxt,
			 struct ipc_log_context *ilctxt,
			 const struct file_operations *fops)
			 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,
static void dfunc_string(struct encode_context *ectxt,
+10 −1
Original line number Original line Diff line number Diff line
/* 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
 * 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
 * it under the terms of the GNU General Public License version 2 and
@@ -119,6 +119,8 @@ struct ipc_log_context {
	struct list_head dfunc_info_list;
	struct list_head dfunc_info_list;
	spinlock_t context_lock_lhb1;
	spinlock_t context_lock_lhb1;
	struct completion read_avail;
	struct completion read_avail;
	struct kref refcount;
	bool destroyed;
};
};


struct dfunc_info {
struct dfunc_info {
@@ -147,6 +149,13 @@ enum {
			((x) < TSV_TYPE_MSG_END))
			((x) < TSV_TYPE_MSG_END))
#define MAX_MSG_DECODED_SIZE (MAX_MSG_SIZE*4)
#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))
#if (defined(CONFIG_DEBUG_FS))
void check_and_create_debugfs(void);
void check_and_create_debugfs(void);