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