blob: 9ac22010f7d46b84742d2241bdd43c4e5a766970 [file] [log] [blame]
Vinay Krishna Erannad938c422014-03-10 17:14:21 +05301/*
2* Copyright (c) 2014 The Linux Foundation. All rights reserved.
3*
4* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5*
6*
7* Permission to use, copy, modify, and/or distribute this software for
8* any purpose with or without fee is hereby granted, provided that the
9* above copyright notice and this permission notice appear in all
10* copies.
11*
12* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19* PERFORMANCE OF THIS SOFTWARE.
20*/
21
22/*
23* This file was originally distributed by Qualcomm Atheros, Inc.
24* under proprietary terms before Copyright ownership was assigned
25* to the Linux Foundation.
26*/
27
28/******************************************************************************
29 * wlan_logging_sock_svc.c
30 *
31 ******************************************************************************/
32#ifdef WLAN_LOGGING_SOCK_SVC_ENABLE
33#include <wlan_nlink_srv.h>
34#include <vos_status.h>
35#include <vos_trace.h>
36#include <wlan_nlink_common.h>
37#include <wlan_logging_sock_svc.h>
38#include <vos_types.h>
39#include <vos_trace.h>
40#include <kthread.h>
41
42#define LOGGING_TRACE(level, args...) \
43 VOS_TRACE(VOS_MODULE_ID_HDD, level, ## args)
44
45/* Global variables */
46
47#define ANI_NL_MSG_LOG_TYPE 89
48#define INVALID_PID -1
49
50#define MAX_LOGMSG_LENGTH 4096
51#define SECONDS_IN_A_DAY (86400)
52
53struct log_msg {
54 struct list_head node;
55 unsigned int radio;
56 unsigned int index;
57 /* indicates the current filled log length in logbuf */
58 unsigned int filled_length;
59 /*
60 * Buf to hold the log msg
61 * tAniHdr + log
62 */
63 char logbuf[MAX_LOGMSG_LENGTH];
64};
65
66struct wlan_logging {
67 /* Log Fatal and ERROR to console */
68 bool log_fe_to_console;
69 /* Number of buffers to be used for logging */
70 int num_buf;
71 /* Lock to synchronize access to shared logging resource */
72 spinlock_t spin_lock;
73 /* Holds the free node which can be used for filling logs */
74 struct list_head free_list;
75 /* Holds the filled nodes which needs to be indicated to APP */
76 struct list_head filled_list;
77 /* Wait queue for Logger thread */
78 wait_queue_head_t wait_queue;
79 /* Logger thread */
80 struct task_struct *thread;
81 /* Logging thread sets this variable on exit */
82 struct completion shutdown_comp;
83 /* Indicates to logger thread to exit */
84 bool exit;
85 /* Holds number of dropped logs*/
86 unsigned int drop_count;
87 /* current logbuf to which the log will be filled to */
88 struct log_msg *pcur_node;
89};
90
91static struct wlan_logging gwlan_logging;
92static struct log_msg *gplog_msg;
93
94/* PID of the APP to log the message */
95static int gapp_pid = INVALID_PID;
96
97/* Utility function to send a netlink message to an application
98 * in user space
99 */
100static int wlan_send_sock_msg_to_app(tAniHdr *wmsg, int radio,
101 int src_mod, int pid)
102{
103 int err = -1;
104 int payload_len;
105 int tot_msg_len;
106 tAniNlHdr *wnl = NULL;
107 struct sk_buff *skb;
108 struct nlmsghdr *nlh;
109 int wmsg_length = be16_to_cpu(wmsg->length);
110 static int nlmsg_seq;
111
112 if (radio < 0 || radio > ANI_MAX_RADIOS) {
113 LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
114 "%s: invalid radio id [%d]",
115 __func__, radio);
116 return -EINVAL;
117 }
118
119 payload_len = wmsg_length + sizeof(wnl->radio);
120 tot_msg_len = NLMSG_SPACE(payload_len);
121 skb = dev_alloc_skb(tot_msg_len);
122 if (skb == NULL) {
123 LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
124 "%s: dev_alloc_skb() failed for msg size[%d]",
125 __func__, tot_msg_len);
126 return -ENOMEM;
127 }
128 nlh = nlmsg_put(skb, pid, nlmsg_seq++, src_mod, payload_len,
129 NLM_F_REQUEST);
130 if (NULL == nlh) {
131 LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
132 "%s: nlmsg_put() failed for msg size[%d]",
133 __func__, tot_msg_len);
134 kfree_skb(skb);
135 return -ENOMEM;
136 }
137
138 wnl = (tAniNlHdr *) nlh;
139 wnl->radio = radio;
140 memcpy(&wnl->wmsg, wmsg, wmsg_length);
141 LOGGING_TRACE(VOS_TRACE_LEVEL_INFO,
142 "%s: Sending Msg Type [0x%X] to pid[%d]\n",
143 __func__, be16_to_cpu(wmsg->type), pid);
144
145 err = nl_srv_ucast(skb, pid, MSG_DONTWAIT);
146 return err;
147}
148
149static void set_default_logtoapp_log_level(void)
150{
151 vos_trace_setValue(VOS_MODULE_ID_WDI, VOS_TRACE_LEVEL_ALL, VOS_TRUE);
152 vos_trace_setValue(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ALL, VOS_TRUE);
153 vos_trace_setValue(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ALL, VOS_TRUE);
154 vos_trace_setValue(VOS_MODULE_ID_PE, VOS_TRACE_LEVEL_ALL, VOS_TRUE);
155 vos_trace_setValue(VOS_MODULE_ID_WDA, VOS_TRACE_LEVEL_ALL, VOS_TRUE);
156 vos_trace_setValue(VOS_MODULE_ID_HDD_SOFTAP, VOS_TRACE_LEVEL_ALL,
157 VOS_TRUE);
158 vos_trace_setValue(VOS_MODULE_ID_SAP, VOS_TRACE_LEVEL_ALL, VOS_TRUE);
159}
160
161static void clear_default_logtoapp_log_level(void)
162{
163 int module;
164
165 for (module = 0; module < VOS_MODULE_ID_MAX; module++) {
166 vos_trace_setValue(module, VOS_TRACE_LEVEL_NONE,
167 VOS_FALSE);
168 vos_trace_setValue(module, VOS_TRACE_LEVEL_FATAL,
169 VOS_TRUE);
170 vos_trace_setValue(module, VOS_TRACE_LEVEL_ERROR,
171 VOS_TRUE);
172 }
173
174 vos_trace_setValue(VOS_MODULE_ID_RSV3, VOS_TRACE_LEVEL_NONE,
175 VOS_FALSE);
176 vos_trace_setValue(VOS_MODULE_ID_RSV4, VOS_TRACE_LEVEL_NONE,
177 VOS_FALSE);
178}
179
180/* Need to call this with spin_lock acquired */
181static int wlan_queue_logmsg_for_app(void)
182{
183 char *ptr;
184 int ret = 0;
185
186 ptr = &gwlan_logging.pcur_node->logbuf[sizeof(tAniHdr)];
187 ptr[gwlan_logging.pcur_node->filled_length] = '\0';
188
189 *(unsigned short *)(gwlan_logging.pcur_node->logbuf) =
190 ANI_NL_MSG_LOG_TYPE;
191 *(unsigned short *)(gwlan_logging.pcur_node->logbuf + 2) =
192 gwlan_logging.pcur_node->filled_length;
193 list_add_tail(&gwlan_logging.pcur_node->node,
194 &gwlan_logging.filled_list);
195
196 if (!list_empty(&gwlan_logging.free_list)) {
197 /* Get buffer from free list */
198 gwlan_logging.pcur_node =
199 (struct log_msg *)(gwlan_logging.free_list.next);
200 list_del_init(gwlan_logging.free_list.next);
201 } else if (!list_empty(&gwlan_logging.filled_list)) {
202 /* Get buffer from filled list */
203 /* This condition will drop the packet from being
204 * indicated to app
205 */
206 gwlan_logging.pcur_node =
207 (struct log_msg *)(gwlan_logging.filled_list.next);
208 pr_err("%s: drop_count = %u index = %d filled_length = %d\n",
209 __func__, ++gwlan_logging.drop_count,
210 gwlan_logging.pcur_node->index,
211 gwlan_logging.pcur_node->filled_length);
212 list_del_init(gwlan_logging.filled_list.next);
213 ret = 1;
214 }
215
216 /* Reset the current node values */
217 gwlan_logging.pcur_node->filled_length = 0;
218 return ret;
219}
220
221
222int wlan_log_to_user(VOS_TRACE_LEVEL log_level, char *to_be_sent, int length)
223{
224 /* Add the current time stamp */
225 char *ptr;
226 char tbuf[50];
227 int tlen;
228 int total_log_len;
229 unsigned int *pfilled_length;
230 bool wake_up_thread = false;
231
232 struct timeval tv;
233
234 if (gapp_pid == INVALID_PID) {
235 /*
236 * This is to make sure that we print the logs to kmsg console
237 * when no logger app is running. This is also needed to
238 * log the initial messages during loading of driver where even
239 * if app is running it will not be able to
240 * register with driver immediately and start logging all the
241 * messages.
242 */
243 pr_info("%s\n", to_be_sent);
244 return -EIO;
245 }
246
247 /* Format the Log time [Secondselapsedinaday.microseconds] */
248 do_gettimeofday(&tv);
249 tlen = snprintf(tbuf, sizeof(tbuf), "[%s][%5lu.%06lu] ", current->comm,
250 (unsigned long) (tv.tv_sec%SECONDS_IN_A_DAY),
251 tv.tv_usec);
252
253 /* 1+1 indicate '\n'+'\0' */
254 total_log_len = length + tlen + 1 + 1;
255
256 spin_lock_bh(&gwlan_logging.spin_lock);
257 pfilled_length = &gwlan_logging.pcur_node->filled_length;
258
259 /* Check if we can accomodate more log into current node/buffer */
260 if ((MAX_LOGMSG_LENGTH - (*pfilled_length + sizeof(tAniNlHdr))) <
261 total_log_len) {
262 wake_up_thread = true;
263 wlan_queue_logmsg_for_app();
264 pfilled_length = &gwlan_logging.pcur_node->filled_length;
265 }
266
267 ptr = &gwlan_logging.pcur_node->logbuf[sizeof(tAniHdr)];
268
269 /* Assumption here is that we receive logs which is always less than
270 * MAX_LOGMSG_LENGTH, where we can accomodate the
271 * tAniNlHdr + [context][timestamp] + log
272 * VOS_ASSERT if we cannot accomodate the the complete log into
273 * the available buffer.
274 *
275 * Continue and copy logs to the available length and discard the rest.
276 */
277 if (MAX_LOGMSG_LENGTH < (sizeof(tAniNlHdr) + total_log_len)) {
278 VOS_ASSERT(0);
279 total_log_len = MAX_LOGMSG_LENGTH - sizeof(tAniNlHdr) - 2;
280 }
281
282 memcpy(&ptr[*pfilled_length], tbuf, tlen);
283 memcpy(&ptr[*pfilled_length + tlen], to_be_sent,
284 min(length, (total_log_len - tlen)));
285 *pfilled_length += tlen + min(length, total_log_len - tlen);
286 ptr[*pfilled_length] = '\n';
287 *pfilled_length += 1;
288
289 spin_unlock_bh(&gwlan_logging.spin_lock);
290
291 /* Wakeup logger thread */
292 if (true == wake_up_thread)
293 wake_up_interruptible(&gwlan_logging.wait_queue);
294
295 if (gwlan_logging.log_fe_to_console
296 && ((VOS_TRACE_LEVEL_FATAL == log_level)
297 || (VOS_TRACE_LEVEL_ERROR == log_level))) {
298 pr_info("%s\n", to_be_sent);
299 }
300
301 return 0;
302}
303
304static int send_filled_buffers_to_user(void)
305{
306 int ret = -1;
307 struct log_msg *plog_msg;
308 int payload_len;
309 int tot_msg_len;
310 tAniNlHdr *wnl;
311 struct sk_buff *skb = NULL;
312 struct nlmsghdr *nlh;
313 static int nlmsg_seq;
314
315 while (!list_empty(&gwlan_logging.filled_list)
316 && !gwlan_logging.exit) {
317
318 skb = dev_alloc_skb(MAX_LOGMSG_LENGTH);
319 if (skb == NULL) {
320 pr_err("%s: dev_alloc_skb() failed for msg size[%d]\n",
321 __func__, MAX_LOGMSG_LENGTH);
322 ret = -ENOMEM;
323 break;
324 }
325
326 spin_lock(&gwlan_logging.spin_lock);
327
328 plog_msg = (struct log_msg *)
329 (gwlan_logging.filled_list.next);
330 list_del_init(gwlan_logging.filled_list.next);
331 /* 4 extra bytes for the radio idx */
332 payload_len = plog_msg->filled_length +
333 sizeof(wnl->radio) + sizeof(tAniHdr);
334
335 tot_msg_len = NLMSG_SPACE(payload_len);
336 nlh = nlmsg_put(skb, gapp_pid, nlmsg_seq++,
337 ANI_NL_MSG_LOG, payload_len,
338 NLM_F_REQUEST);
339 if (NULL == nlh) {
340 list_add_tail(&plog_msg->node,
341 &gwlan_logging.free_list);
342 spin_unlock(&gwlan_logging.spin_lock);
343 pr_err("%s: drop_count = %u\n", __func__,
344 ++gwlan_logging.drop_count);
345 pr_err("%s: nlmsg_put() failed for msg size[%d]\n",
346 __func__, tot_msg_len);
347 dev_kfree_skb(skb);
348 skb = NULL;
349 ret = -EINVAL;
350 continue;
351 }
352 spin_unlock(&gwlan_logging.spin_lock);
353
354 wnl = (tAniNlHdr *) nlh;
355 wnl->radio = plog_msg->radio;
356 memcpy(&wnl->wmsg, plog_msg->logbuf,
357 plog_msg->filled_length +
358 sizeof(tAniHdr));
359
360 spin_lock(&gwlan_logging.spin_lock);
361 list_add_tail(&plog_msg->node,
362 &gwlan_logging.free_list);
363 spin_unlock(&gwlan_logging.spin_lock);
364
365 ret = nl_srv_ucast(skb, gapp_pid, 0);
366 if (ret < 0) {
367 pr_err("%s: Send Failed %d drop_count = %u\n",
368 __func__, ret, ++gwlan_logging.drop_count);
369 skb = NULL;
370 gapp_pid = INVALID_PID;
371 clear_default_logtoapp_log_level();
372 } else {
373 skb = NULL;
374 ret = 0;
375 }
376 }
377
378 return ret;
379}
380
381/**
382 * wlan_logging_thread() - The WLAN Logger thread
383 * @Arg - pointer to the HDD context
384 *
385 * This thread logs log message to App registered for the logs.
386 */
387static int wlan_logging_thread(void *Arg)
388{
389 int ret_wait_status = 0;
390
391 set_user_nice(current, -2);
392
393#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
394 daemonize("wlan_logging_thread");
395#endif
396
397 while (!gwlan_logging.exit) {
398 ret_wait_status = wait_event_interruptible(
399 gwlan_logging.wait_queue,
400 (!list_empty(&gwlan_logging.filled_list)
401 || gwlan_logging.exit));
402
403 if (ret_wait_status == -ERESTARTSYS) {
404 pr_err("%s: wait_event_interruptible returned -ERESTARTSYS",
405 __func__);
406 break;
407 }
408
409 if (gwlan_logging.exit) {
410 pr_err("%s: Exiting the thread\n", __func__);
411 break;
412 }
413
414 if (INVALID_PID == gapp_pid) {
415 pr_err("%s: Invalid PID\n", __func__);
416 continue;
417 }
418
419 send_filled_buffers_to_user();
420 }
421
422 complete_and_exit(&gwlan_logging.shutdown_comp, 0);
423
424 pr_info("%s: Terminating\n", __func__);
425
426 return 0;
427}
428
429/*
430 * Process all the Netlink messages from Logger Socket app in user space
431 */
432static int wlan_logging_proc_sock_rx_msg(struct sk_buff *skb)
433{
434 tAniNlHdr *wnl;
435 int radio;
436 int type;
437 int ret;
438
439 wnl = (tAniNlHdr *) skb->data;
440 radio = wnl->radio;
441 type = wnl->nlh.nlmsg_type;
442
443 if (radio < 0 || radio > ANI_MAX_RADIOS) {
444 LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
445 "%s: invalid radio id [%d]\n",
446 __func__, radio);
447 return -EINVAL;
448 }
449
450 if (gapp_pid != INVALID_PID) {
451 if (wnl->nlh.nlmsg_pid > gapp_pid) {
452 gapp_pid = wnl->nlh.nlmsg_pid;
453 }
454
455 spin_lock_bh(&gwlan_logging.spin_lock);
456 if (gwlan_logging.pcur_node->filled_length) {
457 wlan_queue_logmsg_for_app();
458 }
459 spin_unlock_bh(&gwlan_logging.spin_lock);
460 wake_up_interruptible(&gwlan_logging.wait_queue);
461 } else {
462 /* This is to set the default levels (WLAN logging
463 * default values not the VOS trace default) when
464 * logger app is registered for the first time.
465 */
466 gapp_pid = wnl->nlh.nlmsg_pid;
467 set_default_logtoapp_log_level();
468 }
469
470 ret = wlan_send_sock_msg_to_app(&wnl->wmsg, 0,
471 ANI_NL_MSG_LOG, wnl->nlh.nlmsg_pid);
472 if (ret < 0) {
473 LOGGING_TRACE(VOS_TRACE_LEVEL_ERROR,
474 "wlan_send_sock_msg_to_app: failed");
475 }
476
477 return ret;
478}
479
480int wlan_logging_sock_activate_svc(int log_fe_to_console, int num_buf)
481{
482 int i = 0;
483 unsigned long irq_flag;
484
485 pr_info("%s: Initalizing FEConsoleLog = %d NumBuff = %d\n",
486 __func__, log_fe_to_console, num_buf);
487
488 gapp_pid = INVALID_PID;
489
490 gplog_msg = (struct log_msg *) vos_mem_malloc(
491 num_buf * sizeof(struct log_msg));
492 if (!gplog_msg) {
493 pr_err("%s: Could not allocate memory\n", __func__);
494 return -ENOMEM;
495 }
496
497 vos_mem_zero(gplog_msg, (num_buf * sizeof(struct log_msg)));
498
499 gwlan_logging.log_fe_to_console = !!log_fe_to_console;
500 gwlan_logging.num_buf = num_buf;
501
502 spin_lock_init(&gwlan_logging.spin_lock);
503
504 spin_lock_irqsave(&gwlan_logging.spin_lock, irq_flag);
505 INIT_LIST_HEAD(&gwlan_logging.free_list);
506 INIT_LIST_HEAD(&gwlan_logging.filled_list);
507
508 for (i = 0; i < num_buf; i++) {
509 list_add(&gplog_msg[i].node, &gwlan_logging.free_list);
510 gplog_msg[i].index = i;
511 }
512 gwlan_logging.pcur_node = (struct log_msg *)
513 (gwlan_logging.free_list.next);
514 list_del_init(gwlan_logging.free_list.next);
515 spin_unlock_irqrestore(&gwlan_logging.spin_lock, irq_flag);
516
517 init_waitqueue_head(&gwlan_logging.wait_queue);
518 gwlan_logging.exit = false;
519 init_completion(&gwlan_logging.shutdown_comp);
520 gwlan_logging.thread = kthread_create(wlan_logging_thread, NULL,
521 "wlan_logging_thread");
522 if (IS_ERR(gwlan_logging.thread)) {
523 pr_err("%s: Could not Create LogMsg Thread Controller",
524 __func__);
525 vos_mem_free(gplog_msg);
526 return -ENOMEM;
527 }
528 wake_up_process(gwlan_logging.thread);
529
530 nl_srv_register(ANI_NL_MSG_LOG, wlan_logging_proc_sock_rx_msg);
531
532 pr_info("%s: Activated wlan_logging svc\n", __func__);
533 return 0;
534}
535
536int wlan_logging_sock_deactivate_svc(void)
537{
538
539 nl_srv_unregister(ANI_NL_MSG_LOG, wlan_logging_proc_sock_rx_msg);
540 clear_default_logtoapp_log_level();
541 gapp_pid = INVALID_PID;
542
543 gwlan_logging.exit = true;
544 INIT_COMPLETION(gwlan_logging.shutdown_comp);
545 wake_up_interruptible(&gwlan_logging.wait_queue);
546 wait_for_completion_interruptible(&gwlan_logging.shutdown_comp);
547
548 vos_mem_free(gplog_msg);
549
550 pr_info("%s: Deactivate wlan_logging svc\n", __func__);
551
552 return 0;
553}
554
555#endif /* WLAN_LOGGING_SOCK_SVC_ENABLE */