diff --git a/sys/fs/cuse/cuse.c b/sys/fs/cuse/cuse.c index 578bf3eda74f..2fc38dda3da6 100644 --- a/sys/fs/cuse/cuse.c +++ b/sys/fs/cuse/cuse.c @@ -699,12 +699,38 @@ cuse_server_unref(struct cuse_server *pcs) free(pcs, M_CUSE); } +static int +cuse_server_do_close(struct cuse_server *pcs) +{ + int retval; + + cuse_lock(); + cuse_server_is_closing(pcs); + /* final client wakeup, if any */ + cuse_server_wakeup_all_client_locked(pcs); + + knlist_clear(&pcs->selinfo.si_note, 1); + + retval = pcs->refs; + cuse_unlock(); + + return (retval); +} + static void cuse_server_free(void *arg) { struct cuse_server *pcs = arg; - /* drop refcount */ + /* + * The final server unref should be done by the server thread + * to prevent deadlock in the client cdevpriv destructor, + * which cannot destroy itself. + */ + while (cuse_server_do_close(pcs) != 1) + pause("W", hz); + + /* drop final refcount */ cuse_server_unref(pcs); } @@ -746,21 +772,10 @@ static int cuse_server_close(struct cdev *dev, int fflag, int devtype, struct thread *td) { struct cuse_server *pcs; - int error; - error = cuse_server_get(&pcs); - if (error != 0) - goto done; + if (cuse_server_get(&pcs) == 0) + cuse_server_do_close(pcs); - cuse_lock(); - cuse_server_is_closing(pcs); - /* final client wakeup, if any */ - cuse_server_wakeup_all_client_locked(pcs); - - knlist_clear(&pcs->selinfo.si_note, 1); - cuse_unlock(); - -done: return (0); }