--- a/sys/dev/netif/plip/if_plip.c +++ b/sys/dev/netif/plip/if_plip.c @@ -90,6 +90,7 @@ #include #include #include +#include #include #include @@ -124,6 +125,32 @@ #define LPMAXERRS 100 #endif + +#ifndef LPMAXSPINTOTAL /* hard cap: total port polls per call */ +#define LPMAXSPINTOTAL 4000 +#endif + +/* + * DF-4657 fix: lpoutput()/lp_intr() spin on the status port inside + * crit_enter() with only per-byte budgets; the aggregate time with + * interrupts disabled was unbounded (LPMAXSPIN1 + payload*LPMAXSPIN2 + * port polls per packet, looped). Any unprivileged user routing + * packets out lp# could pin a CPU indefinitely. Impose a hard wall on + * the total handshake polls per if_output/if_intr invocation; tunable + * via hw.plip.maxspin for large MTUs on healthy links. + */ +static int lp_maxspin = LPMAXSPINTOTAL; +SYSCTL_INT(_hw, OID_AUTO, plip_maxspin, CTLFLAG_RW, &lp_maxspin, 0, + "max total plip handshake polls per output/intr"); +static int lp_spin_budget; + +static __inline int lp_spin_ok (void); +static __inline int +lp_spin_ok (void) +{ + return (lp_spin_budget-- > 0); +} + #define CLPIPHDRLEN 14 /* We send dummy ethernet addresses (two) + packet type in front of packet */ #define CLPIP_SHAKE 0x80 /* This bit toggles between nibble reception */ #define MLPIPHDRLEN CLPIPHDRLEN @@ -391,12 +418,12 @@ { ppb_wdtr(ppbus, ctxmitl[byte]); while (ppb_rstr(ppbus) & CLPIP_SHAKE) - if (--spin == 0) { + if (!lp_spin_ok() || --spin == 0) { return 1; } ppb_wdtr(ppbus, ctxmith[byte]); while (!(ppb_rstr(ppbus) & CLPIP_SHAKE)) - if (--spin == 0) { + if (!lp_spin_ok() || --spin == 0) { return 1; } return 0; @@ -408,14 +435,14 @@ u_char c, cl; while((ppb_rstr(ppbus) & CLPIP_SHAKE)) - if(!--spin) { + if(!lp_spin_ok() || !--spin) { return -1; } cl = ppb_rstr(ppbus); ppb_wdtr(ppbus, 0x10); while(!(ppb_rstr(ppbus) & CLPIP_SHAKE)) - if(!--spin) { + if(!lp_spin_ok() || !--spin) { return -1; } c = ppb_rstr(ppbus); @@ -453,6 +480,10 @@ crit_enter(); + lp_spin_budget = lp_maxspin; + + crit_enter(); + if (sc->sc_if.if_flags & IFF_LINK0) { /* Ack. the request */ @@ -512,7 +543,7 @@ j = LPMAXSPIN2; while((ppb_rstr(ppbus) & LPIP_SHAKE)) - if(!--j) goto err; + if(!lp_spin_ok() || !--j) goto err; c = ppb_rstr(ppbus); ppb_wdtr(ppbus, 0); @@ -521,6 +552,8 @@ j = LPMAXSPIN2; while (!((cl=ppb_rstr(ppbus)) & LPIP_SHAKE)) { + if (!lp_spin_ok()) + goto err; if (cl != c && (((cl = ppb_rstr(ppbus)) ^ 0xb8) & 0xf8) == (c & 0xf8)) @@ -575,11 +608,11 @@ { ppb_wdtr(ppbus, txmith[byte]); while (!(ppb_rstr(ppbus) & LPIP_SHAKE)) - if (--spin == 0) + if (!lp_spin_ok() || --spin == 0) return 1; ppb_wdtr(ppbus, txmitl[byte]); while (ppb_rstr(ppbus) & LPIP_SHAKE) - if (--spin == 0) + if (!lp_spin_ok() || --spin == 0) return 1; return 0; } @@ -603,6 +636,8 @@ err = 1; /* assume we're aborting because of an error */ + lp_spin_budget = lp_maxspin; + crit_enter(); /* Suspend (on laptops) or receive-errors might have taken us offline */ @@ -619,7 +654,7 @@ spin = LPMAXSPIN1; ppb_wdtr(ppbus, 0x08); while ((ppb_rstr(ppbus) & 0x08) == 0) - if (--spin == 0) { + if (!lp_spin_ok() || --spin == 0) { goto nend; }