Commit 1d9dff94f7 for frr

commit 1d9dff94f7b6801ce3f2b3f8fa0e7d9dae5d12b8
Author: Mark Stapp <mjs@cisco.com>
Date:   Thu Oct 29 14:40:32 2026 -0400

    lib: disable task scheduling from event lib show commands

    There are several show commands in lib/event that need to
    hold the event-loop mutex while producing output. The vty
    lib code cannot try to schedule a task - a background write
    of buffered data for example - during those commands. We add
    a special api that avoids task scheduling, and use that around
    the lib/event show commands' loops.

    Signed-off-by: Mark Stapp <mjs@cisco.com>

diff --git a/lib/event.c b/lib/event.c
index 1ad0f3048b..53025cb44b 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -315,6 +315,11 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
 	tmp.funcname = "TOTAL";
 	tmp.types = filter;

+	/* Defer background write scheduling from the vty - we're holding that lock
+	 * here.
+	 */
+	vty_defer_scheduling(vty, true);
+
 	frr_with_mutex (&masters_mtx) {
 		for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) {
 			const char *name = m->name ? m->name : "main";
@@ -349,6 +354,9 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
 		}
 	}

+	/* Resume background write scheduling from the vty */
+	vty_defer_scheduling(vty, false);
+
 	vty_out(vty, "\n");
 	vty_out(vty, "Total Event statistics\n");
 	vty_out(vty, "-------------------------\n");
@@ -600,6 +608,11 @@ DEFUN_NOSH (show_event_poll,
 	struct listnode *node;
 	struct event_loop *m;

+	/* Defer background write scheduling from the vty - we're holding that lock
+	 * here.
+	 */
+	vty_defer_scheduling(vty, true);
+
 	frr_with_mutex (&masters_mtx) {
 		for (ALL_LIST_ELEMENTS_RO(masters, node, m)) {
 			pthread_mutex_lock(&m->mtx);
@@ -608,6 +621,9 @@ DEFUN_NOSH (show_event_poll,
 		}
 	}

+	/* Resume background write scheduling from the vty */
+	vty_defer_scheduling(vty, false);
+
 	return CMD_SUCCESS;
 }

@@ -663,6 +679,11 @@ DEFPY_NOSH (show_event_timers,
 	struct listnode *node;
 	struct event_loop *m;

+	/* Defer background write scheduling from the vty - we're holding that lock
+	 * here.
+	 */
+	vty_defer_scheduling(vty, true);
+
 	frr_with_mutex (&masters_mtx) {
 		for (ALL_LIST_ELEMENTS_RO(masters, node, m)) {
 			pthread_mutex_lock(&m->mtx);
@@ -671,6 +692,9 @@ DEFPY_NOSH (show_event_timers,
 		}
 	}

+	/* Resume background write scheduling from the vty */
+	vty_defer_scheduling(vty, false);
+
 	return CMD_SUCCESS;
 }

diff --git a/lib/vty.c b/lib/vty.c
index 35e48590fb..4bd2440f44 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -2208,7 +2208,11 @@ static int vtysh_flush(struct vty *vty)

 	switch (ret) {
 	case BUFFER_PENDING:
-		vty_event(VTYSH_WRITE, vty);
+		/* Some callers may be holding the scheduling mutex; defer
+		 * for them.
+		 */
+		if (!vty->avoid_sched)
+			vty_event(VTYSH_WRITE, vty);
 		break;
 	case BUFFER_ERROR:
 		flog_err(EC_LIB_SOCKET, "%s: write error to fd %d, closing",
@@ -2976,6 +2980,15 @@ static void vty_event(enum vty_event event, struct vty *vty)
 	}
 }

+/* Defer scheduling tasks, used when the caller will be holding the
+ * scheduling mutex and must avoid deadlock. These callers are inside
+ * the scheduling library, so far.
+ */
+void vty_defer_scheduling(struct vty *vty, bool defer_p)
+{
+	vty->avoid_sched = defer_p;
+}
+
 DEFUN_NOSH (config_who,
        config_who_cmd,
        "who",
diff --git a/lib/vty.h b/lib/vty.h
index 63afd092e4..6f5cdb677c 100644
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -238,6 +238,14 @@ struct vty {
 	 */
 	size_t vty_buf_threshold;
 	size_t vty_buf_size_accum;
+
+	/* During output, it's possible for a vty to schedule a task to
+	 * continue flushing buffered output. Some specialized callers
+	 * in the library code may be holding the event-loop mutex while
+	 * they produce output: in those paths, we defer task-scheduling
+	 * to avoid deadlocks
+	 */
+	bool avoid_sched;
 };

 static inline void vty_push_context(struct vty *vty, int node, uint64_t id)
@@ -438,6 +446,12 @@ static inline bool vty_is_closed(const struct vty *vty)
  */
 extern void vty_resume_response(struct vty *vty, int ret);

+/* Defer scheduling tasks, used when the caller will be holding the
+ * scheduling mutex and must avoid deadlock. These callers are inside
+ * the scheduling library, so far.
+ */
+void vty_defer_scheduling(struct vty *vty, bool defer_p);
+
 /* --------------------------------------------------- */
 /* Callbacks for Mgmtd front-end CLI vty modifications */
 /* --------------------------------------------------- */