--- src/nvidia-modeset/nvidia-modeset-freebsd.c.orig 2017-09-20 03:46:25.000000000 +0300 +++ src/nvidia-modeset/nvidia-modeset-freebsd.c 2017-11-19 23:39:31.080801546 +0200 @@ -250,7 +250,7 @@ struct nvkms_ref_ptr { struct nvkms_ref_ptr* NVKMS_API_CALL nvkms_alloc_ref_ptr(void *ptr) { - struct nvkms_ref_ptr *ref_ptr = nvkms_alloc(sizeof(*ref_ptr), NV_FALSE); + struct nvkms_ref_ptr *ref_ptr = nvkms_alloc(sizeof(*ref_ptr), NV_TRUE); if (ref_ptr) { mtx_init(&ref_ptr->lock, "nvkms-ref-ptr-lock", NULL, MTX_SPIN); // The ref_ptr owner counts as a reference on the ref_ptr itself. @@ -334,7 +334,7 @@ struct nvkms_timer_t { * Global list with pending timers, any change requires acquiring lock */ static struct { - struct sx lock; + struct mtx lock; LIST_HEAD(nvkms_timers_head, nvkms_timer_t) list; } nvkms_timers; @@ -347,10 +347,11 @@ static void nvkms_taskqueue_callback(void *arg, int pe * We can delete this timer from pending timers list - it's being * processed now. */ - sx_xlock(&nvkms_timers.lock); + mtx_lock(&nvkms_timers.lock); LIST_REMOVE(timer, timers_list); - sx_xunlock(&nvkms_timers.lock); + mtx_unlock(&nvkms_timers.lock); +#if 0 /* * After taskqueue_callback we want to be sure that callout_callback * for this timer also have finished. It's important during module @@ -360,6 +361,7 @@ static void nvkms_taskqueue_callback(void *arg, int pe if (timer->callout_created) { callout_drain(&timer->callout); } +#endif sx_xlock(&nvkms_lock); @@ -417,7 +419,7 @@ nvkms_init_timer(struct nvkms_timer_t *timer, nvkms_ti * run in parallel with this, it could race against nvkms_init_timer() * and free the timer before its initialization is complete. */ - sx_xlock(&nvkms_timers.lock); + mtx_lock(&nvkms_timers.lock); LIST_INSERT_HEAD(&nvkms_timers.list, timer, timers_list); if (usec == 0) { @@ -431,7 +433,7 @@ nvkms_init_timer(struct nvkms_timer_t *timer, nvkms_ti NVKMS_USECS_TO_TICKS(usec), nvkms_callout_callback, (void *) timer); } - sx_xunlock(&nvkms_timers.lock); + mtx_unlock(&nvkms_timers.lock); } nvkms_timer_handle_t* @@ -967,7 +969,7 @@ nvidia_modeset_loader(struct module *m, int what, void nvkms_module.is_unloading = NV_FALSE; LIST_INIT(&nvkms_timers.list); - sx_init(&nvkms_timers.lock, "nvidia-modeset timer lock"); + mtx_init(&nvkms_timers.lock, "nvidia-modeset timer lock", NULL, MTX_DEF); nvkms_dev = make_dev(&nvkms_cdevsw, NVKMS_CDEV_MINOR, @@ -976,7 +978,7 @@ nvidia_modeset_loader(struct module *m, int what, void if (nvkms_dev == NULL) { sx_destroy(&nvkms_module.lock); - sx_destroy(&nvkms_timers.lock); + mtx_destroy(&nvkms_timers.lock); sx_destroy(&nvkms_lock); nvkms_free_rm(); @@ -1039,7 +1041,7 @@ nvidia_modeset_loader(struct module *m, int what, void * nvkms_taskqueue_callback() doesn't get called after the * module is unloaded. */ - sx_xlock(&nvkms_timers.lock); + mtx_lock(&nvkms_timers.lock); LIST_FOREACH_SAFE(timer, &nvkms_timers.list, timers_list, tmp) { if (timer->callout_created) { @@ -1050,7 +1052,7 @@ nvidia_modeset_loader(struct module *m, int what, void * completion, and we wait for taskqueue completion with * taskqueue_run below. */ - if (callout_drain(&timer->callout) > 0) { + if (callout_stop(&timer->callout) > 0) { /* We've deactivated timer so we need to clean after it */ LIST_REMOVE(timer, timers_list); if (timer->isRefPtr) { @@ -1061,15 +1063,29 @@ nvidia_modeset_loader(struct module *m, int what, void } } - sx_xunlock(&nvkms_timers.lock); +restart: + LIST_FOREACH_SAFE(timer, &nvkms_timers.list, timers_list, tmp) { + if (timer->callout_created) { + /* Can not hold the lock that's used in the callback while draing. */ + mtx_unlock(&nvkms_timers.lock); + callout_drain(&timer->callout); + /* Run a task that may have been enqueued by the drained callout. */ + taskqueue_run(taskqueue_nvkms); + mtx_lock(&nvkms_timers.lock); + /* Restart iteration afer dropping the lock. */ + goto restart; + } + } + mtx_unlock(&nvkms_timers.lock); + taskqueue_run(taskqueue_nvkms); destroy_dev(nvkms_dev); nvkms_dev = NULL; sx_destroy(&nvkms_module.lock); - sx_destroy(&nvkms_timers.lock); + mtx_destroy(&nvkms_timers.lock); sx_destroy(&nvkms_lock); nvkms_free_rm();