diag: Add fixes for DCI
1) Fix an issue where after running a DCI app and exiting it,
second app does not receive any DCI data. Issue was due to
incorrect state of a flag used by SMD channel.
2) Add a mutex around shared data structures like cumulative masks.
Multiple clients may want to change DCI mask at the same time, so
mutex will prevent any corruption
3) Add support for health commands. The client request how many logs
were dropped and received.
Change-Id: Ica12b3c93aa4dda9b04aa13cd5a9f8dbc412148c
Signed-off-by: Shalabh Jain <shalabhj@codeaurora.org>
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 7c0c0b9..5cd5ce9 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -35,6 +35,8 @@
unsigned int dci_max_clients = 10;
unsigned char dci_cumulative_log_mask[DCI_LOG_MASK_SIZE];
unsigned char dci_cumulative_event_mask[DCI_EVENT_MASK_SIZE];
+struct mutex dci_log_mask_mutex;
+struct mutex dci_event_mask_mutex;
#define DCI_CHK_CAPACITY(entry, new_data_len) \
((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0) \
@@ -91,14 +93,15 @@
read_bytes += 5 + dci_pkt_len;
buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
}
- driver->in_busy_dci = 1;
/* wake up all sleeping DCI clients which have some data */
for (i = 0; i < MAX_DCI_CLIENTS; i++)
if (driver->dci_client_tbl[i].client &&
- driver->dci_client_tbl[i].data_len)
+ driver->dci_client_tbl[i].data_len) {
+ driver->in_busy_dci = 1;
diag_update_sleeping_process(
driver->dci_client_tbl[i].client->tgid,
DCI_DATA_TYPE);
+ }
}
}
@@ -224,6 +227,8 @@
dropped_events++;
return;
}
+ driver->dci_client_tbl[i].
+ received_events++;
*(int *)(entry->dci_data+
entry->data_len) = DCI_EVENT_TYPE;
memcpy(entry->dci_data+
@@ -281,6 +286,7 @@
dropped_logs++;
return;
}
+ driver->dci_client_tbl[i].received_logs++;
*(int *)(entry->dci_data+entry->data_len) =
DCI_LOG_TYPE;
memcpy(entry->dci_data+entry->data_len+4, buf+4,
@@ -378,14 +384,6 @@
}
}
mutex_lock(&driver->dci_mutex);
- if (new_dci_client)
- driver->num_dci_client++;
- if (driver->num_dci_client > MAX_DCI_CLIENTS) {
- pr_info("diag: Max DCI Client limit reached\n");
- driver->num_dci_client--;
- mutex_unlock(&driver->dci_mutex);
- return ret;
- }
/* Make an entry in kernel DCI table */
driver->dci_tag++;
for (i = 0; i < dci_max_reg; i++) {
@@ -483,7 +481,7 @@
temp += 4;
head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
- pr_info("diag: head of dci log mask %p\n", head_log_mask_ptr);
+ pr_debug("diag: head of dci log mask %p\n", head_log_mask_ptr);
count = 0; /* iterator for extracting log codes */
while (count < num_codes) {
log_code = *(uint16_t *)temp;
@@ -500,11 +498,11 @@
while (log_mask_ptr) {
if (*log_mask_ptr == equip_id) {
found = 1;
- pr_info("diag: find equip id = %x at %p\n",
+ pr_debug("diag: find equip id = %x at %p\n",
equip_id, log_mask_ptr);
break;
} else {
- pr_info("diag: did not find equip id = %x at %p\n",
+ pr_debug("diag: did not find equip id = %x at %p\n",
equip_id, log_mask_ptr);
log_mask_ptr += 514;
}
@@ -583,9 +581,11 @@
uint8_t *update_ptr = dci_cumulative_event_mask;
uint8_t *event_mask_ptr;
+ mutex_lock(&dci_event_mask_mutex);
event_mask_ptr = driver->dci_client_tbl[client_index].dci_event_mask;
for (i = 0; i < DCI_EVENT_MASK_SIZE; i++)
*(update_ptr+i) |= *(event_mask_ptr+i);
+ mutex_unlock(&dci_event_mask_mutex);
}
void diag_send_dci_event_mask(smd_channel_t *ch)
@@ -631,6 +631,7 @@
uint8_t *log_mask_ptr =
driver->dci_client_tbl[client_index].dci_log_mask;
+ mutex_lock(&dci_log_mask_mutex);
*update_ptr = 0; /* add first equip id */
/* skip the first equip id */
update_ptr++; log_mask_ptr++;
@@ -644,6 +645,7 @@
update_ptr++;
log_mask_ptr++;
}
+ mutex_unlock(&dci_log_mask_mutex);
}
void diag_send_dci_log_mask(smd_channel_t *ch)
@@ -743,6 +745,8 @@
driver->num_dci_client = 0;
driver->in_busy_dci = 0;
mutex_init(&driver->dci_mutex);
+ mutex_init(&dci_log_mask_mutex);
+ mutex_init(&dci_event_mask_mutex);
if (driver->buf_in_dci == NULL) {
driver->buf_in_dci = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
if (driver->buf_in_dci == NULL)
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 97a285c..afcabcc 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -53,6 +53,17 @@
int total_capacity;
int dropped_logs;
int dropped_events;
+ int received_logs;
+ int received_events;
+};
+
+/* This is used for DCI health stats */
+struct diag_dci_health_stats {
+ int dropped_logs;
+ int dropped_events;
+ int received_logs;
+ int received_events;
+ int reset_status;
};
enum {
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 100c56f..aea58b0 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -241,17 +241,23 @@
int i = 0;
struct diagchar_priv *diagpriv_data = file->private_data;
+ pr_debug("diag: process exit %s\n", current->comm);
if (!(file->private_data)) {
pr_alert("diag: Invalid file pointer");
return -ENOMEM;
}
-
- /* clean up any DCI registrations for this client
+ /* clean up any DCI registrations, if this is a DCI client
* This will specially help in case of ungraceful exit of any DCI client
* This call will remove any pending registrations of such client
*/
- diagchar_ioctl(NULL, DIAG_IOCTL_DCI_DEINIT, 0);
-
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ if (driver->dci_client_tbl[i].client &&
+ driver->dci_client_tbl[i].client->tgid ==
+ current->tgid) {
+ diagchar_ioctl(NULL, DIAG_IOCTL_DCI_DEINIT, 0);
+ break;
+ }
+ }
/* If the exiting process is the socket process */
if (driver->socket_process &&
(driver->socket_process->tgid == current->tgid)) {
@@ -372,7 +378,9 @@
int success = -1;
void *temp_buf;
uint16_t support_list = 0;
- struct diag_dci_client_tbl *notify_params;
+ struct diag_dci_client_tbl *params =
+ kzalloc(sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
+ struct diag_dci_health_stats stats;
int status;
if (iocmd == DIAG_IOCTL_COMMAND_REG) {
@@ -446,8 +454,12 @@
return DIAG_DCI_NO_REG;
if (driver->num_dci_client >= MAX_DCI_CLIENTS)
return DIAG_DCI_NO_REG;
- notify_params = (struct diag_dci_client_tbl *) ioarg;
+ if (copy_from_user(params, (void *)ioarg,
+ sizeof(struct diag_dci_client_tbl)))
+ return -EFAULT;
mutex_lock(&driver->dci_mutex);
+ if (!(driver->num_dci_client))
+ driver->in_busy_dci = 0;
driver->num_dci_client++;
pr_debug("diag: id = %d\n", driver->dci_client_id);
driver->dci_client_id++;
@@ -455,9 +467,9 @@
if (driver->dci_client_tbl[i].client == NULL) {
driver->dci_client_tbl[i].client = current;
driver->dci_client_tbl[i].list =
- notify_params->list;
+ params->list;
driver->dci_client_tbl[i].signal_type =
- notify_params->signal_type;
+ params->signal_type;
create_dci_log_mask_tbl(driver->
dci_client_tbl[i].dci_log_mask);
create_dci_event_mask_tbl(driver->
@@ -469,6 +481,8 @@
IN_BUF_SIZE;
driver->dci_client_tbl[i].dropped_logs = 0;
driver->dci_client_tbl[i].dropped_events = 0;
+ driver->dci_client_tbl[i].received_logs = 0;
+ driver->dci_client_tbl[i].received_events = 0;
break;
}
}
@@ -478,32 +492,52 @@
success = -1;
/* Delete this process from DCI table */
mutex_lock(&driver->dci_mutex);
- for (i = 0; i < dci_max_reg; i++) {
- if (driver->req_tracking_tbl[i].pid == current->tgid) {
- pr_debug("diag: delete %d\n", current->tgid);
+ for (i = 0; i < dci_max_reg; i++)
+ if (driver->req_tracking_tbl[i].pid == current->tgid)
driver->req_tracking_tbl[i].pid = 0;
- success = i;
- }
- }
for (i = 0; i < MAX_DCI_CLIENTS; i++) {
- if (driver->dci_client_tbl[i].client == current) {
+ if (driver->dci_client_tbl[i].client &&
+ driver->dci_client_tbl[i].client->tgid ==
+ current->tgid) {
driver->dci_client_tbl[i].client = NULL;
+ success = i;
break;
}
}
- /* if any registrations were deleted successfully OR a valid
- client_id was sent in DEINIT call , then its DCI client */
- if (success >= 0 || ioarg)
+ if (success >= 0)
driver->num_dci_client--;
- driver->num_dci_client--;
mutex_unlock(&driver->dci_mutex);
- pr_debug("diag: complete deleting registrations\n");
return success;
} else if (iocmd == DIAG_IOCTL_DCI_SUPPORT) {
if (driver->ch_dci)
support_list = support_list | DIAG_CON_MPSS;
*(uint16_t *)ioarg = support_list;
return DIAG_DCI_NO_ERROR;
+ } else if (iocmd == DIAG_IOCTL_DCI_HEALTH_STATS) {
+ if (copy_from_user(&stats, (void *)ioarg,
+ sizeof(struct diag_dci_health_stats)))
+ return -EFAULT;
+ for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+ params = &(driver->dci_client_tbl[i]);
+ if (params->client &&
+ params->client->tgid == current->tgid) {
+ stats.dropped_logs = params->dropped_logs;
+ stats.dropped_events = params->dropped_events;
+ stats.received_logs = params->received_logs;
+ stats.received_events = params->received_events;
+ if (stats.reset_status) {
+ params->dropped_logs = 0;
+ params->dropped_events = 0;
+ params->received_logs = 0;
+ params->received_events = 0;
+ }
+ break;
+ }
+ }
+ if (copy_to_user((void *)ioarg, &stats,
+ sizeof(struct diag_dci_health_stats)))
+ return -EFAULT;
+ return DIAG_DCI_NO_ERROR;
} else if (iocmd == DIAG_IOCTL_LSM_DEINIT) {
for (i = 0; i < driver->num_clients; i++)
if (driver->client_map[i].pid == current->tgid)