Skip to content

Commit 31a5e9f

Browse files
widgetiiVixand
andauthored
openipc_frame_ts: tunable ring_depth module_param (closes #200) (#201)
Per-channel ring depth was hard-coded to 256 events, sized for the SR-anchor consumer in #155 which reads cached state every ~5 s and tolerates stale entries. For consumers that want every frame's (pts_us, wall_ns) — frame-by-frame latency histograms, evidence chains, multi-sensor sync correlators — 256 is too small when the reader's dispatch latency is bursty. Empirically on cv500 / hi3516av300_lite (#200 Obs 3): under RTSP+RTCP load through libevent, ~52 dropped/s accumulate over 30 s sessions with the consumer draining in batches of 8 per on_readable until EAGAIN. The consumer is not under-draining; libevent just isn't dispatched often enough between the network event ticks, and a 256-slot ring fills in the gap. Convert OPENIPC_FT_DEPTH from a compile-time #define to a ring_depth module_param (default 256, range 16..65536, rounded up to next power-of-2 at init for the head/tail wrap mask). Per-channel rings now kmalloc'd in __init; freed in __exit. Operators wanting per-frame consumers under load can: insmod open_openipc_frame_ts.ko ring_depth=4096 (16× default, ~8 s buffer at 240 fps × 2 types; 128 KiB/chn × 8 chn = 1 MiB worst case). Default 256 keeps the memory footprint identical for existing builds. The fix is non-disruptive: at default params the binary behaviour is identical to before (same depth, same wrap math, same drop semantics). Only the storage moved from BSS to heap. xref: #155 (chrdev introduction), #199 (cv500 / V4 / V4A MIPI_FS push moved to ISP_INT_FE_FSTART — non-disruptive event source that yields the high push rate driving the drops on cv500). Co-authored-by: Vasiliy Yakovlev <vixand@openipc.org>
1 parent 67d793c commit 31a5e9f

1 file changed

Lines changed: 81 additions & 15 deletions

File tree

kernel/openipc_frame_ts/openipc_frame_ts.c

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,33 @@
3939
#define OPENIPC_FT_MAX_CHN 8
4040
#define OPENIPC_FT_MAX_EVT_TYPE 2 /* MIPI_FS + ISP_FEND so far */
4141
/*
42-
* Per-channel ring depth (power of 2). 256 events ≈ 0.5 s of buffer at
43-
* 240 fps × 2 event types — covers high-fps modes (imx335 800x480 240 fps)
44-
* with margin for reader-side jitter (scheduler latency, printf-to-disk).
45-
* At 32 B/event the per-channel cost is 8 KiB.
42+
* Per-channel ring depth bounds. Must be powers of two — push uses
43+
* `head & (depth-1)` wrap math, which only works for power-of-2 depths.
44+
*
45+
* Default 256 ≈ 0.5 s buffer at 240 fps × 2 event types (8 KiB/chn). Fine
46+
* for the SR-anchor use case (reads cached state every ~5 s and tolerates
47+
* stale entries).
48+
*
49+
* For per-frame consumers — frame-by-frame latency histograms, evidence
50+
* chains, multi-sensor sync correlators — the default may be undersized
51+
* if the reader's dispatch latency is bursty (libevent under RTSP+RTCP
52+
* load on cv500 was observed to lose ~52 events/s during 30 s sessions,
53+
* see openhisilicon#200). Raise via `insmod open_openipc_frame_ts
54+
* ring_depth=4096` (16× default, ~8 s buffer; 128 KiB/chn × 8 chn = 1 MiB
55+
* total worst case).
4656
*/
47-
#define OPENIPC_FT_DEPTH 256
57+
#define OPENIPC_FT_DEPTH_MIN 16
58+
#define OPENIPC_FT_DEPTH_MAX 65536
59+
#define OPENIPC_FT_DEPTH_DEFAULT 256
60+
61+
static unsigned int ring_depth = OPENIPC_FT_DEPTH_DEFAULT;
62+
module_param(ring_depth, uint, 0444);
63+
MODULE_PARM_DESC(ring_depth,
64+
"Per-channel ring depth (power of 2, 16..65536, default 256). "
65+
"Bump for per-frame consumers under bursty reader load.");
66+
67+
static unsigned int g_ring_depth;
68+
static unsigned int g_ring_mask;
4869
/*
4970
* Drop double-pushes of the SAME event type within these intervals.
5071
*
@@ -74,7 +95,7 @@ static const u64 openipc_ft_min_interval_ns[OPENIPC_FT_MAX_EVT_TYPE] = {
7495
};
7596

7697
struct openipc_ft_chn {
77-
struct openipc_frame_ts_event ring[OPENIPC_FT_DEPTH];
98+
struct openipc_frame_ts_event *ring; /* kmalloc'd, g_ring_depth entries */
7899
unsigned int head;
79100
unsigned int tail;
80101
u32 seq[OPENIPC_FT_MAX_EVT_TYPE];
@@ -170,12 +191,12 @@ static void strobe_on_event(unsigned int event_type)
170191

171192
static inline unsigned int ring_count(const struct openipc_ft_chn *c)
172193
{
173-
return (c->head - c->tail) & (OPENIPC_FT_DEPTH - 1);
194+
return (c->head - c->tail) & g_ring_mask;
174195
}
175196

176197
static inline bool ring_full(const struct openipc_ft_chn *c)
177198
{
178-
return ring_count(c) == (OPENIPC_FT_DEPTH - 1);
199+
return ring_count(c) == g_ring_mask;
179200
}
180201

181202
static inline bool ring_empty(const struct openipc_ft_chn *c)
@@ -216,7 +237,7 @@ void openipc_frame_ts_push(unsigned int vi_chn, unsigned int event_type)
216237

217238
if (ring_full(c)) {
218239
/* drop oldest */
219-
c->tail = (c->tail + 1) & (OPENIPC_FT_DEPTH - 1);
240+
c->tail = (c->tail + 1) & g_ring_mask;
220241
c->dropped++;
221242
}
222243
e = &c->ring[c->head];
@@ -226,7 +247,7 @@ void openipc_frame_ts_push(unsigned int vi_chn, unsigned int event_type)
226247
e->seq = c->seq[event_type]++;
227248
e->event_type = event_type;
228249
e->reserved = 0;
229-
c->head = (c->head + 1) & (OPENIPC_FT_DEPTH - 1);
250+
c->head = (c->head + 1) & g_ring_mask;
230251
spin_unlock_irqrestore(&c->lock, flags);
231252

232253
strobe_on_event(event_type);
@@ -312,7 +333,7 @@ static bool pop_one(u32 chn_mask, u32 evt_mask,
312333
struct openipc_frame_ts_event *cand =
313334
&c->ring[c->tail];
314335

315-
c->tail = (c->tail + 1) & (OPENIPC_FT_DEPTH - 1);
336+
c->tail = (c->tail + 1) & g_ring_mask;
316337
if (event_passes(cand, chn_mask, evt_mask)) {
317338
*out = *cand;
318339
got = true;
@@ -607,13 +628,43 @@ static const struct attribute_group strobe_attr_group = {
607628
.attrs = strobe_attrs,
608629
};
609630

631+
static unsigned int sanitize_ring_depth(unsigned int req)
632+
{
633+
unsigned int d = req;
634+
unsigned int p;
635+
636+
if (d < OPENIPC_FT_DEPTH_MIN)
637+
d = OPENIPC_FT_DEPTH_MIN;
638+
if (d > OPENIPC_FT_DEPTH_MAX)
639+
d = OPENIPC_FT_DEPTH_MAX;
640+
641+
/* round up to next power of 2 */
642+
p = 1;
643+
while (p < d)
644+
p <<= 1;
645+
return p;
646+
}
647+
610648
static int __init openipc_ft_init(void)
611649
{
612650
unsigned int i;
613651
int ret;
614652

615-
for (i = 0; i < OPENIPC_FT_MAX_CHN; i++)
653+
g_ring_depth = sanitize_ring_depth(ring_depth);
654+
g_ring_mask = g_ring_depth - 1;
655+
if (g_ring_depth != ring_depth)
656+
pr_info("openipc_frame_ts: ring_depth %u → %u (clamped + power-of-2)\n",
657+
ring_depth, g_ring_depth);
658+
659+
for (i = 0; i < OPENIPC_FT_MAX_CHN; i++) {
660+
g_chn[i].ring = kmalloc_array(g_ring_depth,
661+
sizeof(struct openipc_frame_ts_event), GFP_KERNEL);
662+
if (!g_chn[i].ring) {
663+
ret = -ENOMEM;
664+
goto err_free_rings;
665+
}
616666
spin_lock_init(&g_chn[i].lock);
667+
}
617668

618669
init_waitqueue_head(&g_wait);
619670

@@ -627,7 +678,7 @@ static int __init openipc_ft_init(void)
627678
ret = misc_register(&g_miscdev);
628679
if (ret) {
629680
pr_err("openipc_frame_ts: misc_register failed: %d\n", ret);
630-
return ret;
681+
goto err_free_rings;
631682
}
632683

633684
ret = sysfs_create_group(&g_miscdev.this_device->kobj,
@@ -636,20 +687,35 @@ static int __init openipc_ft_init(void)
636687
pr_warn("openipc_frame_ts: strobe sysfs attrs not created: %d\n",
637688
ret);
638689

639-
pr_info("openipc_frame_ts: /dev/%s minor=%d, event types=%u\n",
640-
OPENIPC_FT_NAME, g_miscdev.minor, OPENIPC_FT_MAX_EVT_TYPE);
690+
pr_info("openipc_frame_ts: /dev/%s minor=%d, event types=%u, ring_depth=%u\n",
691+
OPENIPC_FT_NAME, g_miscdev.minor, OPENIPC_FT_MAX_EVT_TYPE,
692+
g_ring_depth);
641693
return 0;
694+
695+
err_free_rings:
696+
while (i--) {
697+
kfree(g_chn[i].ring);
698+
g_chn[i].ring = NULL;
699+
}
700+
return ret;
642701
}
643702

644703
static void __exit openipc_ft_exit(void)
645704
{
705+
unsigned int i;
706+
646707
sysfs_remove_group(&g_miscdev.this_device->kobj, &strobe_attr_group);
647708
hrtimer_cancel(&g_strobe_timer);
648709
mutex_lock(&g_strobe_lock);
649710
g_strobe_mode = STROBE_OFF;
650711
strobe_release_gpio_locked();
651712
mutex_unlock(&g_strobe_lock);
652713
misc_deregister(&g_miscdev);
714+
715+
for (i = 0; i < OPENIPC_FT_MAX_CHN; i++) {
716+
kfree(g_chn[i].ring);
717+
g_chn[i].ring = NULL;
718+
}
653719
}
654720

655721
module_init(openipc_ft_init);

0 commit comments

Comments
 (0)