diag: Protect shared resources while mask updates
DIAG sends mask updates to peripherals over SMD control channels.
It uses several buffers, variables and SMD channel for this update
process. These shared resources risk corruption from several work
queue functions. Putting them under a mutex will ensure they do
not get overwritten.
Change-Id: I1d0a82b60dd8384d4b44ace29b25d60afe2ac5ad
Signed-off-by: Shalabh Jain <shalabhj@codeaurora.org>
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 656223b..49d687d 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -162,6 +162,7 @@
int count_write_struct_pool;
int used;
/* Buffers for masks */
+ struct mutex diag_cntl_mutex;
struct diag_ctrl_event_mask *event_mask;
struct diag_ctrl_log_mask *log_mask;
struct diag_ctrl_msg_mask *msg_mask;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 4d3fc29..a7a4a2a 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -22,7 +22,6 @@
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/of.h>
-#include <linux/spinlock.h>
#include <linux/kmemleak.h>
#ifdef CONFIG_DIAG_OVER_USB
#include <mach/usbdiag.h>
@@ -60,7 +59,6 @@
int num_items;
int index;
};
-spinlock_t diag_cntl_lock;
#define CREATE_MSG_MASK_TBL_ROW(XX) \
do { \
@@ -775,9 +773,9 @@
void *buf = driver->buf_log_mask_update;
int header_size = sizeof(struct diag_ctrl_log_mask);
struct mask_info *ptr = (struct mask_info *)driver->log_masks;
- int i, size, wr_size = -ENOMEM, retry_count = 0;
- unsigned long flags = 0;
+ int i, size, wr_size = -ENOMEM, retry_count = 0, timer;
+ mutex_lock(&driver->diag_cntl_mutex);
for (i = 0; i < MAX_EQUIP_ID; i++) {
size = (ptr->num_items+7)/8;
/* reached null entry */
@@ -798,15 +796,13 @@
size);
if (ch) {
while (retry_count < 3) {
- spin_lock_irqsave(&diag_cntl_lock,
- flags);
wr_size = smd_write(ch, buf,
header_size + size);
- spin_unlock_irqrestore(&diag_cntl_lock,
- flags);
if (wr_size == -ENOMEM) {
retry_count++;
- usleep(20000);
+ for (timer = 0; timer < 5;
+ timer++)
+ udelay(2000);
} else
break;
}
@@ -822,17 +818,19 @@
}
ptr++;
}
+ mutex_unlock(&driver->diag_cntl_mutex);
}
void diag_send_event_mask_update(smd_channel_t *ch, int num_bytes)
{
void *buf = driver->buf_event_mask_update;
int header_size = sizeof(struct diag_ctrl_event_mask);
- int wr_size = -ENOMEM, retry_count = 0;
- unsigned long flags = 0;
+ int wr_size = -ENOMEM, retry_count = 0, timer;
+ mutex_lock(&driver->diag_cntl_mutex);
if (num_bytes == 0) {
pr_debug("diag: event mask not set yet, so no update\n");
+ mutex_unlock(&driver->diag_cntl_mutex);
return;
}
/* send event mask update */
@@ -846,12 +844,11 @@
memcpy(buf+header_size, driver->event_masks, num_bytes);
if (ch) {
while (retry_count < 3) {
- spin_lock_irqsave(&diag_cntl_lock, flags);
wr_size = smd_write(ch, buf, header_size + num_bytes);
- spin_unlock_irqrestore(&diag_cntl_lock, flags);
if (wr_size == -ENOMEM) {
retry_count++;
- usleep(20000);
+ for (timer = 0; timer < 5; timer++)
+ udelay(2000);
} else
break;
}
@@ -860,17 +857,18 @@
wr_size, header_size + num_bytes);
} else
pr_err("diag: ch not valid for event update\n");
+ mutex_unlock(&driver->diag_cntl_mutex);
}
void diag_send_msg_mask_update(smd_channel_t *ch, int updated_ssid_first,
int updated_ssid_last, int proc)
{
void *buf = driver->buf_msg_mask_update;
- int first, last, size = -ENOMEM, retry_count = 0;
+ int first, last, size = -ENOMEM, retry_count = 0, timer;
int header_size = sizeof(struct diag_ctrl_msg_mask);
uint8_t *ptr = driver->msg_masks;
- unsigned long flags = 0;
+ mutex_lock(&driver->diag_cntl_mutex);
while (*(uint32_t *)(ptr + 4)) {
first = *(uint32_t *)ptr;
ptr += 4;
@@ -893,31 +891,31 @@
4 * (driver->msg_mask->msg_mask_size));
if (ch) {
while (retry_count < 3) {
- spin_lock_irqsave(&diag_cntl_lock,
- flags);
size = smd_write(ch, buf, header_size +
4*(driver->msg_mask->msg_mask_size));
- spin_unlock_irqrestore(&diag_cntl_lock,
- flags);
if (size == -ENOMEM) {
retry_count++;
- usleep(20000);
+ for (timer = 0; timer < 5;
+ timer++)
+ udelay(2000);
} else
break;
}
if (size != header_size +
4*(driver->msg_mask->msg_mask_size))
- pr_err("diag: msg mask update fail %d,"
- " tried %d\n", size,
- header_size + 4*(driver->msg_mask->msg_mask_size));
+ pr_err("diag: proc %d, msg mask update "
+ "fail %d, tried %d\n", proc, size,
+ header_size + 4*(driver->msg_mask->msg_mask_size));
else
pr_debug("diag: sending mask update for"
- " ssid first %d, last %d on PROC %d\n", first, last, proc);
+ "ssid first %d, last %d on PROC %d\n", first, last, proc);
} else
- pr_err("diag: ch invalid msg mask update\n");
+ pr_err("diag: proc %d, ch invalid msg mask"
+ "update\n", proc);
}
ptr += MAX_SSID_PER_RANGE*4;
}
+ mutex_unlock(&driver->diag_cntl_mutex);
}
static int diag_process_apps_pkt(unsigned char *buf, int len)
@@ -1790,7 +1788,7 @@
diag_debug_buf_idx = 0;
driver->read_len_legacy = 0;
driver->use_device_tree = has_device_tree();
- spin_lock_init(&diag_cntl_lock);
+ mutex_init(&driver->diag_cntl_mutex);
if (driver->event_mask == NULL) {
driver->event_mask = kzalloc(sizeof(