Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

relayfs: support a counter tracking if data is too big to write

It really doesn't matter if the user/admin knows what the last too big
value is. Record how many times this case is triggered would be helpful.

Solve the existing issue where relay_reset() doesn't restore the value.

Store the counter in the per-cpu buffer structure instead of the global
buffer structure. It also solves the racy condition which is likely to
happen when a few of per-cpu buffers encounter the too big data case and
then access the global field last_toobig without lock protection.

Remove the printk in relay_close() since kernel module can directly call
relay_stats() as they want.

Link: https://lkml.kernel.org/r/20250612061201.34272-6-kerneljasonxing@gmail.com
Signed-off-by: Jason Xing <kernelxing@tencent.com>
Reviewed-by: Yushan Zhou <katrinzhou@tencent.com>
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Jason Xing and committed by
Andrew Morton
19f3cb64 7f217389

+13 -10
+3 -2
include/linux/relay.h
··· 33 33 */ 34 34 enum { 35 35 RELAY_STATS_BUF_FULL = (1 << 0), 36 + RELAY_STATS_WRT_BIG = (1 << 1), 36 37 37 - RELAY_STATS_LAST = RELAY_STATS_BUF_FULL, 38 + RELAY_STATS_LAST = RELAY_STATS_WRT_BIG, 38 39 }; 39 40 40 41 struct rchan_buf_stats 41 42 { 42 43 unsigned int full_count; /* counter for buffer full */ 44 + unsigned int big_count; /* counter for too big to write */ 43 45 }; 44 46 45 47 /* ··· 81 79 const struct rchan_callbacks *cb; /* client callbacks */ 82 80 struct kref kref; /* channel refcount */ 83 81 void *private_data; /* for user-defined data */ 84 - size_t last_toobig; /* tried to log event > subbuf size */ 85 82 struct rchan_buf * __percpu *buf; /* per-cpu channel buffers */ 86 83 int is_global; /* One global buffer ? */ 87 84 struct list_head list; /* for channel list */
+10 -8
kernel/relay.c
··· 303 303 buf->data = buf->start; 304 304 buf->offset = 0; 305 305 buf->stats.full_count = 0; 306 + buf->stats.big_count = 0; 306 307 307 308 for (i = 0; i < buf->chan->n_subbufs; i++) 308 309 buf->padding[i] = 0; ··· 603 602 return length; 604 603 605 604 toobig: 606 - buf->chan->last_toobig = length; 605 + buf->stats.big_count++; 607 606 return 0; 608 607 } 609 608 EXPORT_SYMBOL_GPL(relay_switch_subbuf); ··· 663 662 if ((buf = *per_cpu_ptr(chan->buf, i))) 664 663 relay_close_buf(buf); 665 664 666 - if (chan->last_toobig) 667 - printk(KERN_WARNING "relay: one or more items not logged " 668 - "[item size (%zd) > sub-buffer size (%zd)]\n", 669 - chan->last_toobig, chan->subbuf_size); 670 - 671 665 list_del(&chan->list); 672 666 kref_put(&chan->kref, relay_destroy_channel); 673 667 mutex_unlock(&relay_channels_mutex); ··· 715 719 rbuf = *per_cpu_ptr(chan->buf, 0); 716 720 if (flags & RELAY_STATS_BUF_FULL) 717 721 count = rbuf->stats.full_count; 722 + else if (flags & RELAY_STATS_WRT_BIG) 723 + count = rbuf->stats.big_count; 718 724 } else { 719 725 for_each_online_cpu(i) { 720 726 rbuf = *per_cpu_ptr(chan->buf, i); 721 - if (rbuf && flags & RELAY_STATS_BUF_FULL) 722 - count += rbuf->stats.full_count; 727 + if (rbuf) { 728 + if (flags & RELAY_STATS_BUF_FULL) 729 + count += rbuf->stats.full_count; 730 + else if (flags & RELAY_STATS_WRT_BIG) 731 + count += rbuf->stats.big_count; 732 + } 723 733 } 724 734 } 725 735