Merge "leds: qpnp-flash: Fix possible race condition in debugfs"

This commit is contained in:
Linux Build Service Account 2017-02-15 17:01:30 -08:00 committed by Gerrit - the friendly Code Review server
commit 9f6ca698a1

View file

@ -226,6 +226,7 @@ struct flash_led_platform_data {
}; };
struct qpnp_flash_led_buffer { struct qpnp_flash_led_buffer {
struct mutex debugfs_lock; /* Prevent thread concurrency */
size_t rpos; size_t rpos;
size_t wpos; size_t wpos;
size_t len; size_t len;
@ -282,6 +283,7 @@ static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led,
log->rpos = 0; log->rpos = 0;
log->wpos = 0; log->wpos = 0;
log->len = logbufsize - sizeof(*log); log->len = logbufsize - sizeof(*log);
mutex_init(&log->debugfs_lock);
led->log = log; led->log = log;
led->buffer_cnt = 1; led->buffer_cnt = 1;
@ -303,20 +305,26 @@ static int flash_led_dfs_close(struct inode *inode, struct file *file)
if (led && led->log) { if (led && led->log) {
file->private_data = NULL; file->private_data = NULL;
mutex_destroy(&led->log->debugfs_lock);
kfree(led->log); kfree(led->log);
} }
return 0; return 0;
} }
#define MIN_BUFFER_WRITE_LEN 20
static int print_to_log(struct qpnp_flash_led_buffer *log, static int print_to_log(struct qpnp_flash_led_buffer *log,
const char *fmt, ...) const char *fmt, ...)
{ {
va_list args; va_list args;
int cnt; int cnt;
char *log_buf = &log->data[log->wpos]; char *log_buf;
size_t size = log->len - log->wpos; 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); va_start(args, fmt);
cnt = vscnprintf(log_buf, size, fmt, args); cnt = vscnprintf(log_buf, size, fmt, args);
va_end(args); va_end(args);
@ -330,29 +338,31 @@ 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 *led = fp->private_data;
struct qpnp_flash_led_buffer *log = led->log; struct qpnp_flash_led_buffer *log = led->log;
uint val; uint val;
int rc; int rc = 0;
size_t len; size_t len;
size_t ret; size_t ret;
if (log->rpos >= log->wpos && led->buffer_cnt == 0) mutex_lock(&log->debugfs_lock);
return 0; if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
goto unlock_mutex;
rc = regmap_read(led->regmap, INT_LATCHED_STS(led->base), &val); rc = regmap_read(led->regmap, INT_LATCHED_STS(led->base), &val);
if (rc) { if (rc) {
dev_err(&led->pdev->dev, dev_err(&led->pdev->dev,
"Unable to read from address %x, rc(%d)\n", "Unable to read from address %x, rc(%d)\n",
INT_LATCHED_STS(led->base), rc); INT_LATCHED_STS(led->base), rc);
return -EINVAL; goto unlock_mutex;
} }
led->buffer_cnt--; led->buffer_cnt--;
rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base)); rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base));
if (rc == 0) if (rc == 0)
return rc; goto unlock_mutex;
rc = print_to_log(log, "0x%02X ", val); rc = print_to_log(log, "0x%02X ", val);
if (rc == 0) if (rc == 0)
return rc; goto unlock_mutex;
if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
log->data[log->wpos - 1] = '\n'; log->data[log->wpos - 1] = '\n';
@ -362,36 +372,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); ret = copy_to_user(buf, &log->data[log->rpos], len);
if (ret) { if (ret) {
pr_err("error copy register value to user\n"); pr_err("error copy register value to user\n");
return -EFAULT; rc = -EFAULT;
goto unlock_mutex;
} }
len -= ret; len -= ret;
*ppos += len; *ppos += len;
log->rpos += 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, static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
size_t count, loff_t *ppos) { size_t count, loff_t *ppos) {
struct qpnp_flash_led *led = fp->private_data; struct qpnp_flash_led *led = fp->private_data;
struct qpnp_flash_led_buffer *log = led->log; struct qpnp_flash_led_buffer *log = led->log;
int rc; int rc = 0;
size_t len; size_t len;
size_t ret; size_t ret;
if (log->rpos >= log->wpos && led->buffer_cnt == 0) mutex_lock(&log->debugfs_lock);
return 0; if ((log->rpos >= log->wpos && led->buffer_cnt == 0) ||
((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
goto unlock_mutex;
led->buffer_cnt--; led->buffer_cnt--;
rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base)); rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base));
if (rc == 0) if (rc == 0)
return rc; goto unlock_mutex;
rc = print_to_log(log, "0x%02X ", led->fault_reg); rc = print_to_log(log, "0x%02X ", led->fault_reg);
if (rc == 0) if (rc == 0)
return rc; goto unlock_mutex;
if (log->wpos > 0 && log->data[log->wpos - 1] == ' ') if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
log->data[log->wpos - 1] = '\n'; log->data[log->wpos - 1] = '\n';
@ -401,14 +418,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); ret = copy_to_user(buf, &log->data[log->rpos], len);
if (ret) { if (ret) {
pr_err("error copy register value to user\n"); pr_err("error copy register value to user\n");
return -EFAULT; rc = -EFAULT;
goto unlock_mutex;
} }
len -= ret; len -= ret;
*ppos += len; *ppos += len;
log->rpos += 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, static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
@ -421,10 +443,14 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
size_t ret = 0; size_t ret = 0;
struct qpnp_flash_led *led = file->private_data; struct qpnp_flash_led *led = file->private_data;
char *kbuf = kmalloc(count + 1, GFP_KERNEL); char *kbuf;
if (!kbuf) mutex_lock(&led->log->debugfs_lock);
return -ENOMEM; kbuf = kmalloc(count + 1, GFP_KERNEL);
if (!kbuf) {
ret = -ENOMEM;
goto unlock_mutex;
}
ret = copy_from_user(kbuf, buf, count); ret = copy_from_user(kbuf, buf, count);
if (!ret) { if (!ret) {
@ -453,6 +479,8 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
free_buf: free_buf:
kfree(kbuf); kfree(kbuf);
unlock_mutex:
mutex_unlock(&led->log->debugfs_lock);
return ret; return ret;
} }
@ -465,10 +493,14 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file,
int data; int data;
size_t ret = 0; size_t ret = 0;
struct qpnp_flash_led *led = file->private_data; struct qpnp_flash_led *led = file->private_data;
char *kbuf = kmalloc(count + 1, GFP_KERNEL); char *kbuf;
if (!kbuf) mutex_lock(&led->log->debugfs_lock);
return -ENOMEM; kbuf = kmalloc(count + 1, GFP_KERNEL);
if (!kbuf) {
ret = -ENOMEM;
goto unlock_mutex;
}
ret = copy_from_user(kbuf, buf, count); ret = copy_from_user(kbuf, buf, count);
if (ret == count) { if (ret == count) {
@ -496,6 +528,8 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file,
free_buf: free_buf:
kfree(kbuf); kfree(kbuf);
unlock_mutex:
mutex_unlock(&led->log->debugfs_lock);
return ret; return ret;
} }