DF-4657 / fix.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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | --- a/sys/dev/netif/plip/if_plip.c +++ b/sys/dev/netif/plip/if_plip.c @@ -90,6 +90,7 @@ #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/rman.h> +#include <sys/sysctl.h> #include <sys/thread2.h> #include <machine/clock.h> @@ -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; } |