diff options
author | Kalle Valo <kvalo@qca.qualcomm.com> | 2012-02-06 01:23:27 -0500 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2012-02-08 04:25:07 -0500 |
commit | 9b9a4f2acac2a04416ba15242b8666d4f8273e31 (patch) | |
tree | 26dc0187280dcf2612418ab1243c592e5500edf1 | |
parent | 5fbea5dcc05415474bae7108803e324f112d5b58 (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.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/debug.c | 121 |
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 | ||
40 | int ath6kl_printk(const char *level, const char *fmt, ...) | 39 | int 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 | ||
271 | static 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 | |||
301 | void ath6kl_debug_fwlog_event(struct ath6kl *ar, const void *buf, size_t len) | 270 | void 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 | ||
326 | static 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 | ||
333 | static ssize_t ath6kl_fwlog_read(struct file *file, char __user *user_buf, | 306 | static 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 | ||
1652 | int ath6kl_debug_init(struct ath6kl *ar) | 1623 | int 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 | ||
1743 | void ath6kl_debug_cleanup(struct ath6kl *ar) | 1701 | void 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 | ||