diff options
author | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-09-03 01:54:34 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-09-03 01:54:34 +0200 |
commit | cfd25faa2857ee9de75910d81530be62d7ba4704 (patch) | |
tree | 1bebf05a269d6923290e5e362a5bdf46fda6e8f4 /sys/src/9/pc/usbuhci.c | |
parent | 1be10947ba37964feb10e0cf45576b029043866b (diff) |
usb: fix isowrite putsamples race
Diffstat (limited to 'sys/src/9/pc/usbuhci.c')
-rw-r--r-- | sys/src/9/pc/usbuhci.c | 28 |
1 files changed, 16 insertions, 12 deletions
diff --git a/sys/src/9/pc/usbuhci.c b/sys/src/9/pc/usbuhci.c index a063fcd9a..282648ff7 100644 --- a/sys/src/9/pc/usbuhci.c +++ b/sys/src/9/pc/usbuhci.c @@ -993,21 +993,27 @@ interrupt(Ureg*, void *a) * it is activated and tdu advanced. */ static long -putsamples(Isoio *iso, uchar *b, long count) +putsamples(Ctlr *ctlr, Isoio *iso, uchar *b, long count) { - long tot; - long n; + long n, tot, left; + Td *tdu; for(tot = 0; isocanwrite(iso) && tot < count; tot += n){ n = count-tot; - if(n > maxtdlen(iso->tdu) - iso->nleft) - n = maxtdlen(iso->tdu) - iso->nleft; - memmove(iso->tdu->data+iso->nleft, b+tot, n); + tdu = iso->tdu; + left = iso->nleft; + if(n > maxtdlen(tdu) - left) + n = maxtdlen(tdu) - left; + iunlock(ctlr); /* can pagefault here */ + memmove(tdu->data+left, b+tot, n); + ilock(ctlr); + if(tdu != iso->tdu) + continue; iso->nleft += n; - if(iso->nleft == maxtdlen(iso->tdu)){ - tdisoinit(iso, iso->tdu, iso->nleft); + if(iso->nleft == maxtdlen(tdu)){ + tdisoinit(iso, tdu, iso->nleft); + iso->tdu = tdu->next; iso->nleft = 0; - iso->tdu = iso->tdu->next; } } return tot; @@ -1065,9 +1071,7 @@ episowrite(Ep *ep, Isoio *iso, void *a, long count) } if(iso->state != Qrun) panic("episowrite: iso not running"); - iunlock(ctlr); /* We could page fault here */ - nw = putsamples(iso, b+tot, count-tot); - ilock(ctlr); + nw = putsamples(ctlr, iso, b+tot, count-tot); } while(isodelay(iso) == 0){ iunlock(ctlr); |