DF-5847 / diag5847.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | --- nd6.c.orig 2026-10-26 00:00:00.000000000 +0000 +++ nd6.c 2026-10-26 00:00:00.000000000 +0000 @@ -141,6 +141,44 @@ struct nd_prhead nd_prefix = { 0 }; struct mtx nd6_mtx = MTX_INITIALIZER("nd6"); +/* DF-5847 DIAGNOSTIC ONLY -- never for upstream */ +struct nd6dbg { + int op; /* 1=add 2=del 3=resolve 4=nd6free */ + int cpu; + void *ln; + void *rt; + void *rt_llinfo; + long t; /* ticks */ + void *ln_prev; + void *ln_next; +}; +static struct nd6dbg nd6dbg_ring[64]; +static volatile int nd6dbg_idx; + +static void +nd6dbg_log(int op, void *lnp, void *rtp) +{ + struct nd6dbg *d; + struct llinfo_nd6 *ln = lnp; + int i = atomic_fetchadd_int(&nd6dbg_idx, 1); + + d = &nd6dbg_ring[i & 63]; + d->op = op; + d->cpu = mycpuid; + d->ln = lnp; + d->rt = rtp; + d->rt_llinfo = rtp ? ((struct rtentry *)rtp)->rt_llinfo : NULL; + d->t = ticks; + d->ln_prev = ln ? ln->ln_prev : NULL; + d->ln_next = ln ? ln->ln_next : NULL; +} + +static void +nd6dbg_dump(void) +{ + int i, n; + + kprintf("DF5847: ring dump (idx=%d):\n", nd6dbg_idx); + n = nd6dbg_idx; + for (i = n - 16; i < n; i++) { + struct nd6dbg *d = &nd6dbg_ring[i & 63]; + kprintf("DF5847: [%d] op=%d cpu=%d ln=%p rt=%p rt_llinfo=%p " + "ln_prev=%p ln_next=%p t=%ld\n", + i, d->op, d->cpu, d->ln, d->rt, d->rt_llinfo, + d->ln_prev, d->ln_next, d->t); + } +} +/* END DF-5847 DIAGNOSTIC */ + int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL; #define SIN6(s) ((struct sockaddr_in6 *)s) @@ -486,6 +524,22 @@ /* sanity check */ if (!rt) panic("rt=0 in nd6_timer(ln=%p)", ln); + /* DF-5847 DIAGNOSTIC: catch walk-over-unlinked (ln_prev==NULL + * is the RTM_DELETE tombstone) before the llinfo compare. */ + if (ln->ln_prev == NULL) { + kprintf("DF5847: timer walked unlinked ln=%p rt=%p " + "cpu=%d\n", ln, rt, mycpuid); + nd6dbg_dump(); + panic("DF5847 walk-over-unlinked ln=%p", ln); + } + if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln) { + kprintf("DF5847: invariant trip ln=%p rt_llinfo=%p " + "cpu=%d\n", ln, rt->rt_llinfo, mycpuid); + nd6dbg_dump(); + panic("rt_llinfo(%p) is not equal to ln(%p)", + rt->rt_llinfo, ln); + } if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln) panic("rt_llinfo(%p) is not equal to ln(%p)", rt->rt_llinfo, ln); @@ -1028,6 +1082,9 @@ struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next; struct nd_defrouter *dr; struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr; + nd6dbg_log(4, (void *)ln, (void *)rt); + /* * we used to have kpfctlinput(PRC_HOSTDEAD) here. * even though it is not harmful, it was not really necessary. @@ -1300,6 +1357,8 @@ nd6_inuse++; nd6_allocated++; bzero(ln, sizeof(*ln)); + nd6dbg_log(req == RTM_ADD ? 1 : 3, (void *)ln, (void *)rt); + ln->ln_rt = rt; /* this is required for "ndp" command. - shin */ if (req == RTM_ADD) { @@ -1410,6 +1469,8 @@ in6m = IN6_LOOKUP_MULTI(&llsol, ifp); if (in6m) in6_delmulti(in6m); } + nd6dbg_log(2, (void *)ln, (void *)rt); nd6_inuse--; ln->ln_next->ln_prev = ln->ln_prev; ln->ln_prev->ln_next = ln->ln_next; |