bcmdhd_88: improve data integrity of nv_logger
Bibhay Ranjan [Mon, 26 Sep 2016 13:45:15 +0000 (18:45 +0530)]
Bug 200238576

Change-Id: I8a8ae9d66f4918fadb7107cd41de3c1670a4a4b3
Signed-off-by: Bibhay Ranjan <bibhayr@nvidia.com>
Reviewed-on: http://git-master/r/1227051
Reviewed-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bhadram Varka <vbhadram@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>

drivers/net/wireless/bcmdhd_88/nv_logger.c
drivers/net/wireless/bcmdhd_88/nv_logger.h

index 9360a24..e353a3a 100644 (file)
@@ -25,9 +25,7 @@ char nv_error_buffer[MAX_ERROR_SIZE];
 bool enable_file_logging;
 struct list_head list1;
 struct list_head list2;
-static int reset_log_size;
-struct mutex sysfs_dump_mtx;
-struct mutex suspend_lock;
+bool select_list;
 
 struct workqueue_struct *logger_wqueue;
 struct log_buffer {
@@ -35,6 +33,7 @@ struct log_buffer {
        char *buf;
        char *info;
        int event;
+       int size;
 };
 
 struct log_node {
@@ -61,12 +60,9 @@ void write_log_init()
        if (dhdlog_sysfs_init())
                goto netlink_fail;
 
-       mutex_init(&sysfs_dump_mtx);
-       mutex_init(&suspend_lock);
-
        dhd_log_netlink_send_msg(0, 0, 0, NULL, 0);
        enable_file_logging = true;
-
+       select_list = true;
        return;
 
 netlink_fail:
@@ -93,30 +89,17 @@ int write_log(int event, const char *buf, const char *info)
        int info_len = 0;
        int time_len = 0;
        struct timespec ts;
-       static int list1_size;
-       static int list2_size;
        struct timeval now;
        struct tm date_time;
+       static int count = 0;
 
-       mutex_lock(&suspend_lock);
        if (!enable_file_logging) {
-               mutex_unlock(&suspend_lock);
                return -1;
        }
-       mutex_unlock(&suspend_lock);
 
        if (buf == NULL)
                return -1;
 
-       if (mutex_trylock(&sysfs_dump_mtx)) {
-               if (1 == reset_log_size) {
-                       reset_log_size = 0;
-                       list1_size = 0;
-                       list2_size = 0;
-               }
-               mutex_unlock(&sysfs_dump_mtx);
-       }
-
        switch (event) {
 
        case WLC_E_ESCAN_RESULT:
@@ -166,30 +149,31 @@ int write_log(int event, const char *buf, const char *info)
                        temp->log->info = NULL;
                }
                temp->log->event = event;
-
-       /* whichever list is not busy, dump data in that list */
-               if (1 == atomic_read(&list1_val)) {
+               temp->log->size = time_len + buf_len + info_len;
+               /* whichever list is not busy, dump data in that list.
+                  Make sure we fill the last active list with MAX_LOG_NUM
+                  before switching the lists
+               */
+               if (select_list && (1 == atomic_read(&list1_val))) {
+                       count++;
                        list_add_tail(&(temp->list), &(list1));
-                       list1_size += time_len + buf_len + info_len;
-               } else if (1 == atomic_read(&list2_val)) {
+               } else if (!select_list && (1 == atomic_read(&list2_val))) {
+                       count++;
                        list_add_tail(&(temp->list), &(list2));
-                       list2_size += time_len + buf_len + info_len;
                } else {
                /* send data directly over netlink because both lists are busy*/
                        pr_err("Message dropped due to busy queues");
                }
 
-               if (list1_size > MAX_LOGLIMIT) {
-                       atomic_set(&list1_val, 0);
-                       queue_work(logger_wqueue, &enqueue_work);
-                       list1_size = 0;
-               } else if (list2_size > MAX_LOGLIMIT) {
-                       atomic_set(&list2_val, 0);
+               if (count == MAX_LOG_NUM) {
+                       count = 0;
+                       if (select_list)
+                               atomic_set(&list1_val, 0);
+                       else
+                               atomic_set(&list2_val, 0);
                        queue_work(logger_wqueue, &enqueue_work);
-                       list2_size = 0;
+                       select_list = (select_list == false);
                }
-
-               break;
        }
        return buf_len + info_len;
 }
@@ -199,29 +183,42 @@ void write_queue_work(struct work_struct *work)
        struct log_node *temp = NULL;
        struct list_head *pos = NULL, *n = NULL;
        char *log = NULL;
+       int list1_size = 0;
+       int list2_size = 0;
+
+       /* iterate over the listi until list_for_each_safe empties the list.
+          The list is empty is deduced if pos == head, where for eg &(list1)
+          is the head for list1.
+       */
 
        /* queuing in list1 is blocked, so can dequeue list1*/
        if (atomic_read(&list1_val) == 0) {
-
-               list_for_each_safe(pos, n, &(list1)) {
-                       temp = list_entry(pos, struct log_node, list);
-               /* for the correct string of the event */
-                       strcat(logbuf, temp->log->tmstmp);
-
-                       if (temp->log->buf != NULL)
-                               strcat(logbuf, temp->log->buf);
-                       strcat(logbuf, " ");
-                       if (temp->log->info != NULL)
-                               strcat(logbuf, temp->log->info);
-                       strcat(logbuf, "\n");
-                       list_del(pos);
-                       kfree(temp->log->info);
-                       kfree(temp->log->buf);
-                       kfree(temp->log);
-                       kfree(temp);
+               while (pos != &list1) {
+                       list_for_each_safe(pos, n, &(list1)) {
+                               if (list1_size > MAX_LOGLIMIT)
+                                       break;
+                               temp = list_entry(pos, struct log_node, list);
+                               /* for the correct string of the event */
+                               strcat(logbuf, temp->log->tmstmp);
+
+                               if (temp->log->buf != NULL)
+                                       strcat(logbuf, temp->log->buf);
+                               strcat(logbuf, " ");
+                               if (temp->log->info != NULL)
+                                       strcat(logbuf, temp->log->info);
+                               strcat(logbuf, "\n");
+                               list1_size += temp->log->size;
+
+                               list_del(pos);
+                               kfree(temp->log->info);
+                               kfree(temp->log->buf);
+                               kfree(temp->log);
+                               kfree(temp);
+                       }
+                       write_log_file(logbuf);
+                       memset(logbuf, '\0', sizeof(logbuf));
+                       list1_size = 0;
                }
-               write_log_file(logbuf);
-               memset(logbuf, '\0', sizeof(logbuf));
                /* make this list available for writing now */
                atomic_set(&list1_val, 1);
 
@@ -229,27 +226,32 @@ void write_queue_work(struct work_struct *work)
 
        /* queuing in list1 is blocked, so can dequeue list1*/
        if (atomic_read(&list2_val) == 0) {
-
-               list_for_each_safe(pos, n, &(list2)) {
-                       temp = list_entry(pos, struct log_node, list);
-               /* for the correct string of the event */
-                       strcat(logbuf, temp->log->tmstmp);
-
-                       if (temp->log->buf != NULL)
-                               strcat(logbuf, temp->log->buf);
-                       strcat(logbuf, " ");
-                       if (temp->log->info != NULL)
-                               strcat(logbuf, temp->log->info);
-                       strcat(logbuf, "\n");
-
-                       list_del(pos);
-                       kfree(temp->log->info);
-                       kfree(temp->log->buf);
-                       kfree(temp->log);
-                       kfree(temp);
+               while (pos != &list2) {
+                       list_for_each_safe(pos, n, &(list2)) {
+                               if (list1_size > MAX_LOGLIMIT)
+                                       break;
+                               temp = list_entry(pos, struct log_node, list);
+                       /* for the correct string of the event */
+                               strcat(logbuf, temp->log->tmstmp);
+
+                               if (temp->log->buf != NULL)
+                                       strcat(logbuf, temp->log->buf);
+                               strcat(logbuf, " ");
+                               if (temp->log->info != NULL)
+                                       strcat(logbuf, temp->log->info);
+                               strcat(logbuf, "\n");
+                               list2_size += temp->log->size;
+
+                               list_del(pos);
+                               kfree(temp->log->info);
+                               kfree(temp->log->buf);
+                               kfree(temp->log);
+                               kfree(temp);
+                       }
+                       write_log_file(logbuf);
+                       memset(logbuf, '\0', sizeof(logbuf));
+                       list2_size = 0;
                }
-               write_log_file(logbuf);
-               memset(logbuf, '\0', sizeof(logbuf));
                /* make this list available for writing now */
                atomic_set(&list2_val, 1);
        }
@@ -266,9 +268,7 @@ void write_log_file(const char *log)
 
 void nvlogger_suspend_work()
 {
-       mutex_lock(&suspend_lock);
        enable_file_logging = false;
-       mutex_unlock(&suspend_lock);
        pr_info("nvlogger_suspend_work\n");
        cancel_work_sync(&enqueue_work);
 }
@@ -276,9 +276,7 @@ void nvlogger_suspend_work()
 void nvlogger_resume_work()
 {
        pr_info("nvlogger_resume_work\n");
-       mutex_lock(&suspend_lock);
        enable_file_logging = true;
-       mutex_unlock(&suspend_lock);
 }
 
 #define NETLINK_CARBON     29
@@ -371,9 +369,6 @@ void dumplogs()
        atomic_set(&list1_val, 0);
        atomic_set(&list2_val, 0);
        queue_work(logger_wqueue, &enqueue_work);
-       mutex_lock(&sysfs_dump_mtx);
-       reset_log_size = 1;
-       mutex_unlock(&sysfs_dump_mtx);
 }
 
 struct kobject *dhdlog_sysfs_kobj;
@@ -386,16 +381,12 @@ static ssize_t dhdlog_sysfs_enablelog_store(struct kobject *kobj,
        pr_info("dhdlog_sysfs_enablelog_store = %s", buf);
        if (strncmp(buf, "0", 1) == 0 || strncmp(buf, "false", 5) == 0
                || strncmp(buf, "no", 2) == 0) {
-               mutex_lock(&suspend_lock);
                enable_file_logging = false;
-               mutex_unlock(&suspend_lock);
        } else if (strncmp(buf, "dump", 4) == 0) {
                dumplogs();
        } else if (strncmp(buf, "1", 1) == 0 || strncmp(buf, "true", 4) == 0
                || strncmp(buf, "yes", 3) == 0) {
-               mutex_lock(&suspend_lock);
                enable_file_logging = true;
-               mutex_unlock(&suspend_lock);
        }
 
        return count;
index 056812d..86d767a 100644 (file)
@@ -35,6 +35,7 @@
 #define MAX_LOGLIMIT 1024
 #define TIMESTAMPSIZE 40
 #define MAX_ERROR_SIZE 512
+#define MAX_LOG_NUM 20
 #define nv_sprintf(fmt, args...) \
 do { \
        snprintf(nv_error_buffer, MAX_ERROR_SIZE-1, fmt , ## args); \