Merge "leds: qpnp-flash: Fix possible race condition in debugfs"
This commit is contained in:
commit
9f6ca698a1
1 changed files with 56 additions and 22 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue