1 /*
2 * DHD debug ring API and structures - implementation
3 *
4 * Copyright (C) 2020, Broadcom.
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 *
21 * <<Broadcom-WL-IPTag/Open:>>
22 *
23 * $Id$
24 */
25 #include <typedefs.h>
26 #include <osl.h>
27 #include <bcmutils.h>
28 #include <bcmendian.h>
29 #include <dngl_stats.h>
30 #include <dhd.h>
31 #include <dhd_dbg.h>
32 #include <dhd_dbg_ring.h>
33
34 dhd_dbg_ring_t *
dhd_dbg_ring_alloc_init(dhd_pub_t * dhd,uint16 ring_id,char * ring_name,uint32 ring_sz,void * allocd_buf,bool pull_inactive)35 dhd_dbg_ring_alloc_init(dhd_pub_t *dhd, uint16 ring_id,
36 char *ring_name, uint32 ring_sz, void *allocd_buf,
37 bool pull_inactive)
38 {
39 dhd_dbg_ring_t *ring = NULL;
40 int ret = 0;
41 unsigned long flags = 0;
42
43 ring = MALLOCZ(dhd->osh, sizeof(dhd_dbg_ring_t));
44 if (!ring)
45 goto fail;
46
47 ret = dhd_dbg_ring_init(dhd, ring, ring_id,
48 (uint8 *)ring_name, ring_sz,
49 allocd_buf, pull_inactive);
50 if (ret != BCME_OK) {
51 DHD_ERROR(("%s: unable to init ring %s!\n",
52 __FUNCTION__, ring_name));
53 goto fail;
54 }
55 DHD_DBG_RING_LOCK(ring->lock, flags);
56 ring->state = RING_ACTIVE;
57 ring->threshold = 0;
58 DHD_DBG_RING_UNLOCK(ring->lock, flags);
59
60 return ring;
61
62 fail:
63 if (ring) {
64 dhd_dbg_ring_deinit(dhd, ring);
65 ring->ring_buf = NULL;
66 ring->ring_size = 0;
67 MFREE(dhd->osh, ring, sizeof(dhd_dbg_ring_t));
68 }
69 return NULL;
70 }
71
72 void
dhd_dbg_ring_dealloc_deinit(void ** ring_ptr,dhd_pub_t * dhd)73 dhd_dbg_ring_dealloc_deinit(void **ring_ptr, dhd_pub_t *dhd)
74 {
75 dhd_dbg_ring_t *ring = NULL;
76 dhd_dbg_ring_t **dbgring = (dhd_dbg_ring_t **)ring_ptr;
77
78 if (!dbgring)
79 return;
80
81 ring = *dbgring;
82
83 if (ring) {
84 dhd_dbg_ring_deinit(dhd, ring);
85 ring->ring_buf = NULL;
86 ring->ring_size = 0;
87 MFREE(dhd->osh, ring, sizeof(dhd_dbg_ring_t));
88 *dbgring = NULL;
89 }
90 }
91
92 int
dhd_dbg_ring_init(dhd_pub_t * dhdp,dhd_dbg_ring_t * ring,uint16 id,uint8 * name,uint32 ring_sz,void * allocd_buf,bool pull_inactive)93 dhd_dbg_ring_init(dhd_pub_t *dhdp, dhd_dbg_ring_t *ring, uint16 id, uint8 *name,
94 uint32 ring_sz, void *allocd_buf, bool pull_inactive)
95 {
96 void *buf;
97 unsigned long flags = 0;
98
99 if (allocd_buf == NULL) {
100 return BCME_NOMEM;
101 } else {
102 buf = allocd_buf;
103 }
104
105 ring->lock = DHD_DBG_RING_LOCK_INIT(dhdp->osh);
106
107 DHD_DBG_RING_LOCK(ring->lock, flags);
108 ring->id = id;
109 strlcpy((char *)ring->name, (char *)name, sizeof(ring->name));
110 ring->ring_size = ring_sz;
111 ring->wp = ring->rp = 0;
112 ring->ring_buf = buf;
113 ring->threshold = DBGRING_FLUSH_THRESHOLD(ring);
114 ring->state = RING_SUSPEND;
115 ring->rem_len = 0;
116 ring->sched_pull = TRUE;
117 ring->pull_inactive = pull_inactive;
118 DHD_DBG_RING_UNLOCK(ring->lock, flags);
119
120 return BCME_OK;
121 }
122
123 void
dhd_dbg_ring_deinit(dhd_pub_t * dhdp,dhd_dbg_ring_t * ring)124 dhd_dbg_ring_deinit(dhd_pub_t *dhdp, dhd_dbg_ring_t *ring)
125 {
126 unsigned long flags = 0;
127
128 DHD_DBG_RING_LOCK(ring->lock, flags);
129 ring->id = 0;
130 ring->name[0] = 0;
131 ring->wp = ring->rp = 0;
132 memset(&ring->stat, 0, sizeof(ring->stat));
133 ring->threshold = 0;
134 ring->state = RING_STOP;
135 DHD_DBG_RING_UNLOCK(ring->lock, flags);
136
137 DHD_DBG_RING_LOCK_DEINIT(dhdp->osh, ring->lock);
138 }
139
140 void
dhd_dbg_ring_sched_pull(dhd_dbg_ring_t * ring,uint32 pending_len,os_pullreq_t pull_fn,void * os_pvt,const int id)141 dhd_dbg_ring_sched_pull(dhd_dbg_ring_t *ring, uint32 pending_len,
142 os_pullreq_t pull_fn, void *os_pvt, const int id)
143 {
144 unsigned long flags = 0;
145
146 DHD_DBG_RING_LOCK(ring->lock, flags);
147 /* if the current pending size is bigger than threshold and
148 * threshold is set
149 */
150 if (ring->threshold > 0 &&
151 (pending_len >= ring->threshold) && ring->sched_pull) {
152 /*
153 * Update the state and release the lock before calling
154 * the pull_fn. Do not transfer control to other layers
155 * with locks held. If the call back again calls into
156 * the same layer fro this context, can lead to deadlock.
157 */
158 ring->sched_pull = FALSE;
159 DHD_DBG_RING_UNLOCK(ring->lock, flags);
160 pull_fn(os_pvt, id);
161 } else {
162 DHD_DBG_RING_UNLOCK(ring->lock, flags);
163 }
164 }
165
166 uint32
dhd_dbg_ring_get_pending_len(dhd_dbg_ring_t * ring)167 dhd_dbg_ring_get_pending_len(dhd_dbg_ring_t *ring)
168 {
169 uint32 pending_len = 0;
170 unsigned long flags = 0;
171
172 DHD_DBG_RING_LOCK(ring->lock, flags);
173 if (ring->stat.written_bytes > ring->stat.read_bytes) {
174 pending_len = ring->stat.written_bytes - ring->stat.read_bytes;
175 } else if (ring->stat.written_bytes < ring->stat.read_bytes) {
176 pending_len = PENDING_LEN_MAX - ring->stat.read_bytes + ring->stat.written_bytes;
177 } else {
178 pending_len = 0;
179 }
180 DHD_DBG_RING_UNLOCK(ring->lock, flags);
181
182 return pending_len;
183 }
184
185 int
dhd_dbg_ring_push(dhd_dbg_ring_t * ring,dhd_dbg_ring_entry_t * hdr,void * data)186 dhd_dbg_ring_push(dhd_dbg_ring_t *ring, dhd_dbg_ring_entry_t *hdr, void *data)
187 {
188 unsigned long flags;
189 uint32 w_len;
190 uint32 avail_size;
191 dhd_dbg_ring_entry_t *w_entry, *r_entry;
192
193 if (!ring || !hdr || !data) {
194 return BCME_BADARG;
195 }
196
197 DHD_DBG_RING_LOCK(ring->lock, flags);
198
199 if (ring->state != RING_ACTIVE) {
200 DHD_DBG_RING_UNLOCK(ring->lock, flags);
201 return BCME_OK;
202 }
203
204 w_len = ENTRY_LENGTH(hdr);
205
206 DHD_DBGIF(("%s: RING%d[%s] hdr->len=%u, w_len=%u, wp=%d, rp=%d, ring_start=0x%p;"
207 " ring_size=%u\n",
208 __FUNCTION__, ring->id, ring->name, hdr->len, w_len, ring->wp, ring->rp,
209 ring->ring_buf, ring->ring_size));
210
211 if (w_len > ring->ring_size) {
212 DHD_DBG_RING_UNLOCK(ring->lock, flags);
213 DHD_ERROR(("%s: RING%d[%s] w_len=%u, ring_size=%u,"
214 " write size exceeds ring size !\n",
215 __FUNCTION__, ring->id, ring->name, w_len, ring->ring_size));
216 return BCME_ERROR;
217 }
218 /* Claim the space */
219 do {
220 avail_size = DBG_RING_CHECK_WRITE_SPACE(ring->rp, ring->wp, ring->ring_size);
221 if (avail_size <= w_len) {
222 /* Prepare the space */
223 if (ring->rp <= ring->wp) {
224 ring->tail_padded = TRUE;
225 ring->rem_len = ring->ring_size - ring->wp;
226 DHD_DBGIF(("%s: RING%d[%s] Insuffient tail space,"
227 " rp=%d, wp=%d, rem_len=%d, ring_size=%d,"
228 " avail_size=%d, w_len=%d\n", __FUNCTION__,
229 ring->id, ring->name, ring->rp, ring->wp,
230 ring->rem_len, ring->ring_size, avail_size,
231 w_len));
232
233 /* 0 pad insufficient tail space */
234 memset((uint8 *)ring->ring_buf + ring->wp, 0, ring->rem_len);
235 /* If read pointer is still at the beginning, make some room */
236 if (ring->rp == 0) {
237 r_entry = (dhd_dbg_ring_entry_t *)((uint8 *)ring->ring_buf +
238 ring->rp);
239 ring->rp += ENTRY_LENGTH(r_entry);
240 ring->stat.read_bytes += ENTRY_LENGTH(r_entry);
241 DHD_DBGIF(("%s: rp at 0, move by one entry length"
242 " (%u bytes)\n",
243 __FUNCTION__, (uint32)ENTRY_LENGTH(r_entry)));
244 }
245 if (ring->rp == ring->wp) {
246 ring->rp = 0;
247 }
248 ring->wp = 0;
249 DHD_DBGIF(("%s: new rp=%u, wp=%u\n",
250 __FUNCTION__, ring->rp, ring->wp));
251 } else {
252 /* Not enough space for new entry, free some up */
253 r_entry = (dhd_dbg_ring_entry_t *)((uint8 *)ring->ring_buf +
254 ring->rp);
255 /* check bounds before incrementing read ptr */
256 if (ring->rp + ENTRY_LENGTH(r_entry) >= ring->ring_size) {
257 DHD_ERROR(("%s: RING%d[%s] rp points out of boundary,"
258 "ring->wp=%u, ring->rp=%u, ring->ring_size=%d\n",
259 __FUNCTION__, ring->id, ring->name, ring->wp,
260 ring->rp, ring->ring_size));
261 ASSERT(0);
262 DHD_DBG_RING_UNLOCK(ring->lock, flags);
263 return BCME_BUFTOOSHORT;
264 }
265 ring->rp += ENTRY_LENGTH(r_entry);
266 /* skip padding if there is one */
267 if (ring->tail_padded &&
268 ((ring->rp + ring->rem_len) == ring->ring_size)) {
269 DHD_DBGIF(("%s: RING%d[%s] Found padding,"
270 " avail_size=%d, w_len=%d, set rp = 0\n",
271 __FUNCTION__,
272 ring->id, ring->name, avail_size, w_len));
273 ring->rp = 0;
274 ring->tail_padded = FALSE;
275 ring->rem_len = 0;
276 }
277 ring->stat.read_bytes += ENTRY_LENGTH(r_entry);
278 DHD_DBGIF(("%s: RING%d[%s] read_bytes=%d, wp=%d, rp=%d\n",
279 __FUNCTION__, ring->id, ring->name, ring->stat.read_bytes,
280 ring->wp, ring->rp));
281 }
282 } else {
283 break;
284 }
285 } while (TRUE);
286
287 /* check before writing to the ring */
288 if (ring->wp + w_len >= ring->ring_size) {
289 DHD_ERROR(("%s: RING%d[%s] wp pointed out of ring boundary, "
290 "wp=%d, ring_size=%d, w_len=%u\n", __FUNCTION__, ring->id,
291 ring->name, ring->wp, ring->ring_size, w_len));
292 ASSERT(0);
293 DHD_DBG_RING_UNLOCK(ring->lock, flags);
294 return BCME_BUFTOOLONG;
295 }
296
297 w_entry = (dhd_dbg_ring_entry_t *)((uint8 *)ring->ring_buf + ring->wp);
298 /* header */
299 memcpy(w_entry, hdr, DBG_RING_ENTRY_SIZE);
300 w_entry->len = hdr->len;
301 /* payload */
302 memcpy((char *)w_entry + DBG_RING_ENTRY_SIZE, data, w_entry->len);
303 /* update write pointer */
304 ring->wp += w_len;
305
306 /* update statistics */
307 ring->stat.written_records++;
308 ring->stat.written_bytes += w_len;
309 DHD_DBGIF(("%s : RING%d[%s] written_records %d, written_bytes %d, read_bytes=%d,"
310 " ring->threshold=%d, wp=%d, rp=%d\n", __FUNCTION__, ring->id, ring->name,
311 ring->stat.written_records, ring->stat.written_bytes, ring->stat.read_bytes,
312 ring->threshold, ring->wp, ring->rp));
313
314 DHD_DBG_RING_UNLOCK(ring->lock, flags);
315 return BCME_OK;
316 }
317
318 /*
319 * This function folds ring->lock, so callers of this function
320 * should not hold ring->lock.
321 */
322 int
dhd_dbg_ring_pull_single(dhd_dbg_ring_t * ring,void * data,uint32 buf_len,bool strip_header)323 dhd_dbg_ring_pull_single(dhd_dbg_ring_t *ring, void *data, uint32 buf_len, bool strip_header)
324 {
325 dhd_dbg_ring_entry_t *r_entry = NULL;
326 uint32 rlen = 0;
327 char *buf = NULL;
328
329 if (!ring || !data || buf_len <= 0) {
330 return 0;
331 }
332
333 /* pull from ring is allowed for inactive (suspended) ring
334 * in case of ecounters only, this is because, for ecounters
335 * when a trap occurs the ring is suspended and data is then
336 * pulled to dump it to a file. For other rings if ring is
337 * not in active state return without processing (as before)
338 */
339 if (!ring->pull_inactive && (ring->state != RING_ACTIVE)) {
340 goto exit;
341 }
342
343 if (ring->rp == ring->wp) {
344 goto exit;
345 }
346
347 DHD_DBGIF(("%s: RING%d[%s] buf_len=%u, wp=%d, rp=%d, ring_start=0x%p; ring_size=%u\n",
348 __FUNCTION__, ring->id, ring->name, buf_len, ring->wp, ring->rp,
349 ring->ring_buf, ring->ring_size));
350
351 r_entry = (dhd_dbg_ring_entry_t *)((uint8 *)ring->ring_buf + ring->rp);
352
353 /* Boundary Check */
354 rlen = ENTRY_LENGTH(r_entry);
355 if ((ring->rp + rlen) > ring->ring_size) {
356 DHD_ERROR(("%s: entry len %d is out of boundary of ring size %d,"
357 " current ring %d[%s] - rp=%d\n", __FUNCTION__, rlen,
358 ring->ring_size, ring->id, ring->name, ring->rp));
359 rlen = 0;
360 goto exit;
361 }
362
363 if (strip_header) {
364 rlen = r_entry->len;
365 buf = (char *)r_entry + DBG_RING_ENTRY_SIZE;
366 } else {
367 rlen = ENTRY_LENGTH(r_entry);
368 buf = (char *)r_entry;
369 }
370 if (rlen > buf_len) {
371 DHD_ERROR(("%s: buf len %d is too small for entry len %d\n",
372 __FUNCTION__, buf_len, rlen));
373 DHD_ERROR(("%s: ring %d[%s] - ring size=%d, wp=%d, rp=%d\n",
374 __FUNCTION__, ring->id, ring->name, ring->ring_size,
375 ring->wp, ring->rp));
376 ASSERT(0);
377 rlen = 0;
378 goto exit;
379 }
380
381 memcpy(data, buf, rlen);
382 /* update ring context */
383 ring->rp += ENTRY_LENGTH(r_entry);
384 /* don't pass wp but skip padding if there is one */
385 if (ring->rp != ring->wp &&
386 ring->tail_padded && ((ring->rp + ring->rem_len) >= ring->ring_size)) {
387 DHD_DBGIF(("%s: RING%d[%s] Found padding, rp=%d, wp=%d\n",
388 __FUNCTION__, ring->id, ring->name, ring->rp, ring->wp));
389 ring->rp = 0;
390 ring->tail_padded = FALSE;
391 ring->rem_len = 0;
392 }
393 if (ring->rp >= ring->ring_size) {
394 DHD_ERROR(("%s: RING%d[%s] rp pointed out of ring boundary,"
395 " rp=%d, ring_size=%d\n", __FUNCTION__, ring->id,
396 ring->name, ring->rp, ring->ring_size));
397 ASSERT(0);
398 rlen = 0;
399 goto exit;
400 }
401 ring->stat.read_bytes += ENTRY_LENGTH(r_entry);
402 DHD_DBGIF(("%s RING%d[%s]read_bytes %d, wp=%d, rp=%d\n", __FUNCTION__,
403 ring->id, ring->name, ring->stat.read_bytes, ring->wp, ring->rp));
404
405 exit:
406
407 return rlen;
408 }
409
410 int
dhd_dbg_ring_pull(dhd_dbg_ring_t * ring,void * data,uint32 buf_len,bool strip_hdr)411 dhd_dbg_ring_pull(dhd_dbg_ring_t *ring, void *data, uint32 buf_len, bool strip_hdr)
412 {
413 int32 r_len, total_r_len = 0;
414
415 if (!ring || !data)
416 return 0;
417
418 if (!ring->pull_inactive && (ring->state != RING_ACTIVE)) {
419 return 0;
420 }
421
422 while (buf_len > 0) {
423 r_len = dhd_dbg_ring_pull_single(ring, data, buf_len, strip_hdr);
424 if (r_len == 0)
425 break;
426 data = (uint8 *)data + r_len;
427 buf_len -= r_len;
428 total_r_len += r_len;
429 }
430
431 return total_r_len;
432 }
433
434 int
dhd_dbg_ring_config(dhd_dbg_ring_t * ring,int log_level,uint32 threshold)435 dhd_dbg_ring_config(dhd_dbg_ring_t *ring, int log_level, uint32 threshold)
436 {
437 unsigned long flags = 0;
438
439 if (!ring)
440 return BCME_BADADDR;
441
442 if (ring->state == RING_STOP)
443 return BCME_UNSUPPORTED;
444
445 DHD_DBG_RING_LOCK(ring->lock, flags);
446
447 if (log_level == 0)
448 ring->state = RING_SUSPEND;
449 else
450 ring->state = RING_ACTIVE;
451
452 ring->log_level = log_level;
453 ring->threshold = MIN(threshold, DBGRING_FLUSH_THRESHOLD(ring));
454
455 DHD_DBG_RING_UNLOCK(ring->lock, flags);
456
457 return BCME_OK;
458 }
459
460 void
dhd_dbg_ring_start(dhd_dbg_ring_t * ring)461 dhd_dbg_ring_start(dhd_dbg_ring_t *ring)
462 {
463 if (!ring)
464 return;
465
466 /* Initialize the information for the ring */
467 ring->state = RING_SUSPEND;
468 ring->log_level = 0;
469 ring->rp = ring->wp = 0;
470 ring->threshold = 0;
471 memset(&ring->stat, 0, sizeof(struct ring_statistics));
472 memset(ring->ring_buf, 0, ring->ring_size);
473 }
474