blob: 39e30d51a84bdec29c716945e8203613c07a4d22 [file] [log] [blame]
Googlerb48fa912023-03-17 12:40:29 +05301
2#include <linux/device.h>
3#include <linux/debugfs.h>
4#include <linux/vmalloc.h>
5#include "core.h"
6#include "wmi.h"
7#include "debug.h"
8
9static struct page *pktlog_virt_to_logical(void *addr)
10{
11 struct page *page;
12 unsigned long vpage = 0UL;
13
14 page = vmalloc_to_page(addr);
15 if (page) {
16 vpage = (unsigned long)page_address(page);
17 vpage |= ((unsigned long)addr & (PAGE_SIZE - 1));
18 }
19 return virt_to_page((void *)vpage);
20}
21
22static void ath_pktlog_release(struct ath_pktlog *pktlog)
23{
24 unsigned long page_cnt, vaddr;
25 struct page *page;
26
27 page_cnt =
28 ((sizeof(*pktlog->buf) +
29 pktlog->buf_size) / PAGE_SIZE) + 1;
30
31 for (vaddr = (unsigned long)(pktlog->buf); vaddr <
32 (unsigned long)(pktlog->buf) +
33 (page_cnt * PAGE_SIZE);
34 vaddr += PAGE_SIZE) {
35 page = pktlog_virt_to_logical((void *)vaddr);
36 clear_bit(PG_reserved, &page->flags);
37 }
38
39 vfree(pktlog->buf);
40 pktlog->buf = NULL;
41}
42
43static int ath_alloc_pktlog_buf(struct ath11k *ar)
44{
45 u32 page_cnt;
46 unsigned long vaddr;
47 struct page *page;
48 struct ath_pktlog *pktlog = &ar->debug.pktlog;
49
50 if (pktlog->buf_size == 0)
51 return -EINVAL;
52
53 page_cnt = (sizeof(*pktlog->buf) +
54 pktlog->buf_size) / PAGE_SIZE;
55
56 pktlog->buf = vmalloc((page_cnt + 2) * PAGE_SIZE);
57 if (!pktlog->buf)
58 return -ENOMEM;
59
60 pktlog->buf = (struct ath_pktlog_buf *)
61 (((unsigned long)
62 (pktlog->buf)
63 + PAGE_SIZE - 1) & PAGE_MASK);
64
65 for (vaddr = (unsigned long)(pktlog->buf);
66 vaddr < ((unsigned long)(pktlog->buf)
67 + (page_cnt * PAGE_SIZE)); vaddr += PAGE_SIZE) {
68 page = pktlog_virt_to_logical((void *)vaddr);
69 set_bit(PG_reserved, &page->flags);
70 }
71
72 return 0;
73}
74
75static void ath_init_pktlog_buf(struct ath11k *ar, struct ath_pktlog *pktlog)
76{
77 if (!ar->ab->pktlog_defs_checksum) {
78 pktlog->buf->bufhdr.magic_num = PKTLOG_MAGIC_NUM;
79 pktlog->buf->bufhdr.version = CUR_PKTLOG_VER;
80 } else {
81 pktlog->buf->bufhdr.magic_num = PKTLOG_NEW_MAGIC_NUM;
82 pktlog->buf->bufhdr.version = ar->ab->pktlog_defs_checksum;
83 }
84 pktlog->buf->rd_offset = -1;
85 pktlog->buf->wr_offset = 0;
86}
87
88static inline void ath_pktlog_mov_rd_idx(struct ath_pktlog *pl_info,
89 int32_t *rd_offset)
90{
91 int32_t boundary;
92 struct ath_pktlog_buf *log_buf = pl_info->buf;
93 struct ath_pktlog_hdr *log_hdr;
94
95 log_hdr = (struct ath_pktlog_hdr *)(log_buf->log_data + *rd_offset);
96 boundary = *rd_offset;
97 boundary += pl_info->hdr_size;
98 boundary += log_hdr->size;
99
100 if (boundary <= pl_info->buf_size)
101 *rd_offset = boundary;
102 else
103 *rd_offset = log_hdr->size;
104
105 if ((pl_info->buf_size - *rd_offset) < pl_info->hdr_size)
106 *rd_offset = 0;
107}
108
109static char *ath_pktlog_getbuf(struct ath_pktlog *pl_info,
110 struct ath_pktlog_hdr_arg *hdr_arg)
111{
112 struct ath_pktlog_buf *log_buf;
113 int32_t cur_wr_offset, buf_size;
114 char *log_ptr;
115
116 log_buf = pl_info->buf;
117 buf_size = pl_info->buf_size;
118
119 spin_lock_bh(&pl_info->lock);
120 cur_wr_offset = log_buf->wr_offset;
121 /* Move read offset to the next entry if there is a buffer overlap */
122 if (log_buf->rd_offset >= 0) {
123 if ((cur_wr_offset <= log_buf->rd_offset) &&
124 (cur_wr_offset + pl_info->hdr_size) >
125 log_buf->rd_offset)
126 ath_pktlog_mov_rd_idx(pl_info, &log_buf->rd_offset);
127 } else {
128 log_buf->rd_offset = cur_wr_offset;
129 }
130
131 memcpy(&log_buf->log_data[cur_wr_offset],
132 hdr_arg->pktlog_hdr, pl_info->hdr_size);
133
134 cur_wr_offset += pl_info->hdr_size;
135
136 if ((buf_size - cur_wr_offset) < hdr_arg->payload_size) {
137 while ((cur_wr_offset <= log_buf->rd_offset) &&
138 (log_buf->rd_offset < buf_size))
139 ath_pktlog_mov_rd_idx(pl_info, &log_buf->rd_offset);
140 cur_wr_offset = 0;
141 }
142
143 while ((cur_wr_offset <= log_buf->rd_offset) &&
144 ((cur_wr_offset + hdr_arg->payload_size) > log_buf->rd_offset))
145 ath_pktlog_mov_rd_idx(pl_info, &log_buf->rd_offset);
146
147 log_ptr = &log_buf->log_data[cur_wr_offset];
148 cur_wr_offset += hdr_arg->payload_size;
149
150 log_buf->wr_offset =
151 ((buf_size - cur_wr_offset) >=
152 pl_info->hdr_size) ? cur_wr_offset : 0;
153 spin_unlock_bh(&pl_info->lock);
154
155 return log_ptr;
156}
157
158static vm_fault_t pktlog_pgfault(struct vm_fault *vmf)
159{
160 struct vm_area_struct *vma = vmf->vma;
161 unsigned long address = vmf->address;
162
163 if (address == 0UL)
164 return VM_FAULT_NOPAGE;
165
166 if (vmf->pgoff > vma->vm_end)
167 return VM_FAULT_SIGBUS;
168
169 get_page(virt_to_page(address));
170 vmf->page = virt_to_page(address);
171 return 0;
172}
173
174static struct vm_operations_struct pktlog_vmops = {
175 .fault = pktlog_pgfault
176};
177
178static int ath_pktlog_mmap(struct file *file, struct vm_area_struct *vma)
179{
180 struct ath11k *ar = file->private_data;
181
182 /* entire buffer should be mapped */
183 if (vma->vm_pgoff != 0)
184 return -EINVAL;
185
186 if (!ar->debug.pktlog.buf) {
187 pr_err("Can't allocate pktlog buf\n");
188 return -ENOMEM;
189 }
190
191 vma->vm_flags |= VM_LOCKED;
192 vma->vm_ops = &pktlog_vmops;
193
194 return 0;
195}
196
197static ssize_t ath_pktlog_read(struct file *file, char __user *userbuf,
198 size_t count, loff_t *ppos)
199{
200 size_t bufhdr_size;
201 size_t nbytes = 0, ret_val = 0;
202 int rem_len;
203 int start_offset, end_offset;
204 int fold_offset, ppos_data, cur_rd_offset;
205 struct ath11k *ar = file->private_data;
206 struct ath_pktlog *info = &ar->debug.pktlog;
207 struct ath_pktlog_buf *log_buf = info->buf;
208
209 if (log_buf == NULL)
210 return 0;
211
212 bufhdr_size = sizeof(log_buf->bufhdr);
213
214 /* copy valid log entries from circular buffer into user space */
215 rem_len = count;
216
217 nbytes = 0;
218
219 if (*ppos < bufhdr_size) {
220 nbytes = min((int)(bufhdr_size - *ppos), rem_len);
221 if (copy_to_user(userbuf,
222 ((char *)&log_buf->bufhdr) + *ppos, nbytes))
223 return -EFAULT;
224 rem_len -= nbytes;
225 ret_val += nbytes;
226 }
227
228 spin_lock_bh(&info->lock);
229
230 start_offset = log_buf->rd_offset;
231
232 if ((rem_len == 0) || (start_offset < 0))
233 goto read_done;
234
235 fold_offset = -1;
236 cur_rd_offset = start_offset;
237
238 /* Find the last offset and fold-offset if the buffer is folded */
239 do {
240 int log_data_offset;
241 struct ath_pktlog_hdr *log_hdr;
242
243 log_hdr = (struct ath_pktlog_hdr *)(log_buf->log_data + cur_rd_offset);
244 log_data_offset = cur_rd_offset + info->hdr_size;
245
246 if ((fold_offset == -1) &&
247 ((info->buf_size - log_data_offset) <= log_hdr->size))
248 fold_offset = log_data_offset - 1;
249
250 ath_pktlog_mov_rd_idx(info, &cur_rd_offset);
251
252 if ((fold_offset == -1) && (cur_rd_offset == 0) &&
253 (cur_rd_offset != log_buf->wr_offset))
254 fold_offset = log_data_offset + log_hdr->size - 1;
255
256 end_offset = log_data_offset + log_hdr->size - 1;
257
258 } while (cur_rd_offset != log_buf->wr_offset);
259
260 ppos_data = *ppos + ret_val - bufhdr_size + start_offset;
261
262 if (fold_offset == -1) {
263 if (ppos_data > end_offset)
264 goto read_done;
265
266 nbytes = min(rem_len, end_offset - ppos_data + 1);
267 if (copy_to_user(userbuf + ret_val,
268 log_buf->log_data + ppos_data, nbytes)) {
269 ret_val = -EFAULT;
270 goto out;
271 }
272 ret_val += nbytes;
273 rem_len -= nbytes;
274 } else {
275 if (ppos_data <= fold_offset) {
276 nbytes = min(rem_len, fold_offset - ppos_data + 1);
277 if (copy_to_user(userbuf + ret_val,
278 log_buf->log_data + ppos_data, nbytes)) {
279 ret_val = -EFAULT;
280 goto out;
281 }
282 ret_val += nbytes;
283 rem_len -= nbytes;
284 }
285
286 if (rem_len == 0)
287 goto read_done;
288
289 ppos_data =
290 *ppos + ret_val - (bufhdr_size +
291 (fold_offset - start_offset + 1));
292
293 if (ppos_data <= end_offset) {
294 nbytes = min(rem_len, end_offset - ppos_data + 1);
295 if (copy_to_user(userbuf + ret_val, log_buf->log_data
296 + ppos_data,
297 nbytes)) {
298 ret_val = -EFAULT;
299 goto out;
300 }
301 ret_val += nbytes;
302 rem_len -= nbytes;
303 }
304 }
305
306read_done:
307 *ppos += ret_val;
308out:
309 spin_unlock_bh(&info->lock);
310 return ret_val;
311}
312
313static const struct file_operations fops_pktlog_dump = {
314 .read = ath_pktlog_read,
315 .mmap = ath_pktlog_mmap,
316 .open = simple_open
317};
318
319static ssize_t write_pktlog_start(struct file *file, const char __user *ubuf,
320 size_t count, loff_t *ppos)
321{
322 struct ath11k *ar = file->private_data;
323 struct ath_pktlog *pktlog = &ar->debug.pktlog;
324 u32 start_pktlog;
325 int err;
326
327 err = kstrtou32_from_user(ubuf, count, 0, &start_pktlog);
328 if (err)
329 return err;
330
331 if (ar->debug.is_pkt_logging && start_pktlog) {
332 pr_err("packet logging is inprogress\n");
333 return -EINVAL;
334 }
335 if (start_pktlog) {
336 if (pktlog->buf != NULL)
337 ath_pktlog_release(pktlog);
338
339 err = ath_alloc_pktlog_buf(ar);
340 if (err != 0)
341 return err;
342
343 ath_init_pktlog_buf(ar, pktlog);
344 ar->debug.is_pkt_logging = 1;
345 } else {
346 ar->debug.is_pkt_logging = 0;
347 }
348
349 return count;
350}
351
352static ssize_t read_pktlog_start(struct file *file, char __user *ubuf,
353 size_t count, loff_t *ppos)
354{
355 char buf[32];
356 struct ath11k *ar = file->private_data;
357 int len = 0;
358
359 len = scnprintf(buf, sizeof(buf) - len, "%d\n",
360 ar->debug.is_pkt_logging);
361 return simple_read_from_buffer(ubuf, count, ppos, buf, len);
362}
363
364static const struct file_operations fops_pktlog_start = {
365 .read = read_pktlog_start,
366 .write = write_pktlog_start,
367 .open = simple_open
368};
369
370static ssize_t pktlog_size_write(struct file *file, const char __user *ubuf,
371 size_t count, loff_t *ppos)
372{
373 struct ath11k *ar = file->private_data;
374 u32 pktlog_size;
375
376 if (kstrtou32_from_user(ubuf, count, 0, &pktlog_size))
377 return -EINVAL;
378
379 if (pktlog_size == ar->debug.pktlog.buf_size)
380 return count;
381
382 if (ar->debug.is_pkt_logging) {
383 pr_debug("Stop packet logging before changing the size\n");
384 return -EINVAL;
385 }
386
387 ar->debug.pktlog.buf_size = pktlog_size;
388
389 return count;
390}
391
392static ssize_t pktlog_size_read(struct file *file, char __user *ubuf,
393 size_t count, loff_t *ppos)
394{
395 char buf[32];
396 struct ath11k *ar = file->private_data;
397 int len = 0;
398
399 len = scnprintf(buf, sizeof(buf) - len, "%uL\n",
400 ar->debug.pktlog.buf_size);
401 return simple_read_from_buffer(ubuf, count, ppos, buf, len);
402}
403
404static const struct file_operations fops_pktlog_size = {
405 .read = pktlog_size_read,
406 .write = pktlog_size_write,
407 .open = simple_open
408};
409
410static void ath_pktlog_init(struct ath11k *ar)
411{
412 struct ath_pktlog *pktlog = &ar->debug.pktlog;
413
414 spin_lock_init(&pktlog->lock);
415 pktlog->buf_size = ATH_DEBUGFS_PKTLOG_SIZE_DEFAULT;
416 pktlog->buf = NULL;
417
418 pktlog->hdr_size = sizeof(struct ath_pktlog_hdr);
419 pktlog->hdr_size_field_offset =
420 offsetof(struct ath_pktlog_hdr, size);
421}
422
423void ath11k_init_pktlog(struct ath11k *ar)
424{
425 ar->debug.debugfs_pktlog = debugfs_create_dir("pktlog",
426 ar->debug.debugfs_pdev);
427 debugfs_create_file("start", S_IRUGO | S_IWUSR,
428 ar->debug.debugfs_pktlog, ar, &fops_pktlog_start);
429 debugfs_create_file("size", S_IRUGO | S_IWUSR,
430 ar->debug.debugfs_pktlog, ar, &fops_pktlog_size);
431 debugfs_create_file("dump", S_IRUGO | S_IWUSR,
432 ar->debug.debugfs_pktlog, ar, &fops_pktlog_dump);
433 ath_pktlog_init(ar);
434}
435
436void ath11k_deinit_pktlog(struct ath11k *ar)
437{
438 struct ath_pktlog *pktlog = &ar->debug.pktlog;
439
440 if (pktlog->buf != NULL)
441 ath_pktlog_release(pktlog);
442}
443
444static int ath_pktlog_pull_hdr(struct ath_pktlog_hdr_arg *arg,
445 u8 *data)
446{
447 struct ath_pktlog_hdr *hdr = (struct ath_pktlog_hdr *)data;
448
449 hdr->flags = __le16_to_cpu(hdr->flags);
450 hdr->missed_cnt = __le16_to_cpu(hdr->missed_cnt);
451 hdr->log_type = __le16_to_cpu(hdr->log_type);
452 hdr->size = __le16_to_cpu(hdr->size);
453 hdr->timestamp = __le32_to_cpu(hdr->timestamp);
454 hdr->type_specific_data = __le32_to_cpu(hdr->type_specific_data);
455
456 arg->log_type = hdr->log_type;
457 arg->payload = hdr->payload;
458 arg->payload_size = hdr->size;
459 arg->pktlog_hdr = data;
460
461 return 0;
462}
463
464static void ath_pktlog_write_buf(struct ath_pktlog *pl_info,
465 struct ath_pktlog_hdr_arg *hdr_arg)
466{
467 char *log_data;
468
469 log_data = ath_pktlog_getbuf(pl_info, hdr_arg);
470 if (!log_data)
471 return;
472
473 memcpy(log_data, hdr_arg->payload, hdr_arg->payload_size);
474}
475
476void ath11k_htt_pktlog_process(struct ath11k *ar, u8 *data)
477{
478 struct ath_pktlog *pl_info = &ar->debug.pktlog;
479 struct ath_pktlog_hdr_arg hdr_arg;
480 int ret;
481
482 if (!ar->debug.is_pkt_logging)
483 return;
484
485 ret = ath_pktlog_pull_hdr(&hdr_arg, data);
486 if (ret)
487 return;
488
489 ath_pktlog_write_buf(pl_info, &hdr_arg);
490}
491
492void ath11k_htt_ppdu_pktlog_process(struct ath11k *ar, u8 *data, u32 len)
493{
494 struct ath_pktlog *pl_info = &ar->debug.pktlog;
495 struct ath_pktlog_hdr hdr;
496 struct ath_pktlog_hdr_arg hdr_arg;
497
498 if (!ar->debug.is_pkt_logging)
499 return;
500
501 hdr.flags = (1 << PKTLOG_FLG_FRM_TYPE_REMOTE_S);
502 hdr.missed_cnt = 0;
503 hdr.log_type = ATH11K_PKTLOG_TYPE_PPDU_STATS;
504 hdr.timestamp = 0;
505 hdr.size = len;
506 hdr.type_specific_data = 0;
507
508 hdr_arg.log_type = hdr.log_type;
509 hdr_arg.payload_size = hdr.size;
510 hdr_arg.payload = (u8 *)data;
511 hdr_arg.pktlog_hdr = (u8 *)&hdr;
512
513 ath_pktlog_write_buf(pl_info, &hdr_arg);
514}
515
516void ath11k_rx_stats_buf_pktlog_process(struct ath11k *ar, u8 *data, u16 log_type, u32 len)
517{
518 struct ath_pktlog *pl_info = &ar->debug.pktlog;
519 struct ath_pktlog_hdr hdr;
520 struct ath_pktlog_hdr_arg hdr_arg;
521
522 if (!ar->debug.is_pkt_logging)
523 return;
524
525 hdr.flags = (1 << PKTLOG_FLG_FRM_TYPE_REMOTE_S);
526 hdr.missed_cnt = 0;
527 hdr.log_type = log_type;
528 hdr.timestamp = 0;
529 hdr.size = len;
530 hdr.type_specific_data = 0;
531
532 hdr_arg.log_type = log_type;
533 hdr_arg.payload_size = len;
534 hdr_arg.payload = data;
535 hdr_arg.pktlog_hdr = (u8 *)&hdr;
536
537 ath_pktlog_write_buf(pl_info, &hdr_arg);
538}