mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-12-24 06:59:27 -05:00
239 lines
6.3 KiB
Diff
239 lines
6.3 KiB
Diff
From f11ae3df500bc2a093ddffee6ea40da859de0fa9 Mon Sep 17 00:00:00 2001
|
|
From: ansharma <ansharma@codeaurora.org>
|
|
Date: Thu, 19 Jan 2017 20:22:14 +0530
|
|
Subject: leds: qpnp-flash: Fix possible race condition in debugfs
|
|
|
|
There is a possible race condition when debugfs files are concurrently
|
|
accessed by multiple threads. Fix this.
|
|
|
|
CRs-Fixed: 1109420, 1109326
|
|
Change-Id: I19e9107079ac8d039b12a37ae612727f824552d4
|
|
Signed-off-by: ansharma <ansharma@codeaurora.org>
|
|
---
|
|
drivers/leds/leds-qpnp-flash.c | 80 ++++++++++++++++++++++++++++++------------
|
|
1 file changed, 57 insertions(+), 23 deletions(-)
|
|
|
|
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
|
|
index 43298a7..56ba8f5 100644
|
|
--- a/drivers/leds/leds-qpnp-flash.c
|
|
+++ b/drivers/leds/leds-qpnp-flash.c
|
|
@@ -1,4 +1,4 @@
|
|
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
|
|
+/* Copyright (c) 2014-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
|
|
@@ -225,6 +225,7 @@ struct flash_led_platform_data {
|
|
};
|
|
|
|
struct qpnp_flash_led_buffer {
|
|
+ struct mutex debugfs_lock; /* Prevent thread concurrency */
|
|
size_t rpos;
|
|
size_t wpos;
|
|
size_t len;
|
|
@@ -280,6 +281,7 @@ static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led,
|
|
log->rpos = 0;
|
|
log->wpos = 0;
|
|
log->len = logbufsize - sizeof(*log);
|
|
+ mutex_init(&log->debugfs_lock);
|
|
led->log = log;
|
|
|
|
led->buffer_cnt = 1;
|
|
@@ -301,20 +303,26 @@ static int flash_led_dfs_close(struct inode *inode, struct file *file)
|
|
|
|
if (led && led->log) {
|
|
file->private_data = NULL;
|
|
+ mutex_destroy(&led->log->debugfs_lock);
|
|
kfree(led->log);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
+#define MIN_BUFFER_WRITE_LEN 20
|
|
static int print_to_log(struct qpnp_flash_led_buffer *log,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int cnt;
|
|
- char *log_buf = &log->data[log->wpos];
|
|
+ char *log_buf;
|
|
size_t size = log->len - log->wpos;
|
|
|
|
+ if (size < MIN_BUFFER_WRITE_LEN)
|
|
+ return 0; /* not enough buffer left */
|
|
+
|
|
+ log_buf = &log->data[log->wpos];
|
|
va_start(args, fmt);
|
|
cnt = vscnprintf(log_buf, size, fmt, args);
|
|
va_end(args);
|
|
@@ -328,12 +336,14 @@ static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
|
|
struct qpnp_flash_led *led = fp->private_data;
|
|
struct qpnp_flash_led_buffer *log = led->log;
|
|
u8 val;
|
|
- int rc;
|
|
+ int rc = 0;
|
|
size_t len;
|
|
size_t ret;
|
|
|
|
- if (log->rpos >= log->wpos && led->buffer_cnt == 0)
|
|
- return 0;
|
|
+ mutex_lock(&log->debugfs_lock);
|
|
+ if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
|
|
+ ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
|
|
+ goto unlock_mutex;
|
|
|
|
rc = spmi_ext_register_readl(led->spmi_dev->ctrl,
|
|
led->spmi_dev->sid, INT_LATCHED_STS(led->base), &val, 1);
|
|
@@ -341,17 +351,17 @@ static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
|
|
dev_err(&led->spmi_dev->dev,
|
|
"Unable to read from address %x, rc(%d)\n",
|
|
INT_LATCHED_STS(led->base), rc);
|
|
- return -EINVAL;
|
|
+ goto unlock_mutex;
|
|
}
|
|
led->buffer_cnt--;
|
|
|
|
rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base));
|
|
if (rc == 0)
|
|
- return rc;
|
|
+ goto unlock_mutex;
|
|
|
|
rc = print_to_log(log, "0x%02X ", val);
|
|
if (rc == 0)
|
|
- return rc;
|
|
+ goto unlock_mutex;
|
|
|
|
if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
|
|
log->data[log->wpos - 1] = '\n';
|
|
@@ -361,36 +371,43 @@ static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
|
|
ret = copy_to_user(buf, &log->data[log->rpos], len);
|
|
if (ret) {
|
|
pr_err("error copy register value to user\n");
|
|
- return -EFAULT;
|
|
+ rc = -EFAULT;
|
|
+ goto unlock_mutex;
|
|
}
|
|
|
|
len -= ret;
|
|
*ppos += len;
|
|
log->rpos += len;
|
|
|
|
- return len;
|
|
+ rc = len;
|
|
+
|
|
+unlock_mutex:
|
|
+ mutex_unlock(&log->debugfs_lock);
|
|
+ return rc;
|
|
}
|
|
|
|
static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
|
|
size_t count, loff_t *ppos) {
|
|
struct qpnp_flash_led *led = fp->private_data;
|
|
struct qpnp_flash_led_buffer *log = led->log;
|
|
- int rc;
|
|
+ int rc = 0;
|
|
size_t len;
|
|
size_t ret;
|
|
|
|
- if (log->rpos >= log->wpos && led->buffer_cnt == 0)
|
|
- return 0;
|
|
+ mutex_lock(&log->debugfs_lock);
|
|
+ if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
|
|
+ ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
|
|
+ goto unlock_mutex;
|
|
|
|
led->buffer_cnt--;
|
|
|
|
rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base));
|
|
if (rc == 0)
|
|
- return rc;
|
|
+ goto unlock_mutex;
|
|
|
|
rc = print_to_log(log, "0x%02X ", led->fault_reg);
|
|
if (rc == 0)
|
|
- return rc;
|
|
+ goto unlock_mutex;
|
|
|
|
if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
|
|
log->data[log->wpos - 1] = '\n';
|
|
@@ -400,14 +417,19 @@ static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
|
|
ret = copy_to_user(buf, &log->data[log->rpos], len);
|
|
if (ret) {
|
|
pr_err("error copy register value to user\n");
|
|
- return -EFAULT;
|
|
+ rc = -EFAULT;
|
|
+ goto unlock_mutex;
|
|
}
|
|
|
|
len -= ret;
|
|
*ppos += len;
|
|
log->rpos += len;
|
|
|
|
- return len;
|
|
+ rc = len;
|
|
+
|
|
+unlock_mutex:
|
|
+ mutex_unlock(&log->debugfs_lock);
|
|
+ return rc;
|
|
}
|
|
|
|
static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
|
|
@@ -420,10 +442,14 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
|
|
size_t ret = 0;
|
|
|
|
struct qpnp_flash_led *led = file->private_data;
|
|
- char *kbuf = kmalloc(count + 1, GFP_KERNEL);
|
|
+ char *kbuf;
|
|
|
|
- if (!kbuf)
|
|
- return -ENOMEM;
|
|
+ mutex_lock(&led->log->debugfs_lock);
|
|
+ kbuf = kmalloc(count + 1, GFP_KERNEL);
|
|
+ if (!kbuf) {
|
|
+ ret = -ENOMEM;
|
|
+ goto unlock_mutex;
|
|
+ }
|
|
|
|
ret = copy_from_user(kbuf, buf, count);
|
|
if (!ret) {
|
|
@@ -452,6 +478,8 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
|
|
|
|
free_buf:
|
|
kfree(kbuf);
|
|
+unlock_mutex:
|
|
+ mutex_unlock(&led->log->debugfs_lock);
|
|
return ret;
|
|
}
|
|
|
|
@@ -464,10 +492,14 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file,
|
|
int data;
|
|
size_t ret = 0;
|
|
struct qpnp_flash_led *led = file->private_data;
|
|
- char *kbuf = kmalloc(count + 1, GFP_KERNEL);
|
|
+ char *kbuf;
|
|
|
|
- if (!kbuf)
|
|
- return -ENOMEM;
|
|
+ mutex_lock(&led->log->debugfs_lock);
|
|
+ kbuf = kmalloc(count + 1, GFP_KERNEL);
|
|
+ if (!kbuf) {
|
|
+ ret = -ENOMEM;
|
|
+ goto unlock_mutex;
|
|
+ }
|
|
|
|
ret = copy_from_user(kbuf, buf, count);
|
|
if (ret == count) {
|
|
@@ -495,6 +527,8 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file,
|
|
|
|
free_buf:
|
|
kfree(kbuf);
|
|
+unlock_mutex:
|
|
+ mutex_unlock(&led->log->debugfs_lock);
|
|
return ret;
|
|
}
|
|
|
|
--
|
|
cgit v1.1
|
|
|