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