* [PATCH v2 iproute2-next] utils: add fflush_monitor() helper
@ 2026-04-27 8:19 Eric Dumazet
2026-04-27 16:31 ` Stephen Hemminger
0 siblings, 1 reply; 3+ messages in thread
From: Eric Dumazet @ 2026-04-27 8:19 UTC (permalink / raw)
To: David Ahern, Stephen Hemminger
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, netdev,
eric.dumazet, Eric Dumazet
Some fflush() calls only make sense for monitor programs.
For other cases, forcing a flush is expensive.
After this patch, ip, tc and ss are correctly buffering most of their
output when redirected to a file.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/utils.h | 7 +++++++
ip/ipaddress.c | 12 +++++++-----
ip/iplink.c | 4 ++--
ip/ipmonitor.c | 1 +
ip/ipmptcp.c | 10 +++++-----
ip/ipneigh.c | 4 ++--
ip/ipnetconf.c | 2 +-
ip/ipnetns.c | 2 +-
ip/ipnexthop.c | 12 ++++++------
ip/iproute.c | 4 ++--
ip/iprule.c | 2 +-
ip/iptoken.c | 2 +-
ip/tcp_metrics.c | 2 +-
lib/utils.c | 1 +
misc/ss.c | 3 ++-
tc/tc_class.c | 2 +-
tc/tc_filter.c | 2 +-
tc/tc_monitor.c | 1 +
tc/tc_qdisc.c | 2 +-
19 files changed, 44 insertions(+), 31 deletions(-)
diff --git a/include/utils.h b/include/utils.h
index b68d6bc4edcf9ca335148f731cd240871b6c809b..2ed3b75ebb45e0f0e4aedd9bdf21375f74c2bbef 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -38,6 +38,7 @@ extern int numeric;
extern bool do_all;
extern int echo_request;
extern int use_iec;
+extern int monitor_mode;
#define SPRINT_BSIZE 64
#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
@@ -399,4 +400,10 @@ FILE *generic_proc_open(const char *env, const char *name);
int open_fds_add(int fd);
void open_fds_close(void);
+static inline void fflush_monitor(FILE *fp)
+{
+ if (monitor_mode)
+ fflush(fp);
+}
+
#endif /* __UTILS_H__ */
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 4d93a04a8555e141ae30a4aaeb42ac607a181c9b..14aa98e77452c9681c9d3660f6a882df56b44f1c 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -894,7 +894,7 @@ static int print_linkinfo_brief(FILE *fp, const char *name,
print_string(PRINT_FP, NULL, "%s", "\n");
}
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
@@ -1346,10 +1346,12 @@ int print_linkinfo(struct nlmsghdr *n, void *arg)
}
print_string(PRINT_FP, NULL, "%s", "\n");
- fflush(fp);
+ fflush_monitor(fp);
/* prettier here if stderr and stdout go to the same place */
- if (truncated_vfs)
+ if (truncated_vfs) {
+ fflush(fp);
fprintf(stderr, "Truncated VF list: %s\n", name);
+ }
return 1;
}
@@ -1732,7 +1734,7 @@ int print_addrinfo(struct nlmsghdr *n, void *arg)
}
print_string(PRINT_FP, NULL, "%s", "\n");
brief_exit:
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
@@ -1768,7 +1770,7 @@ static int print_selected_addrinfo(struct ifinfomsg *ifi,
if (brief) {
print_string(PRINT_FP, NULL, "%s", "\n");
- fflush(fp);
+ fflush_monitor(fp);
}
return 0;
}
diff --git a/ip/iplink.c b/ip/iplink.c
index fce6631d2d821a8739d28805e13d98f6804bda30..806f86987ed7c8af5ddd40f04a44fb8ce8debfaf 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -1296,7 +1296,7 @@ static void print_mpls_stats(FILE *fp, struct rtattr *attr)
print_nl();
print_mpls_link_stats(fp, stats, " ");
print_string(PRINT_FP, NULL, "%s", "\n");
- fflush(fp);
+ fflush_monitor(fp);
}
static void print_af_stats_attr(FILE *fp, int ifindex, struct rtattr *attr)
@@ -1359,7 +1359,7 @@ static int print_af_stats(struct nlmsghdr *n, void *arg)
if (tb[IFLA_STATS_AF_SPEC])
print_af_stats_attr(fp, ifsm->ifindex, tb[IFLA_STATS_AF_SPEC]);
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
diff --git a/ip/ipmonitor.c b/ip/ipmonitor.c
index 1f4e860f8d6726edffcf9bf81636c6555757dd09..23bd667a2f7a27e620d0f6dd13a16476c1ac0d08 100644
--- a/ip/ipmonitor.c
+++ b/ip/ipmonitor.c
@@ -324,6 +324,7 @@ int do_ipmonitor(int argc, char **argv)
return err;
}
+ monitor_mode = 1;
if (rtnl_open(&rth, groups) < 0)
exit(1);
diff --git a/ip/ipmptcp.c b/ip/ipmptcp.c
index 3df89db54b4e00f8fe2d18828621999512622d59..37df15ba9b5f2d6974f5fdd2360025ce9a017316 100644
--- a/ip/ipmptcp.c
+++ b/ip/ipmptcp.c
@@ -276,7 +276,7 @@ static int print_mptcp_addrinfo(struct rtattr *addrinfo)
print_nl();
close_json_object();
- fflush(stdout);
+ fflush_monitor(stdout);
return 0;
}
@@ -325,7 +325,7 @@ static int mptcp_addr_dump(void)
}
delete_json_obj();
- fflush(stdout);
+ fflush_monitor(stdout);
return 0;
}
@@ -349,7 +349,7 @@ static int mptcp_addr_show(int argc, char **argv)
ret = print_mptcp_addr(answer, stdout);
delete_json_obj();
free(answer);
- fflush(stdout);
+ fflush_monitor(stdout);
return ret;
}
@@ -430,7 +430,7 @@ static int print_mptcp_limit(struct nlmsghdr *n, void *arg)
print_nl();
close_json_object();
- fflush(stdout);
+ fflush_monitor(stdout);
return 0;
}
@@ -592,7 +592,7 @@ static int mptcp_monitor_msg(struct rtnl_ctrl_data *ctrl,
out:
print_nl();
close_json_object();
- fflush(stdout);
+ fflush_monitor(stdout);
return 0;
}
diff --git a/ip/ipneigh.c b/ip/ipneigh.c
index e678545ad535ad83722d1daef51f08c569df8236..2b74fccdfd9c5e39d675b8d1419e23bf9f6b6e3d 100644
--- a/ip/ipneigh.c
+++ b/ip/ipneigh.c
@@ -298,7 +298,7 @@ static int print_neigh_brief(FILE *fp, struct ndmsg *r, struct rtattr *tb[])
print_string(PRINT_FP, NULL, "%s", "\n");
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
@@ -472,7 +472,7 @@ int print_neigh(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", "");
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
diff --git a/ip/ipnetconf.c b/ip/ipnetconf.c
index 020eff786d0b82f3033a71c2f4fc3d5b1af5f099..efb4da963d463481fa27f41fcb8e65db844381cf 100644
--- a/ip/ipnetconf.c
+++ b/ip/ipnetconf.c
@@ -140,7 +140,7 @@ int print_netconf(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg)
close_json_object();
print_string(PRINT_FP, NULL, "\n", NULL);
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
diff --git a/ip/ipnetns.c b/ip/ipnetns.c
index 3a33a3adacee31080c3ea9d0a52e455243423a75..abe5746415176c137c9755a6211cc4b7cbabe4e2 100644
--- a/ip/ipnetns.c
+++ b/ip/ipnetns.c
@@ -327,7 +327,7 @@ int print_nsid(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", NULL);
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
diff --git a/ip/ipnexthop.c b/ip/ipnexthop.c
index d5c429369d10f629823639fa7e6bfb70be5226d6..a9ebffc8e50101c5c4b2480fd4d8e1bb1a3c7588 100644
--- a/ip/ipnexthop.c
+++ b/ip/ipnexthop.c
@@ -826,7 +826,7 @@ int print_cache_nexthop(struct nlmsghdr *n, void *arg, bool process_cache)
__print_nexthop_entry(fp, NULL, &nhe, n->nlmsg_type == RTM_DELNEXTHOP);
print_string(PRINT_FP, NULL, "%s", "\n");
- fflush(fp);
+ fflush_monitor(fp);
if (process_cache)
ipnh_cache_process_nlmsg(n, &nhe);
@@ -882,7 +882,7 @@ int print_nexthop_bucket(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "%s", "\n");
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
@@ -1178,7 +1178,7 @@ static int ipnh_get_id(__u32 id)
}
delete_json_obj();
- fflush(stdout);
+ fflush_monitor(stdout);
free(answer);
@@ -1266,7 +1266,7 @@ static int ipnh_list_flush(int argc, char **argv, int action)
}
delete_json_obj();
- fflush(stdout);
+ fflush_monitor(stdout);
return 0;
}
@@ -1342,7 +1342,7 @@ static int ipnh_bucket_list(int argc, char **argv)
}
delete_json_obj();
- fflush(stdout);
+ fflush_monitor(stdout);
return 0;
}
@@ -1383,7 +1383,7 @@ static int ipnh_bucket_get_id(__u32 id, __u16 bucket_index)
}
delete_json_obj();
- fflush(stdout);
+ fflush_monitor(stdout);
free(answer);
diff --git a/ip/iproute.c b/ip/iproute.c
index c2538894da633b56f5b926ecafda4960a48e9fbd..c6a6ba9dd37076acfceb9b259874a50e7cd3c3d0 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -1012,7 +1012,7 @@ int print_route(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", NULL);
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
@@ -2035,7 +2035,7 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action)
}
delete_json_obj();
- fflush(stdout);
+ fflush_monitor(stdout);
return 0;
}
diff --git a/ip/iprule.c b/ip/iprule.c
index 3af02da24950ae224614e8e45e3de400675eee57..5bcafe11986f84f4b1946d758647d909456aeb61 100644
--- a/ip/iprule.c
+++ b/ip/iprule.c
@@ -599,7 +599,7 @@ int print_rule(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", "");
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
diff --git a/ip/iptoken.c b/ip/iptoken.c
index f25a7c8b21f57c941d975a0255b0613f8a38f999..c19f9e0b4fdb282d1835959b025adf5bdf5a14e8 100644
--- a/ip/iptoken.c
+++ b/ip/iptoken.c
@@ -85,7 +85,7 @@ static int print_token(struct nlmsghdr *n, void *arg)
"ifname", "%s\n",
ll_index_to_name(ifi->ifi_index));
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
diff --git a/ip/tcp_metrics.c b/ip/tcp_metrics.c
index 9c8fb07240bf01511aa2fc8cc2e80603db3252c9..b69bb282e1f30f53843e2cd9e51bfc3debf4310b 100644
--- a/ip/tcp_metrics.c
+++ b/ip/tcp_metrics.c
@@ -334,7 +334,7 @@ static int process_msg(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", "");
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
diff --git a/lib/utils.c b/lib/utils.c
index 1215fe31cb3f63c0fa768fef60da77a641c51581..7017b0e6afacd8e770fb9f8b26aa5a319c1279e7 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -41,6 +41,7 @@ int timestamp_short;
int pretty;
int use_iec;
int human_readable;
+int monitor_mode;
const char *_SL_ = "\n";
static int open_fds[5];
diff --git a/misc/ss.c b/misc/ss.c
index 14e9f27a75321556240a5f290b8bcf51c605a4c2..0f9b37177d375ddf70cb34674992723b5c4883be 100644
--- a/misc/ss.c
+++ b/misc/ss.c
@@ -5544,7 +5544,7 @@ static int generic_show_sock(struct nlmsghdr *nlh, void *arg)
render();
- fflush(stdout);
+ fflush_monitor(stdout);
return ret;
}
@@ -5580,6 +5580,7 @@ static int handle_follow_request(struct filter *f)
f->rth_for_killing = &rth2;
}
+ monitor_mode = 1;
if (rtnl_dump_filter(&rth, generic_show_sock, f))
ret = -1;
diff --git a/tc/tc_class.c b/tc/tc_class.c
index 6d707d8c924f4b5e90201d4de8e9779f82ce17f3..8197566281fad47d4efcf4768ed406ab809dd08a 100644
--- a/tc/tc_class.c
+++ b/tc/tc_class.c
@@ -380,7 +380,7 @@ int print_class(struct nlmsghdr *n, void *arg)
close_json_object();
}
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
diff --git a/tc/tc_filter.c b/tc/tc_filter.c
index 7db850bda11a3408ec66b457c9fc590f3a734f65..633f027d79865d98ba1ccba48af71d247dfc7a48 100644
--- a/tc/tc_filter.c
+++ b/tc/tc_filter.c
@@ -374,7 +374,7 @@ int print_filter(struct nlmsghdr *n, void *arg)
print_ext_msg(tb);
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
diff --git a/tc/tc_monitor.c b/tc/tc_monitor.c
index 5b9bccbed4140241a4d7a5cffc50865106224867..49bd4b6a460fbfe41a35e06f92f07dbe56a6be2d 100644
--- a/tc/tc_monitor.c
+++ b/tc/tc_monitor.c
@@ -100,6 +100,7 @@ int do_tcmonitor(int argc, char **argv)
return ret;
}
+ monitor_mode = 1;
if (rtnl_open(&rth, groups) < 0)
exit(1);
diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c
index 7eb9a31baa31fb91a8141c9eca1135e76a52fa8b..a8ef8fc9f70d91609d6ed61c2a46b29120512fd2 100644
--- a/tc/tc_qdisc.c
+++ b/tc/tc_qdisc.c
@@ -344,7 +344,7 @@ int print_qdisc(struct nlmsghdr *n, void *arg)
print_ext_msg(tb);
close_json_object();
- fflush(fp);
+ fflush_monitor(fp);
return 0;
}
--
2.54.0.545.g6539524ca2-goog
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v2 iproute2-next] utils: add fflush_monitor() helper
2026-04-27 8:19 [PATCH v2 iproute2-next] utils: add fflush_monitor() helper Eric Dumazet
@ 2026-04-27 16:31 ` Stephen Hemminger
2026-04-27 16:47 ` Eric Dumazet
0 siblings, 1 reply; 3+ messages in thread
From: Stephen Hemminger @ 2026-04-27 16:31 UTC (permalink / raw)
To: Eric Dumazet
Cc: David Ahern, David S . Miller, Jakub Kicinski, Paolo Abeni,
netdev, eric.dumazet
On Mon, 27 Apr 2026 08:19:53 +0000
Eric Dumazet <edumazet@google.com> wrote:
> Some fflush() calls only make sense for monitor programs.
>
> For other cases, forcing a flush is expensive.
>
> After this patch, ip, tc and ss are correctly buffering most of their
> output when redirected to a file.
>
> Signed-off-by: Eric Dumazet <edumazet@google.com>
What about pulling all the fflush from the leaf calls, and move the fflush
to one common place. There are some of these that need to come back
like the addr flush loop, this is just a semi-automated first pass.
From 815ed9eb987fc458987e753b3a2fa1786578b485 Mon Sep 17 00:00:00 2001
From: Stephen Hemminger <stephen@networkplumber.org>
Date: Mon, 27 Apr 2026 09:26:58 -0700
Subject: [PATCH] flush stdout only in monitor mode
As Eric pointed out fflush() only makes sense in monitor mode.
In other modes buffering the output helps with large outputs
like route dumps.
Move the fflush() into rtnl_listen() which is the common code
used by all monitoring subcommands.
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
bridge/fdb.c | 2 --
bridge/link.c | 2 --
bridge/mdb.c | 1 -
bridge/mst.c | 1 -
bridge/vlan.c | 4 ----
bridge/vni.c | 2 --
genl/ctrl.c | 2 --
ip/ipaddress.c | 8 --------
ip/ipfou.c | 1 -
ip/ipila.c | 1 -
ip/ipioam6.c | 1 -
ip/ipl2tp.c | 2 --
ip/iplink.c | 2 --
ip/ipmptcp.c | 6 ------
ip/ipmroute.c | 1 -
ip/ipneigh.c | 4 ----
ip/ipnetconf.c | 1 -
ip/ipnetns.c | 1 -
ip/ipnexthop.c | 6 ------
ip/ipntable.c | 2 --
ip/ipprefix.c | 1 -
ip/iproute.c | 4 ----
ip/iprule.c | 1 -
ip/ipseg6.c | 1 -
ip/ipstats.c | 2 --
ip/iptoken.c | 2 --
ip/iptuntap.c | 1 -
ip/rtmon.c | 1 -
ip/tcp_metrics.c | 4 ----
ip/xfrm_monitor.c | 5 -----
ip/xfrm_policy.c | 2 --
ip/xfrm_state.c | 1 -
lib/libnetlink.c | 2 ++
tc/tc_class.c | 1 -
tc/tc_filter.c | 1 -
tc/tc_qdisc.c | 1 -
36 files changed, 2 insertions(+), 78 deletions(-)
diff --git a/bridge/fdb.c b/bridge/fdb.c
index d57b5750..05f093b5 100644
--- a/bridge/fdb.c
+++ b/bridge/fdb.c
@@ -309,7 +309,6 @@ int print_fdb(struct nlmsghdr *n, void *arg)
print_string(PRINT_ANY, "state", "%s\n",
state_n2a(r->ndm_state));
close_json_object();
- fflush(fp);
return 0;
}
@@ -417,7 +416,6 @@ static int fdb_show(int argc, char **argv)
exit(1);
}
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/bridge/link.c b/bridge/link.c
index 1c8faa85..7638797d 100644
--- a/bridge/link.c
+++ b/bridge/link.c
@@ -292,7 +292,6 @@ int print_linkinfo(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "%s", "\n");
close_json_object();
- fflush(fp);
return 0;
}
@@ -669,7 +668,6 @@ static int brlink_show(int argc, char **argv)
}
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/bridge/mdb.c b/bridge/mdb.c
index 72490971..112deeb9 100644
--- a/bridge/mdb.c
+++ b/bridge/mdb.c
@@ -501,7 +501,6 @@ static int mdb_show(int argc, char **argv)
close_json_object();
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/bridge/mst.c b/bridge/mst.c
index 37362c45..8e46c762 100644
--- a/bridge/mst.c
+++ b/bridge/mst.c
@@ -158,7 +158,6 @@ static int mst_show(int argc, char **argv)
}
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/bridge/vlan.c b/bridge/vlan.c
index 3c240207..27d31ba8 100644
--- a/bridge/vlan.c
+++ b/bridge/vlan.c
@@ -756,7 +756,6 @@ static int print_vlan_stats(struct nlmsghdr *n, void *arg)
struct if_stats_msg *ifsm = NLMSG_DATA(n);
struct rtattr *tb[IFLA_STATS_MAX+1];
int len = n->nlmsg_len;
- FILE *fp = arg;
len -= NLMSG_LENGTH(sizeof(*ifsm));
if (len < 0) {
@@ -778,7 +777,6 @@ static int print_vlan_stats(struct nlmsghdr *n, void *arg)
print_vlan_stats_attr(tb[IFLA_STATS_LINK_XSTATS_SLAVE],
ifsm->ifindex);
- fflush(fp);
return 0;
}
@@ -1205,7 +1203,6 @@ static int vlan_show(int argc, char **argv, int subject)
out:
delete_json_obj();
- fflush(stdout);
return 0;
}
@@ -1260,7 +1257,6 @@ static int vlan_global_show(int argc, char **argv)
close_vlan_port();
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/bridge/vni.c b/bridge/vni.c
index 57b04c8c..b28647fd 100644
--- a/bridge/vni.c
+++ b/bridge/vni.c
@@ -333,7 +333,6 @@ int print_vnifilter_rtm(struct nlmsghdr *n, void *arg)
if (opened)
close_vni_port();
- fflush(stdout);
return 0;
}
@@ -381,7 +380,6 @@ static int vni_show(int argc, char **argv)
}
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/genl/ctrl.c b/genl/ctrl.c
index 9412c2f0..88ff4cb8 100644
--- a/genl/ctrl.c
+++ b/genl/ctrl.c
@@ -275,7 +275,6 @@ static int print_ctrl(struct rtnl_ctrl_data *ctrl,
struct genlmsghdr *ghdr = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr *attrs;
- FILE *fp = (FILE *) arg;
if (n->nlmsg_type != GENL_ID_CTRL) {
fprintf(stderr, "Not a controller message, nlmsg_len=%d "
@@ -339,7 +338,6 @@ static int print_ctrl(struct rtnl_ctrl_data *ctrl,
if (tb[CTRL_ATTR_MCAST_GROUPS])
print_ctrl_mcast(tb[CTRL_ATTR_MCAST_GROUPS]);
- fflush(fp);
return 0;
}
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
index 4d93a04a..f538bf88 100644
--- a/ip/ipaddress.c
+++ b/ip/ipaddress.c
@@ -894,7 +894,6 @@ static int print_linkinfo_brief(FILE *fp, const char *name,
print_string(PRINT_FP, NULL, "%s", "\n");
}
- fflush(fp);
return 0;
}
@@ -1346,7 +1345,6 @@ int print_linkinfo(struct nlmsghdr *n, void *arg)
}
print_string(PRINT_FP, NULL, "%s", "\n");
- fflush(fp);
/* prettier here if stderr and stdout go to the same place */
if (truncated_vfs)
fprintf(stderr, "Truncated VF list: %s\n", name);
@@ -1732,7 +1730,6 @@ int print_addrinfo(struct nlmsghdr *n, void *arg)
}
print_string(PRINT_FP, NULL, "%s", "\n");
brief_exit:
- fflush(fp);
return 0;
}
@@ -1768,7 +1765,6 @@ static int print_selected_addrinfo(struct ifinfomsg *ifi,
if (brief) {
print_string(PRINT_FP, NULL, "%s", "\n");
- fflush(fp);
}
return 0;
}
@@ -2006,7 +2002,6 @@ static int ipaddr_flush(void)
else
printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":"");
}
- fflush(stdout);
return 0;
}
round++;
@@ -2015,7 +2010,6 @@ static int ipaddr_flush(void)
if (show_stats) {
printf("\n*** Round %d, deleting %d addresses ***\n", round, filter.flushed);
- fflush(stdout);
}
/* If we are flushing, and specifying primary, then we
@@ -2027,7 +2021,6 @@ static int ipaddr_flush(void)
goto flush_done;
}
fprintf(stderr, "*** Flush remains incomplete after %d rounds. ***\n", max_flush_loops);
- fflush(stderr);
return 1;
}
@@ -2351,7 +2344,6 @@ static int ipaddr_list_flush_or_save(int argc, char **argv, int action)
print_link_stats(stdout, n);
close_json_object();
}
- fflush(stdout);
out:
free_nlmsg_chain(ainfo);
diff --git a/ip/ipfou.c b/ip/ipfou.c
index 760cfee2..8c1e061a 100644
--- a/ip/ipfou.c
+++ b/ip/ipfou.c
@@ -322,7 +322,6 @@ static int do_show(int argc, char **argv)
return 1;
}
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/ip/ipila.c b/ip/ipila.c
index 80f34f29..2590de9a 100644
--- a/ip/ipila.c
+++ b/ip/ipila.c
@@ -159,7 +159,6 @@ static int do_list(int argc, char **argv)
return 1;
}
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/ip/ipioam6.c b/ip/ipioam6.c
index 18860989..118fcd1c 100644
--- a/ip/ipioam6.c
+++ b/ip/ipioam6.c
@@ -261,7 +261,6 @@ static int ioam6_do_cmd(void)
exit(1);
}
delete_json_obj();
- fflush(stdout);
}
return 0;
diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c
index 87a4b898..97f7ff08 100644
--- a/ip/ipl2tp.c
+++ b/ip/ipl2tp.c
@@ -467,7 +467,6 @@ static int get_session(struct l2tp_data *p)
exit(1);
}
delete_json_obj();
- fflush(stdout);
return 0;
}
@@ -502,7 +501,6 @@ static int get_tunnel(struct l2tp_data *p)
exit(1);
}
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/ip/iplink.c b/ip/iplink.c
index fce6631d..eae51438 100644
--- a/ip/iplink.c
+++ b/ip/iplink.c
@@ -1296,7 +1296,6 @@ static void print_mpls_stats(FILE *fp, struct rtattr *attr)
print_nl();
print_mpls_link_stats(fp, stats, " ");
print_string(PRINT_FP, NULL, "%s", "\n");
- fflush(fp);
}
static void print_af_stats_attr(FILE *fp, int ifindex, struct rtattr *attr)
@@ -1359,7 +1358,6 @@ static int print_af_stats(struct nlmsghdr *n, void *arg)
if (tb[IFLA_STATS_AF_SPEC])
print_af_stats_attr(fp, ifsm->ifindex, tb[IFLA_STATS_AF_SPEC]);
- fflush(fp);
return 0;
}
diff --git a/ip/ipmptcp.c b/ip/ipmptcp.c
index 3df89db5..02cf72ce 100644
--- a/ip/ipmptcp.c
+++ b/ip/ipmptcp.c
@@ -276,7 +276,6 @@ static int print_mptcp_addrinfo(struct rtattr *addrinfo)
print_nl();
close_json_object();
- fflush(stdout);
return 0;
}
@@ -320,12 +319,10 @@ static int mptcp_addr_dump(void)
if (rtnl_dump_filter(&genl_rth, print_mptcp_addr, stdout) < 0) {
fprintf(stderr, "Dump terminated\n");
delete_json_obj();
- fflush(stdout);
return -2;
}
delete_json_obj();
- fflush(stdout);
return 0;
}
@@ -349,7 +346,6 @@ static int mptcp_addr_show(int argc, char **argv)
ret = print_mptcp_addr(answer, stdout);
delete_json_obj();
free(answer);
- fflush(stdout);
return ret;
}
@@ -430,7 +426,6 @@ static int print_mptcp_limit(struct nlmsghdr *n, void *arg)
print_nl();
close_json_object();
- fflush(stdout);
return 0;
}
@@ -592,7 +587,6 @@ static int mptcp_monitor_msg(struct rtnl_ctrl_data *ctrl,
out:
print_nl();
close_json_object();
- fflush(stdout);
return 0;
}
diff --git a/ip/ipmroute.c b/ip/ipmroute.c
index c540eab1..164632e6 100644
--- a/ip/ipmroute.c
+++ b/ip/ipmroute.c
@@ -206,7 +206,6 @@ int print_mroute(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", NULL);
close_json_object();
- fflush(fp);
return 0;
}
diff --git a/ip/ipneigh.c b/ip/ipneigh.c
index e678545a..b21fd637 100644
--- a/ip/ipneigh.c
+++ b/ip/ipneigh.c
@@ -298,7 +298,6 @@ static int print_neigh_brief(FILE *fp, struct ndmsg *r, struct rtattr *tb[])
print_string(PRINT_FP, NULL, "%s", "\n");
close_json_object();
- fflush(fp);
return 0;
}
@@ -472,7 +471,6 @@ int print_neigh(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", "");
close_json_object();
- fflush(fp);
return 0;
}
@@ -630,7 +628,6 @@ static int do_show_or_flush(int argc, char **argv, int flush)
else
printf("*** Flush is complete after %d round%s ***\n", round, round > 1?"s":"");
}
- fflush(stdout);
return 0;
}
round++;
@@ -638,7 +635,6 @@ static int do_show_or_flush(int argc, char **argv, int flush)
exit(1);
if (show_stats) {
printf("\n*** Round %d, deleting %d entries ***\n", round, filter.flushed);
- fflush(stdout);
}
filter.state &= ~NUD_FAILED;
}
diff --git a/ip/ipnetconf.c b/ip/ipnetconf.c
index 020eff78..57bb99d1 100644
--- a/ip/ipnetconf.c
+++ b/ip/ipnetconf.c
@@ -140,7 +140,6 @@ int print_netconf(struct rtnl_ctrl_data *ctrl, struct nlmsghdr *n, void *arg)
close_json_object();
print_string(PRINT_FP, NULL, "\n", NULL);
- fflush(fp);
return 0;
}
diff --git a/ip/ipnetns.c b/ip/ipnetns.c
index 3a33a3ad..587534ae 100644
--- a/ip/ipnetns.c
+++ b/ip/ipnetns.c
@@ -327,7 +327,6 @@ int print_nsid(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", NULL);
close_json_object();
- fflush(fp);
return 0;
}
diff --git a/ip/ipnexthop.c b/ip/ipnexthop.c
index d5c42936..14b525aa 100644
--- a/ip/ipnexthop.c
+++ b/ip/ipnexthop.c
@@ -826,7 +826,6 @@ int print_cache_nexthop(struct nlmsghdr *n, void *arg, bool process_cache)
__print_nexthop_entry(fp, NULL, &nhe, n->nlmsg_type == RTM_DELNEXTHOP);
print_string(PRINT_FP, NULL, "%s", "\n");
- fflush(fp);
if (process_cache)
ipnh_cache_process_nlmsg(n, &nhe);
@@ -882,7 +881,6 @@ int print_nexthop_bucket(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "%s", "\n");
close_json_object();
- fflush(fp);
return 0;
}
@@ -1178,7 +1176,6 @@ static int ipnh_get_id(__u32 id)
}
delete_json_obj();
- fflush(stdout);
free(answer);
@@ -1266,7 +1263,6 @@ static int ipnh_list_flush(int argc, char **argv, int action)
}
delete_json_obj();
- fflush(stdout);
return 0;
}
@@ -1342,7 +1338,6 @@ static int ipnh_bucket_list(int argc, char **argv)
}
delete_json_obj();
- fflush(stdout);
return 0;
}
@@ -1383,7 +1378,6 @@ static int ipnh_bucket_get_id(__u32 id, __u16 bucket_index)
}
delete_json_obj();
- fflush(stdout);
free(answer);
diff --git a/ip/ipntable.c b/ip/ipntable.c
index 54db9b62..51fa067c 100644
--- a/ip/ipntable.c
+++ b/ip/ipntable.c
@@ -532,7 +532,6 @@ static void print_ndtstats(const struct ndt_stats *ndts)
static int print_ntable(struct nlmsghdr *n, void *arg)
{
- FILE *fp = (FILE *)arg;
struct ndtmsg *ndtm = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr *tb[NDTA_MAX+1];
@@ -633,7 +632,6 @@ static int print_ntable(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", "");
close_json_object();
- fflush(fp);
return 0;
}
diff --git a/ip/ipprefix.c b/ip/ipprefix.c
index 4ec30524..5704f2f9 100644
--- a/ip/ipprefix.c
+++ b/ip/ipprefix.c
@@ -81,7 +81,6 @@ int print_prefix(struct nlmsghdr *n, void *arg)
}
fprintf(fp, "\n");
- fflush(fp);
return 0;
}
diff --git a/ip/iproute.c b/ip/iproute.c
index c2538894..820e8701 100644
--- a/ip/iproute.c
+++ b/ip/iproute.c
@@ -1012,7 +1012,6 @@ int print_route(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", NULL);
close_json_object();
- fflush(fp);
return 0;
}
@@ -1781,7 +1780,6 @@ static int iproute_flush(int family, rtnl_filter_t filter_fn)
printf("*** Flush is complete after %d round%s ***\n",
round, round > 1 ? "s" : "");
}
- fflush(stdout);
return 0;
}
round++;
@@ -1798,7 +1796,6 @@ static int iproute_flush(int family, rtnl_filter_t filter_fn)
if (show_stats) {
printf("\n*** Round %d, deleting %d entries ***\n",
round, filter.flushed);
- fflush(stdout);
}
}
}
@@ -2035,7 +2032,6 @@ static int iproute_list_flush_or_save(int argc, char **argv, int action)
}
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/ip/iprule.c b/ip/iprule.c
index 3af02da2..b56b1b18 100644
--- a/ip/iprule.c
+++ b/ip/iprule.c
@@ -599,7 +599,6 @@ int print_rule(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", "");
close_json_object();
- fflush(fp);
return 0;
}
diff --git a/ip/ipseg6.c b/ip/ipseg6.c
index 305b8961..efd37227 100644
--- a/ip/ipseg6.c
+++ b/ip/ipseg6.c
@@ -197,7 +197,6 @@ static int seg6_do_cmd(void)
exit(1);
}
delete_json_obj();
- fflush(stdout);
}
return 0;
diff --git a/ip/ipstats.c b/ip/ipstats.c
index f0f8dcdc..8a2ac85e 100644
--- a/ip/ipstats.c
+++ b/ip/ipstats.c
@@ -879,7 +879,6 @@ static int ipstats_dump(struct ipstats_stat_enabled *enabled)
rc = -2;
}
- fflush(stdout);
return rc;
}
@@ -1355,6 +1354,5 @@ int ipstats_print(struct nlmsghdr *n, void *arg)
if (rc)
return rc;
- fflush(fp);
return 0;
}
diff --git a/ip/iptoken.c b/ip/iptoken.c
index f25a7c8b..8bd9b4dc 100644
--- a/ip/iptoken.c
+++ b/ip/iptoken.c
@@ -41,7 +41,6 @@ static void usage(void)
static int print_token(struct nlmsghdr *n, void *arg)
{
struct rtnl_dump_args *args = arg;
- FILE *fp = args->fp;
int ifindex = args->ifindex;
struct ifinfomsg *ifi = NLMSG_DATA(n);
int len = n->nlmsg_len;
@@ -85,7 +84,6 @@ static int print_token(struct nlmsghdr *n, void *arg)
"ifname", "%s\n",
ll_index_to_name(ifi->ifi_index));
close_json_object();
- fflush(fp);
return 0;
}
diff --git a/ip/iptuntap.c b/ip/iptuntap.c
index 6718ec6c..0f46fe6a 100644
--- a/ip/iptuntap.c
+++ b/ip/iptuntap.c
@@ -444,7 +444,6 @@ static int do_show(int argc, char **argv)
}
delete_json_obj();
- fflush(stdout);
return 0;
}
diff --git a/ip/rtmon.c b/ip/rtmon.c
index 470f4ba6..507a80da 100644
--- a/ip/rtmon.c
+++ b/ip/rtmon.c
@@ -46,7 +46,6 @@ static int dump_msg(struct rtnl_ctrl_data *ctrl,
if (!init_phase)
write_stamp(fp);
fwrite((void *)n, 1, NLMSG_ALIGN(n->nlmsg_len), fp);
- fflush(fp);
return 0;
}
diff --git a/ip/tcp_metrics.c b/ip/tcp_metrics.c
index 9c8fb072..09943f42 100644
--- a/ip/tcp_metrics.c
+++ b/ip/tcp_metrics.c
@@ -156,7 +156,6 @@ static void print_tcp_metrics(struct rtattr *a)
static int process_msg(struct nlmsghdr *n, void *arg)
{
- FILE *fp = (FILE *) arg;
struct genlmsghdr *ghdr;
struct rtattr *attrs[TCP_METRICS_ATTR_MAX + 1], *a;
const char *h;
@@ -334,7 +333,6 @@ static int process_msg(struct nlmsghdr *n, void *arg)
print_string(PRINT_FP, NULL, "\n", "");
close_json_object();
- fflush(fp);
return 0;
}
@@ -477,7 +475,6 @@ static int tcpm_do_cmd(int cmd, int argc, char **argv)
} else if (show_stats)
printf("*** Flush is complete after %d round%s ***\n",
round, round > 1 ? "s" : "");
- fflush(stdout);
return 0;
}
round++;
@@ -486,7 +483,6 @@ static int tcpm_do_cmd(int cmd, int argc, char **argv)
if (show_stats) {
printf("\n*** Round %d, deleting %d entries ***\n",
round, f.flushed);
- fflush(stdout);
}
}
return 0;
diff --git a/ip/xfrm_monitor.c b/ip/xfrm_monitor.c
index 1f67fe9d..a96358b0 100644
--- a/ip/xfrm_monitor.c
+++ b/ip/xfrm_monitor.c
@@ -85,7 +85,6 @@ static int xfrm_acquire_print(struct nlmsghdr *n, void *arg)
if (oneline)
fprintf(fp, "\n");
- fflush(fp);
return 0;
}
@@ -114,7 +113,6 @@ static int xfrm_state_flush_print(struct nlmsghdr *n, void *arg)
if (oneline)
fprintf(fp, "\n");
- fflush(fp);
return 0;
}
@@ -151,7 +149,6 @@ static int xfrm_policy_flush_print(struct nlmsghdr *n, void *arg)
if (oneline)
fprintf(fp, "\n");
- fflush(fp);
return 0;
}
@@ -232,7 +229,6 @@ static int xfrm_ae_print(struct nlmsghdr *n, void *arg)
xfrm_usersa_print(&id->sa_id, id->reqid, fp);
fprintf(fp, "\n");
- fflush(fp);
return 0;
}
@@ -257,7 +253,6 @@ static int xfrm_mapping_print(struct nlmsghdr *n, void *arg)
xfrm_usersa_print(&map->id, map->reqid, fp);
fprintf(fp, "\n");
- fflush(fp);
return 0;
}
diff --git a/ip/xfrm_policy.c b/ip/xfrm_policy.c
index 8687ced3..e60acd5b 100644
--- a/ip/xfrm_policy.c
+++ b/ip/xfrm_policy.c
@@ -565,7 +565,6 @@ int xfrm_policy_print(struct nlmsghdr *n, void *arg)
if (oneline)
fprintf(fp, "\n");
- fflush(fp);
return 0;
}
@@ -1232,7 +1231,6 @@ int xfrm_policy_default_print(struct nlmsghdr *n, FILE *fp)
fprintf(fp, " in: %s\n", xfrm_policy_to_str(up->in));
fprintf(fp, " fwd: %s\n", xfrm_policy_to_str(up->fwd));
fprintf(fp, " out: %s\n", xfrm_policy_to_str(up->out));
- fflush(fp);
return 0;
}
diff --git a/ip/xfrm_state.c b/ip/xfrm_state.c
index 252a7a53..dcd08d59 100644
--- a/ip/xfrm_state.c
+++ b/ip/xfrm_state.c
@@ -1051,7 +1051,6 @@ static int __do_xfrm_state_print(struct nlmsghdr *n, void *arg, bool nokeys)
if (oneline)
fprintf(fp, "\n");
- fflush(fp);
return 0;
}
diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index 6b275a1f..7c74ebd3 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -1275,6 +1275,8 @@ int rtnl_listen(struct rtnl_handle *rtnl,
}
err = handler(&ctrl, h, jarg);
+ fflush(stdout);
+
if (err < 0)
return err;
diff --git a/tc/tc_class.c b/tc/tc_class.c
index 6d707d8c..ea6d986a 100644
--- a/tc/tc_class.c
+++ b/tc/tc_class.c
@@ -380,7 +380,6 @@ int print_class(struct nlmsghdr *n, void *arg)
close_json_object();
}
close_json_object();
- fflush(fp);
return 0;
}
diff --git a/tc/tc_filter.c b/tc/tc_filter.c
index 7db850bd..b1dcf1ba 100644
--- a/tc/tc_filter.c
+++ b/tc/tc_filter.c
@@ -374,7 +374,6 @@ int print_filter(struct nlmsghdr *n, void *arg)
print_ext_msg(tb);
close_json_object();
- fflush(fp);
return 0;
}
diff --git a/tc/tc_qdisc.c b/tc/tc_qdisc.c
index 7eb9a31b..634d5f70 100644
--- a/tc/tc_qdisc.c
+++ b/tc/tc_qdisc.c
@@ -344,7 +344,6 @@ int print_qdisc(struct nlmsghdr *n, void *arg)
print_ext_msg(tb);
close_json_object();
- fflush(fp);
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v2 iproute2-next] utils: add fflush_monitor() helper
2026-04-27 16:31 ` Stephen Hemminger
@ 2026-04-27 16:47 ` Eric Dumazet
0 siblings, 0 replies; 3+ messages in thread
From: Eric Dumazet @ 2026-04-27 16:47 UTC (permalink / raw)
To: Stephen Hemminger
Cc: David Ahern, David S . Miller, Jakub Kicinski, Paolo Abeni,
netdev, eric.dumazet
On Mon, Apr 27, 2026 at 9:31 AM Stephen Hemminger
<stephen@networkplumber.org> wrote:
>
> On Mon, 27 Apr 2026 08:19:53 +0000
> Eric Dumazet <edumazet@google.com> wrote:
>
> > Some fflush() calls only make sense for monitor programs.
> >
> > For other cases, forcing a flush is expensive.
> >
> > After this patch, ip, tc and ss are correctly buffering most of their
> > output when redirected to a file.
> >
> > Signed-off-by: Eric Dumazet <edumazet@google.com>
>
> What about pulling all the fflush from the leaf calls, and move the fflush
> to one common place. There are some of these that need to come back
> like the addr flush loop, this is just a semi-automated first pass.
SGTM, thanks.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-04-27 16:47 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-27 8:19 [PATCH v2 iproute2-next] utils: add fflush_monitor() helper Eric Dumazet
2026-04-27 16:31 ` Stephen Hemminger
2026-04-27 16:47 ` Eric Dumazet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox