aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKalle Valo <kvalo@qca.qualcomm.com>2012-02-06 01:23:27 -0500
committerKalle Valo <kvalo@qca.qualcomm.com>2012-02-08 04:25:07 -0500
commit9b9a4f2acac2a04416ba15242b8666d4f8273e31 (patch)
tree26dc0187280dcf2612418ab1243c592e5500edf1
parent5fbea5dcc05415474bae7108803e324f112d5b58 (diff)
ath6kl: store firmware logs in skbuffs
Currently firmware logs are stored in a circular buffer, but this was not very flexible and fragile. It's a lot easier to store logs to struct skbuffs and store them in a skb queue. Also this makes it possible to easily increase the buffer size, even dynamically if we so want (but that's not yet supported). From user space point of view nothing should change. Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
-rw-r--r--drivers/net/wireless/ath/ath6kl/core.h5
-rw-r--r--drivers/net/wireless/ath/ath6kl/debug.c121
2 files changed, 41 insertions, 85 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h
index c4d66e066dc9..9a58214135b9 100644
--- a/drivers/net/wireless/ath/ath6kl/core.h
+++ b/drivers/net/wireless/ath/ath6kl/core.h
@@ -652,10 +652,9 @@ struct ath6kl {
652 652
653#ifdef CONFIG_ATH6KL_DEBUG 653#ifdef CONFIG_ATH6KL_DEBUG
654 struct { 654 struct {
655 struct circ_buf fwlog_buf; 655 struct sk_buff_head fwlog_queue;
656 spinlock_t fwlog_lock;
657 void *fwlog_tmp;
658 u32 fwlog_mask; 656 u32 fwlog_mask;
657
659 unsigned int dbgfs_diag_reg; 658 unsigned int dbgfs_diag_reg;
660 u32 diag_reg_addr_wr; 659 u32 diag_reg_addr_wr;
661 u32 diag_reg_val_wr; 660 u32 diag_reg_val_wr;
diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c
index d832058816fe..98b5f15f622e 100644
--- a/drivers/net/wireless/ath/ath6kl/debug.c
+++ b/drivers/net/wireless/ath/ath6kl/debug.c
@@ -16,7 +16,7 @@
16 16
17#include "core.h" 17#include "core.h"
18 18
19#include <linux/circ_buf.h> 19#include <linux/skbuff.h>
20#include <linux/fs.h> 20#include <linux/fs.h>
21#include <linux/vmalloc.h> 21#include <linux/vmalloc.h>
22#include <linux/export.h> 22#include <linux/export.h>
@@ -32,9 +32,8 @@ struct ath6kl_fwlog_slot {
32 u8 payload[0]; 32 u8 payload[0];
33}; 33};
34 34
35#define ATH6KL_FWLOG_SIZE 32768 35#define ATH6KL_FWLOG_MAX_ENTRIES 20
36#define ATH6KL_FWLOG_SLOT_SIZE (sizeof(struct ath6kl_fwlog_slot) + \ 36
37 ATH6KL_FWLOG_PAYLOAD_SIZE)
38#define ATH6KL_FWLOG_VALID_MASK 0x1ffff 37#define ATH6KL_FWLOG_VALID_MASK 0x1ffff
39 38
40int ath6kl_printk(const char *level, const char *fmt, ...) 39int ath6kl_printk(const char *level, const char *fmt, ...)
@@ -268,105 +267,77 @@ static const struct file_operations fops_war_stats = {
268 .llseek = default_llseek, 267 .llseek = default_llseek,
269}; 268};
270 269
271static void ath6kl_debug_fwlog_add(struct ath6kl *ar, const void *buf,
272 size_t buf_len)
273{
274 struct circ_buf *fwlog = &ar->debug.fwlog_buf;
275 size_t space;
276 int i;
277
278 /* entries must all be equal size */
279 if (WARN_ON(buf_len != ATH6KL_FWLOG_SLOT_SIZE))
280 return;
281
282 space = CIRC_SPACE(fwlog->head, fwlog->tail, ATH6KL_FWLOG_SIZE);
283 if (space < buf_len)
284 /* discard oldest slot */
285 fwlog->tail = (fwlog->tail + ATH6KL_FWLOG_SLOT_SIZE) &
286 (ATH6KL_FWLOG_SIZE - 1);
287
288 for (i = 0; i < buf_len; i += space) {
289 space = CIRC_SPACE_TO_END(fwlog->head, fwlog->tail,
290 ATH6KL_FWLOG_SIZE);
291
292 if ((size_t) space > buf_len - i)
293 space = buf_len - i;
294
295 memcpy(&fwlog->buf[fwlog->head], buf, space);
296 fwlog->head = (fwlog->head + space) & (ATH6KL_FWLOG_SIZE - 1);
297 }
298
299}
300
301void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) 270void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len)
302{ 271{
303 struct ath6kl_fwlog_slot *slot = ar->debug.fwlog_tmp; 272 struct ath6kl_fwlog_slot *slot;
273 struct sk_buff *skb;
304 size_t slot_len; 274 size_t slot_len;
305 275
306 if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE)) 276 if (WARN_ON(len > ATH6KL_FWLOG_PAYLOAD_SIZE))
307 return; 277 return;
308 278
309 spin_lock_bh(&ar->debug.fwlog_lock); 279 slot_len = sizeof(*slot) + len;
310 280
281 skb = alloc_skb(slot_len, GFP_KERNEL);
282 if (!skb)
283 return;
284
285 slot = (struct ath6kl_fwlog_slot *) skb_put(skb, slot_len);
311 slot->timestamp = cpu_to_le32(jiffies); 286 slot->timestamp = cpu_to_le32(jiffies);
312 slot->length = cpu_to_le32(len); 287 slot->length = cpu_to_le32(len);
313 memcpy(slot->payload, buf, len); 288 memcpy(slot->payload, buf, len);
314 289
315 slot_len = sizeof(*slot) + len; 290 spin_lock(&ar->debug.fwlog_queue.lock);
316 291
317 if (slot_len < ATH6KL_FWLOG_SLOT_SIZE) 292 __skb_queue_tail(&ar->debug.fwlog_queue, skb);
318 memset(slot->payload + len, 0,
319 ATH6KL_FWLOG_SLOT_SIZE - slot_len);
320 293
321 ath6kl_debug_fwlog_add(ar, slot, ATH6KL_FWLOG_SLOT_SIZE); 294 /* drop oldest entries */
295 while (skb_queue_len(&ar->debug.fwlog_queue) >
296 ATH6KL_FWLOG_MAX_ENTRIES) {
297 skb = __skb_dequeue(&ar->debug.fwlog_queue);
298 kfree_skb(skb);
299 }
322 300
323 spin_unlock_bh(&ar->debug.fwlog_lock); 301 spin_unlock(&ar->debug.fwlog_queue.lock);
324}
325 302
326static bool ath6kl_debug_fwlog_empty(struct ath6kl *ar) 303 return;
327{
328 return CIRC_CNT(ar->debug.fwlog_buf.head,
329 ar->debug.fwlog_buf.tail,
330 ATH6KL_FWLOG_SLOT_SIZE) == 0;
331} 304}
332 305
333static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, 306static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf,
334 size_t count, loff_t *ppos) 307 size_t count, loff_t *ppos)
335{ 308{
336 struct ath6kl *ar = file->private_data; 309 struct ath6kl *ar = file->private_data;
337 struct circ_buf *fwlog = &ar->debug.fwlog_buf; 310 struct sk_buff *skb;
338 size_t len = 0, buf_len = count;
339 ssize_t ret_cnt; 311 ssize_t ret_cnt;
312 size_t len = 0;
340 char *buf; 313 char *buf;
341 int ccnt;
342 314
343 buf = vmalloc(buf_len); 315 buf = vmalloc(count);
344 if (!buf) 316 if (!buf)
345 return -ENOMEM; 317 return -ENOMEM;
346 318
347 /* read undelivered logs from firmware */ 319 /* read undelivered logs from firmware */
348 ath6kl_read_fwlogs(ar); 320 ath6kl_read_fwlogs(ar);
349 321
350 spin_lock_bh(&ar->debug.fwlog_lock); 322 spin_lock(&ar->debug.fwlog_queue.lock);
351 323
352 while (len < buf_len && !ath6kl_debug_fwlog_empty(ar)) { 324 while ((skb = __skb_dequeue(&ar->debug.fwlog_queue))) {
353 ccnt = CIRC_CNT_TO_END(fwlog->head, fwlog->tail, 325 if (skb->len > count - len) {
354 ATH6KL_FWLOG_SIZE); 326 /* not enough space, put skb back and leave */
327 __skb_queue_head(&ar->debug.fwlog_queue, skb);
328 break;
329 }
355 330
356 if ((size_t) ccnt > buf_len - len)
357 ccnt = buf_len - len;
358 331
359 memcpy(buf + len, &fwlog->buf[fwlog->tail], ccnt); 332 memcpy(buf + len, skb->data, skb->len);
360 len += ccnt; 333 len += skb->len;
361 334
362 fwlog->tail = (fwlog->tail + ccnt) & 335 kfree_skb(skb);
363 (ATH6KL_FWLOG_SIZE - 1);
364 } 336 }
365 337
366 spin_unlock_bh(&ar->debug.fwlog_lock); 338 spin_unlock(&ar->debug.fwlog_queue.lock);
367 339
368 if (WARN_ON(len > buf_len)) 340 /* FIXME: what to do if len == 0? */
369 len = buf_len;
370 341
371 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len); 342 ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
372 343
@@ -1651,17 +1622,7 @@ static const struct file_operations fops_power_params = {
1651 1622
1652int ath6kl_debug_init(struct ath6kl *ar) 1623int ath6kl_debug_init(struct ath6kl *ar)
1653{ 1624{
1654 ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE); 1625 skb_queue_head_init(&ar->debug.fwlog_queue);
1655 if (ar->debug.fwlog_buf.buf == NULL)
1656 return -ENOMEM;
1657
1658 ar->debug.fwlog_tmp = kmalloc(ATH6KL_FWLOG_SLOT_SIZE, GFP_KERNEL);
1659 if (ar->debug.fwlog_tmp == NULL) {
1660 vfree(ar->debug.fwlog_buf.buf);
1661 return -ENOMEM;
1662 }
1663
1664 spin_lock_init(&ar->debug.fwlog_lock);
1665 1626
1666 /* 1627 /*
1667 * Actually we are lying here but don't know how to read the mask 1628 * Actually we are lying here but don't know how to read the mask
@@ -1671,11 +1632,8 @@ int ath6kl_debug_init(struct ath6kl *ar)
1671 1632
1672 ar->debugfs_phy = debugfs_create_dir("ath6kl", 1633 ar->debugfs_phy = debugfs_create_dir("ath6kl",
1673 ar->wiphy->debugfsdir); 1634 ar->wiphy->debugfsdir);
1674 if (!ar->debugfs_phy) { 1635 if (!ar->debugfs_phy)
1675 vfree(ar->debug.fwlog_buf.buf);
1676 kfree(ar->debug.fwlog_tmp);
1677 return -ENOMEM; 1636 return -ENOMEM;
1678 }
1679 1637
1680 debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar, 1638 debugfs_create_file("tgt_stats", S_IRUSR, ar->debugfs_phy, ar,
1681 &fops_tgt_stats); 1639 &fops_tgt_stats);
@@ -1742,8 +1700,7 @@ int ath6kl_debug_init(struct ath6kl *ar)
1742 1700
1743void ath6kl_debug_cleanup(struct ath6kl *ar) 1701void ath6kl_debug_cleanup(struct ath6kl *ar)
1744{ 1702{
1745 vfree(ar->debug.fwlog_buf.buf); 1703 skb_queue_purge(&ar->debug.fwlog_queue);
1746 kfree(ar->debug.fwlog_tmp);
1747 kfree(ar->debug.roam_tbl); 1704 kfree(ar->debug.roam_tbl);
1748} 1705}
1749 1706