Re: Please test; midi(4): make midi{read,write}_filtops mp safe
On Sun, Sep 24, 2023 at 11:03:54PM +0300, Vitaliy Makkoveev wrote: > Please test this diff, I have no midi(4) devices. > > midi(4) already uses `audio_lock' mutex(9) for filterops, but they are > still kernel locked. Wipe out old selwakeup API and make them MP safe. > knote_locked(9) will not grab kernel lock, so call it directly from > interrupt handlers instead of scheduling software interrupts. > Works for me (tested with a usb device), pulling the usb cable properly wakes up any process waiting for input/output. ok ratchov@ with the two changes below > @@ -531,20 +518,8 @@ midiattach(struct device *parent, struct > } > #endif > > - sc->inbuf.softintr = softintr_establish(IPL_SOFTMIDI, > - midi_buf_wakeup, >inbuf); > - if (sc->inbuf.softintr == NULL) { > - printf("%s: can't establish input softintr\n", DEVNAME(sc)); > - return; > - } > - > - sc->outbuf.softintr = softintr_establish(IPL_SOFTMIDI, > - midi_buf_wakeup, >outbuf); > - if (sc->outbuf.softintr == NULL) { > - printf("%s: can't establish output softintr\n", DEVNAME(sc)); > - softintr_disestablish(sc->inbuf.softintr); > - return; > - } > + klist_init_mutex(>inbuf.klist, _lock); > + klist_init_mutex(>outbuf.klist, _lock); > > sc->hw_if = hwif; > sc->hw_hdl = hdl; > @@ -580,27 +555,19 @@ mididetach(struct device *self, int flag > KERNEL_ASSERT_LOCKED(); > if (sc->flags & FREAD) { > wakeup(>inbuf.blocking); > - mtx_enter(_lock); > - selwakeup(>inbuf.sel); > - mtx_leave(_lock); > + knote(>inbuf.klist, 0); AFAIU, the knote() calls are not necessary because klist_invalidate() already does the job. > } > if (sc->flags & FWRITE) { > wakeup(>outbuf.blocking); > - mtx_enter(_lock); > - selwakeup(>outbuf.sel); > - mtx_leave(_lock); > + knote(>outbuf.klist, 0); > } > sc->hw_if->close(sc->hw_hdl); > sc->flags = 0; > } > > - klist_invalidate(>inbuf.sel.si_note); > - klist_invalidate(>outbuf.sel.si_note); > + klist_invalidate(>inbuf.klist); > + klist_invalidate(>outbuf.klist); > Could you add two klist_free() calls here? They are simple asserts, but they'd make the attach/detach functions more midi(4) would look more like audio(4).
Re: Please test; midi(4): make midi{read,write}_filtops mp safe
On Tue, Sep 26, 2023 at 10:37:29AM +0200, Alexandre Ratchov wrote: > On Sun, Sep 24, 2023 at 11:03:54PM +0300, Vitaliy Makkoveev wrote: > > Please test this diff, I have no midi(4) devices. > > > > midi(4) already uses `audio_lock' mutex(9) for filterops, but they are > > still kernel locked. Wipe out old selwakeup API and make them MP safe. > > knote_locked(9) will not grab kernel lock, so call it directly from > > interrupt handlers instead of scheduling software interrupts. > > > > Works for me (tested with a usb device), pulling the usb cable > properly wakes up any process waiting for input/output. > > ok ratchov@ with the two changes below > Thanks for klist_invalidate() explanation. Updated diff below, will commit it today later. Index: sys/dev/midi.c === RCS file: /cvs/src/sys/dev/midi.c,v retrieving revision 1.55 diff -u -p -r1.55 midi.c --- sys/dev/midi.c 2 Jul 2022 08:50:41 - 1.55 +++ sys/dev/midi.c 26 Sep 2023 10:37:44 - @@ -31,7 +31,6 @@ #include #include -#define IPL_SOFTMIDI IPL_SOFTNET #define DEVNAME(sc)((sc)->dev.dv_xname) intmidiopen(dev_t, int, int, struct proc *); @@ -65,41 +64,38 @@ struct cfdriver midi_cd = { void filt_midiwdetach(struct knote *); int filt_midiwrite(struct knote *, long); +int filt_midimodify(struct kevent *, struct knote *); +int filt_midiprocess(struct knote *, struct kevent *); const struct filterops midiwrite_filtops = { - .f_flags= FILTEROP_ISFD, + .f_flags= FILTEROP_ISFD | FILTEROP_MPSAFE, .f_attach = NULL, .f_detach = filt_midiwdetach, .f_event= filt_midiwrite, + .f_modify = filt_midimodify, + .f_process = filt_midiprocess, }; void filt_midirdetach(struct knote *); int filt_midiread(struct knote *, long); const struct filterops midiread_filtops = { - .f_flags= FILTEROP_ISFD, + .f_flags= FILTEROP_ISFD | FILTEROP_MPSAFE, .f_attach = NULL, .f_detach = filt_midirdetach, .f_event= filt_midiread, + .f_modify = filt_midimodify, + .f_process = filt_midiprocess, }; void -midi_buf_wakeup(void *addr) +midi_buf_wakeup(struct midi_buffer *buf) { - struct midi_buffer *buf = addr; - if (buf->blocking) { wakeup(>blocking); buf->blocking = 0; } - /* -* As long as selwakeup() grabs the KERNEL_LOCK() make sure it is -* already held here to avoid lock ordering problems with `audio_lock' -*/ - KERNEL_ASSERT_LOCKED(); - mtx_enter(_lock); - selwakeup(>sel); - mtx_leave(_lock); + knote_locked(>klist, 0); } void @@ -117,13 +113,7 @@ midi_iintr(void *addr, int data) MIDIBUF_WRITE(mb, data); - /* -* As long as selwakeup() needs to be protected by the -* KERNEL_LOCK() we have to delay the wakeup to another -* context to keep the interrupt context KERNEL_LOCK() -* free. -*/ - softintr_schedule(sc->inbuf.softintr); + midi_buf_wakeup(mb); } int @@ -226,14 +216,7 @@ void midi_out_stop(struct midi_softc *sc) { sc->isbusy = 0; - - /* -* As long as selwakeup() needs to be protected by the -* KERNEL_LOCK() we have to delay the wakeup to another -* context to keep the interrupt context KERNEL_LOCK() -* free. -*/ - softintr_schedule(sc->outbuf.softintr); + midi_buf_wakeup(>outbuf); } void @@ -342,11 +325,11 @@ midikqfilter(dev_t dev, struct knote *kn error = 0; switch (kn->kn_filter) { case EVFILT_READ: - klist = >inbuf.sel.si_note; + klist = >inbuf.klist; kn->kn_fop = _filtops; break; case EVFILT_WRITE: - klist = >outbuf.sel.si_note; + klist = >outbuf.klist; kn->kn_fop = _filtops; break; default: @@ -355,9 +338,7 @@ midikqfilter(dev_t dev, struct knote *kn } kn->kn_hook = (void *)sc; - mtx_enter(_lock); - klist_insert_locked(klist, kn); - mtx_leave(_lock); + klist_insert(klist, kn); done: device_unref(>dev); return error; @@ -368,24 +349,15 @@ filt_midirdetach(struct knote *kn) { struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; - mtx_enter(_lock); - klist_remove_locked(>inbuf.sel.si_note, kn); - mtx_leave(_lock); + klist_remove(>inbuf.klist, kn); } int filt_midiread(struct knote *kn, long hint) { struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; - int retval; - - if ((hint & NOTE_SUBMIT) == 0) - mtx_enter(_lock); - retval = !MIDIBUF_ISEMPTY(>inbuf); - if ((hint & NOTE_SUBMIT) == 0) -
Re: Please test; midi(4): make midi{read,write}_filtops mp safe
On Mon, Sep 25, 2023 at 04:58:56PM +0300, Vitaliy Makkoveev wrote: > On Mon, Sep 25, 2023 at 05:39:34AM +, Visa Hankala wrote: > > On Sun, Sep 24, 2023 at 11:03:54PM +0300, Vitaliy Makkoveev wrote: > > > Please test this diff, I have no midi(4) devices. > > > > > > midi(4) already uses `audio_lock' mutex(9) for filterops, but they are > > > still kernel locked. Wipe out old selwakeup API and make them MP safe. > > > knote_locked(9) will not grab kernel lock, so call it directly from > > > interrupt handlers instead of scheduling software interrupts. > > > > https://marc.info/?l=openbsd-tech=167604232828221 has minor takeaways > > if you pay attention. > > > > The only significant difference is mididetach() where you did not replaced > selwakeup() with knote(). Did I missed something? > > @@ -577,30 +562,20 @@ mididetach(struct device *self, int flag >* in read/write/ioctl, which return EIO. >*/ > if (sc->flags) { > - KERNEL_ASSERT_LOCKED(); > - if (sc->flags & FREAD) { > + if (sc->flags & FREAD) > wakeup(>inbuf.blocking); > - mtx_enter(_lock); > - selwakeup(>inbuf.sel); > - mtx_leave(_lock); > - } IIRC, klist_invalidate() wakes up the process, so the selwakeup() section should be removed. Does this sound correct? This is how audio(4) detaches, BTW.
Re: Please test; midi(4): make midi{read,write}_filtops mp safe
On Mon, Sep 25, 2023 at 05:39:34AM +, Visa Hankala wrote: > On Sun, Sep 24, 2023 at 11:03:54PM +0300, Vitaliy Makkoveev wrote: > > Please test this diff, I have no midi(4) devices. > > > > midi(4) already uses `audio_lock' mutex(9) for filterops, but they are > > still kernel locked. Wipe out old selwakeup API and make them MP safe. > > knote_locked(9) will not grab kernel lock, so call it directly from > > interrupt handlers instead of scheduling software interrupts. > > https://marc.info/?l=openbsd-tech=167604232828221 has minor takeaways > if you pay attention. > The only significant difference is mididetach() where you did not replaced selwakeup() with knote(). Did I missed something? @@ -577,30 +562,20 @@ mididetach(struct device *self, int flag * in read/write/ioctl, which return EIO. */ if (sc->flags) { - KERNEL_ASSERT_LOCKED(); - if (sc->flags & FREAD) { + if (sc->flags & FREAD) wakeup(>inbuf.blocking); - mtx_enter(_lock); - selwakeup(>inbuf.sel); - mtx_leave(_lock); - } > > Index: sys/dev/midi.c > > === > > RCS file: /cvs/src/sys/dev/midi.c,v > > retrieving revision 1.55 > > diff -u -p -r1.55 midi.c > > --- sys/dev/midi.c 2 Jul 2022 08:50:41 - 1.55 > > +++ sys/dev/midi.c 24 Sep 2023 19:57:56 - > > @@ -31,7 +31,6 @@ > > #include > > #include > > > > -#define IPL_SOFTMIDI IPL_SOFTNET > > #define DEVNAME(sc)((sc)->dev.dv_xname) > > > > intmidiopen(dev_t, int, int, struct proc *); > > @@ -65,41 +64,38 @@ struct cfdriver midi_cd = { > > > > void filt_midiwdetach(struct knote *); > > int filt_midiwrite(struct knote *, long); > > +int filt_midimodify(struct kevent *, struct knote *); > > +int filt_midiprocess(struct knote *, struct kevent *); > > > > const struct filterops midiwrite_filtops = { > > - .f_flags= FILTEROP_ISFD, > > + .f_flags= FILTEROP_ISFD | FILTEROP_MPSAFE, > > .f_attach = NULL, > > .f_detach = filt_midiwdetach, > > .f_event= filt_midiwrite, > > + .f_modify = filt_midimodify, > > + .f_process = filt_midiprocess, > > }; > > > > void filt_midirdetach(struct knote *); > > int filt_midiread(struct knote *, long); > > > > const struct filterops midiread_filtops = { > > - .f_flags= FILTEROP_ISFD, > > + .f_flags= FILTEROP_ISFD | FILTEROP_MPSAFE, > > .f_attach = NULL, > > .f_detach = filt_midirdetach, > > .f_event= filt_midiread, > > + .f_modify = filt_midimodify, > > + .f_process = filt_midiprocess, > > }; > > > > void > > -midi_buf_wakeup(void *addr) > > +midi_buf_wakeup(struct midi_buffer *buf) > > { > > - struct midi_buffer *buf = addr; > > - > > if (buf->blocking) { > > wakeup(>blocking); > > buf->blocking = 0; > > } > > - /* > > -* As long as selwakeup() grabs the KERNEL_LOCK() make sure it is > > -* already held here to avoid lock ordering problems with `audio_lock' > > -*/ > > - KERNEL_ASSERT_LOCKED(); > > - mtx_enter(_lock); > > - selwakeup(>sel); > > - mtx_leave(_lock); > > + knote_locked(>klist, 0); > > } > > > > void > > @@ -117,13 +113,7 @@ midi_iintr(void *addr, int data) > > > > MIDIBUF_WRITE(mb, data); > > > > - /* > > -* As long as selwakeup() needs to be protected by the > > -* KERNEL_LOCK() we have to delay the wakeup to another > > -* context to keep the interrupt context KERNEL_LOCK() > > -* free. > > -*/ > > - softintr_schedule(sc->inbuf.softintr); > > + midi_buf_wakeup(mb); > > } > > > > int > > @@ -226,14 +216,7 @@ void > > midi_out_stop(struct midi_softc *sc) > > { > > sc->isbusy = 0; > > - > > - /* > > -* As long as selwakeup() needs to be protected by the > > -* KERNEL_LOCK() we have to delay the wakeup to another > > -* context to keep the interrupt context KERNEL_LOCK() > > -* free. > > -*/ > > - softintr_schedule(sc->outbuf.softintr); > > + midi_buf_wakeup(>outbuf); > > } > > > > void > > @@ -342,11 +325,11 @@ midikqfilter(dev_t dev, struct knote *kn > > error = 0; > > switch (kn->kn_filter) { > > case EVFILT_READ: > > - klist = >inbuf.sel.si_note; > > + klist = >inbuf.klist; > > kn->kn_fop = _filtops; > > break; > > case EVFILT_WRITE: > > - klist = >outbuf.sel.si_note; > > + klist = >outbuf.klist; > > kn->kn_fop = _filtops; > > break; > > default: > > @@ -355,9 +338,7 @@ midikqfilter(dev_t dev, struct knote *kn > > } > > kn->kn_hook = (void *)sc; > > > > - mtx_enter(_lock); > > - klist_insert_locked(klist, kn); > > - mtx_leave(_lock); > > +
Re: Please test; midi(4): make midi{read,write}_filtops mp safe
On Sun, Sep 24, 2023 at 11:03:54PM +0300, Vitaliy Makkoveev wrote: > Please test this diff, I have no midi(4) devices. > > midi(4) already uses `audio_lock' mutex(9) for filterops, but they are > still kernel locked. Wipe out old selwakeup API and make them MP safe. > knote_locked(9) will not grab kernel lock, so call it directly from > interrupt handlers instead of scheduling software interrupts. https://marc.info/?l=openbsd-tech=167604232828221 has minor takeaways if you pay attention. > Index: sys/dev/midi.c > === > RCS file: /cvs/src/sys/dev/midi.c,v > retrieving revision 1.55 > diff -u -p -r1.55 midi.c > --- sys/dev/midi.c2 Jul 2022 08:50:41 - 1.55 > +++ sys/dev/midi.c24 Sep 2023 19:57:56 - > @@ -31,7 +31,6 @@ > #include > #include > > -#define IPL_SOFTMIDI IPL_SOFTNET > #define DEVNAME(sc) ((sc)->dev.dv_xname) > > int midiopen(dev_t, int, int, struct proc *); > @@ -65,41 +64,38 @@ struct cfdriver midi_cd = { > > void filt_midiwdetach(struct knote *); > int filt_midiwrite(struct knote *, long); > +int filt_midimodify(struct kevent *, struct knote *); > +int filt_midiprocess(struct knote *, struct kevent *); > > const struct filterops midiwrite_filtops = { > - .f_flags= FILTEROP_ISFD, > + .f_flags= FILTEROP_ISFD | FILTEROP_MPSAFE, > .f_attach = NULL, > .f_detach = filt_midiwdetach, > .f_event= filt_midiwrite, > + .f_modify = filt_midimodify, > + .f_process = filt_midiprocess, > }; > > void filt_midirdetach(struct knote *); > int filt_midiread(struct knote *, long); > > const struct filterops midiread_filtops = { > - .f_flags= FILTEROP_ISFD, > + .f_flags= FILTEROP_ISFD | FILTEROP_MPSAFE, > .f_attach = NULL, > .f_detach = filt_midirdetach, > .f_event= filt_midiread, > + .f_modify = filt_midimodify, > + .f_process = filt_midiprocess, > }; > > void > -midi_buf_wakeup(void *addr) > +midi_buf_wakeup(struct midi_buffer *buf) > { > - struct midi_buffer *buf = addr; > - > if (buf->blocking) { > wakeup(>blocking); > buf->blocking = 0; > } > - /* > - * As long as selwakeup() grabs the KERNEL_LOCK() make sure it is > - * already held here to avoid lock ordering problems with `audio_lock' > - */ > - KERNEL_ASSERT_LOCKED(); > - mtx_enter(_lock); > - selwakeup(>sel); > - mtx_leave(_lock); > + knote_locked(>klist, 0); > } > > void > @@ -117,13 +113,7 @@ midi_iintr(void *addr, int data) > > MIDIBUF_WRITE(mb, data); > > - /* > - * As long as selwakeup() needs to be protected by the > - * KERNEL_LOCK() we have to delay the wakeup to another > - * context to keep the interrupt context KERNEL_LOCK() > - * free. > - */ > - softintr_schedule(sc->inbuf.softintr); > + midi_buf_wakeup(mb); > } > > int > @@ -226,14 +216,7 @@ void > midi_out_stop(struct midi_softc *sc) > { > sc->isbusy = 0; > - > - /* > - * As long as selwakeup() needs to be protected by the > - * KERNEL_LOCK() we have to delay the wakeup to another > - * context to keep the interrupt context KERNEL_LOCK() > - * free. > - */ > - softintr_schedule(sc->outbuf.softintr); > + midi_buf_wakeup(>outbuf); > } > > void > @@ -342,11 +325,11 @@ midikqfilter(dev_t dev, struct knote *kn > error = 0; > switch (kn->kn_filter) { > case EVFILT_READ: > - klist = >inbuf.sel.si_note; > + klist = >inbuf.klist; > kn->kn_fop = _filtops; > break; > case EVFILT_WRITE: > - klist = >outbuf.sel.si_note; > + klist = >outbuf.klist; > kn->kn_fop = _filtops; > break; > default: > @@ -355,9 +338,7 @@ midikqfilter(dev_t dev, struct knote *kn > } > kn->kn_hook = (void *)sc; > > - mtx_enter(_lock); > - klist_insert_locked(klist, kn); > - mtx_leave(_lock); > + klist_insert(klist, kn); > done: > device_unref(>dev); > return error; > @@ -368,24 +349,15 @@ filt_midirdetach(struct knote *kn) > { > struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; > > - mtx_enter(_lock); > - klist_remove_locked(>inbuf.sel.si_note, kn); > - mtx_leave(_lock); > + klist_remove(>inbuf.klist, kn); > } > > int > filt_midiread(struct knote *kn, long hint) > { > struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; > - int retval; > - > - if ((hint & NOTE_SUBMIT) == 0) > - mtx_enter(_lock); > - retval = !MIDIBUF_ISEMPTY(>inbuf); > - if ((hint & NOTE_SUBMIT) == 0) > - mtx_leave(_lock); > > - return (retval); > + return (!MIDIBUF_ISEMPTY(>inbuf)); > } > > void > @@ -393,24
Please test; midi(4): make midi{read,write}_filtops mp safe
Please test this diff, I have no midi(4) devices. midi(4) already uses `audio_lock' mutex(9) for filterops, but they are still kernel locked. Wipe out old selwakeup API and make them MP safe. knote_locked(9) will not grab kernel lock, so call it directly from interrupt handlers instead of scheduling software interrupts. Index: sys/dev/midi.c === RCS file: /cvs/src/sys/dev/midi.c,v retrieving revision 1.55 diff -u -p -r1.55 midi.c --- sys/dev/midi.c 2 Jul 2022 08:50:41 - 1.55 +++ sys/dev/midi.c 24 Sep 2023 19:57:56 - @@ -31,7 +31,6 @@ #include #include -#define IPL_SOFTMIDI IPL_SOFTNET #define DEVNAME(sc)((sc)->dev.dv_xname) intmidiopen(dev_t, int, int, struct proc *); @@ -65,41 +64,38 @@ struct cfdriver midi_cd = { void filt_midiwdetach(struct knote *); int filt_midiwrite(struct knote *, long); +int filt_midimodify(struct kevent *, struct knote *); +int filt_midiprocess(struct knote *, struct kevent *); const struct filterops midiwrite_filtops = { - .f_flags= FILTEROP_ISFD, + .f_flags= FILTEROP_ISFD | FILTEROP_MPSAFE, .f_attach = NULL, .f_detach = filt_midiwdetach, .f_event= filt_midiwrite, + .f_modify = filt_midimodify, + .f_process = filt_midiprocess, }; void filt_midirdetach(struct knote *); int filt_midiread(struct knote *, long); const struct filterops midiread_filtops = { - .f_flags= FILTEROP_ISFD, + .f_flags= FILTEROP_ISFD | FILTEROP_MPSAFE, .f_attach = NULL, .f_detach = filt_midirdetach, .f_event= filt_midiread, + .f_modify = filt_midimodify, + .f_process = filt_midiprocess, }; void -midi_buf_wakeup(void *addr) +midi_buf_wakeup(struct midi_buffer *buf) { - struct midi_buffer *buf = addr; - if (buf->blocking) { wakeup(>blocking); buf->blocking = 0; } - /* -* As long as selwakeup() grabs the KERNEL_LOCK() make sure it is -* already held here to avoid lock ordering problems with `audio_lock' -*/ - KERNEL_ASSERT_LOCKED(); - mtx_enter(_lock); - selwakeup(>sel); - mtx_leave(_lock); + knote_locked(>klist, 0); } void @@ -117,13 +113,7 @@ midi_iintr(void *addr, int data) MIDIBUF_WRITE(mb, data); - /* -* As long as selwakeup() needs to be protected by the -* KERNEL_LOCK() we have to delay the wakeup to another -* context to keep the interrupt context KERNEL_LOCK() -* free. -*/ - softintr_schedule(sc->inbuf.softintr); + midi_buf_wakeup(mb); } int @@ -226,14 +216,7 @@ void midi_out_stop(struct midi_softc *sc) { sc->isbusy = 0; - - /* -* As long as selwakeup() needs to be protected by the -* KERNEL_LOCK() we have to delay the wakeup to another -* context to keep the interrupt context KERNEL_LOCK() -* free. -*/ - softintr_schedule(sc->outbuf.softintr); + midi_buf_wakeup(>outbuf); } void @@ -342,11 +325,11 @@ midikqfilter(dev_t dev, struct knote *kn error = 0; switch (kn->kn_filter) { case EVFILT_READ: - klist = >inbuf.sel.si_note; + klist = >inbuf.klist; kn->kn_fop = _filtops; break; case EVFILT_WRITE: - klist = >outbuf.sel.si_note; + klist = >outbuf.klist; kn->kn_fop = _filtops; break; default: @@ -355,9 +338,7 @@ midikqfilter(dev_t dev, struct knote *kn } kn->kn_hook = (void *)sc; - mtx_enter(_lock); - klist_insert_locked(klist, kn); - mtx_leave(_lock); + klist_insert(klist, kn); done: device_unref(>dev); return error; @@ -368,24 +349,15 @@ filt_midirdetach(struct knote *kn) { struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; - mtx_enter(_lock); - klist_remove_locked(>inbuf.sel.si_note, kn); - mtx_leave(_lock); + klist_remove(>inbuf.klist, kn); } int filt_midiread(struct knote *kn, long hint) { struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; - int retval; - - if ((hint & NOTE_SUBMIT) == 0) - mtx_enter(_lock); - retval = !MIDIBUF_ISEMPTY(>inbuf); - if ((hint & NOTE_SUBMIT) == 0) - mtx_leave(_lock); - return (retval); + return (!MIDIBUF_ISEMPTY(>inbuf)); } void @@ -393,24 +365,39 @@ filt_midiwdetach(struct knote *kn) { struct midi_softc *sc = (struct midi_softc *)kn->kn_hook; - mtx_enter(_lock); - klist_remove_locked(>outbuf.sel.si_note, kn); - mtx_leave(_lock); + klist_remove(>outbuf.klist, kn); } int filt_midiwrite(struct knote *kn,