diff -ru src/nv-freebsd.h src.1729565/nv-freebsd.h --- src/nv-freebsd.h 2006-12-19 13:50:18.000000000 -0800 +++ src.1729565/nv-freebsd.h 2007-01-10 10:42:59.978631750 -0800 @@ -36,6 +36,9 @@ #include #include #include +#if __FreeBSD_version >= 700000 +#include +#endif #include #include @@ -180,6 +183,9 @@ struct nvidia_drv2 { STAILQ_HEAD(event_queue, nvidia_event) event_queue; struct selinfo rsel; +#if __FreeBSD_version >= 700000 + struct task dev_task; +#endif } nvidia_drv2_t; typedef @@ -213,8 +219,8 @@ int refcnt; - struct mtx mtx_rm; - struct sx sx_api; + struct mtx rm_mtx; + struct sx api_sx; } nvidia_softc_t; @@ -222,6 +228,21 @@ #define CDEV_MAJOR 180 #define CDEV_CTL_MINOR 255 +extern struct sx kld_refcnt_sx; +extern U032 kld_refcnt; + +#define NV_INC_KLD_REFCNT() \ +do { \ + sx_xlock(&kld_refcnt_sx); kld_refcnt++; \ + sx_xunlock(&kld_refcnt_sx); \ +} while (0) + +#define NV_DEC_KLD_REFCNT() \ +do { \ + sx_xlock(&kld_refcnt_sx); kld_refcnt--; \ + sx_xunlock(&kld_refcnt_sx); \ +} while (0) + extern struct clonedevs *nvidia_ctl_clones; extern struct clonedevs *nvidia_dev_clones; @@ -279,13 +300,14 @@ int nvidia_pci_teardown_intr (device_t dev); U008 nvidia_pci_find_capability (device_t dev, U008); -#define NV_PCI_CHECK_CONFIG_SPACE(_nv, do_the_bars) { \ +#define NV_PCI_CHECK_CONFIG_SPACE(_nv, do_the_bars) \ +do { \ struct nvidia_softc *__sc; \ if (((_nv)->flags & NV_FLAG_CONTROL) == 0) { \ __sc = (_nv)->os_state; \ nvidia_pci_check_config_space(__sc->dev, do_the_bars); \ } \ - } +} while (0) /* nvidia_subr.c */ int nvidia_attach (device_t); @@ -360,5 +382,6 @@ int nvidia_close_ctl (struct cdev *, d_thread_t *); int nvidia_close_dev (struct nvidia_softc *, struct cdev *, d_thread_t *); int nvidia_mmap_dev (struct nvidia_softc *, vm_offset_t, vm_offset_t *); +void nvidia_destroy_dev (void *, int); #endif /* __NV_FREEBSD_H__ */ diff -ru src/nvidia_ctl.c src.1729565/nvidia_ctl.c --- src/nvidia_ctl.c 2006-12-19 13:50:18.000000000 -0800 +++ src.1729565/nvidia_ctl.c 2007-01-10 10:42:59.978631750 -0800 @@ -50,6 +50,8 @@ if (strcmp(name, "nvidiactl") != 0) return; + mtx_lock(&Giant); + /* * XXX clone_create() doesn't handle automatic clone * number allocation correctly at this point, it @@ -58,21 +60,37 @@ * allocate numbers that are still in use. Iterate over * the possible clone numbers until the first unused * one is found. + * + * XXX also, we may not receive an open() callback for + * each clone created here, e.g. stat() will prompt + * VFS lookups without subsequent open attempts. Try to + * reclaim clones orphaned this way. */ do { i = clone_create(&nvidia_ctl_clones, &nvidia_ctl_cdevsw, &clone, dev, 0); - if (i == 0) clone++; + if (i == 0) { + if ((*dev)->si_flags & SI_CHEAPCLONE) { + dev_ref(*dev); + mtx_unlock(&Giant); + return; + } + clone++; + } } while ((clone <= CLONE_UNITMASK) && (i == 0)); if ((i != 0) && (clone <= CLONE_UNITMASK)) { *dev = make_dev(&nvidia_ctl_cdevsw, unit2minor(clone), UID_ROOT, GID_WHEEL, 0666, "nvidiactl.%u", clone); if (*dev != NULL) { + dev_ref(*dev); (*dev)->si_flags |= SI_CHEAPCLONE; (*dev)->si_drv1 = (void *)&nvidia_ctl_sc; + NV_INC_KLD_REFCNT(); } } + + mtx_unlock(&Giant); } int nvidia_ctl_open( @@ -84,20 +102,39 @@ { int status; nv_state_t *nv = &nvidia_ctl_state; + struct nvidia_drv2 *drv2; + + NV_INC_KLD_REFCNT(); if (!dev->si_drv2) { - /* only allow one open() of this file */ - dev->si_drv2 = dev->si_drv1; - } else + drv2 = malloc(sizeof(struct nvidia_drv2), M_NVIDIA, M_WAITOK | M_ZERO); + if (drv2 == NULL) { + NV_DEC_KLD_REFCNT(); + return ENOMEM; + } + STAILQ_INIT(&drv2->event_queue); +#if __FreeBSD_version >= 700000 + TASK_INIT(&drv2->dev_task, M_NOWAIT, nvidia_destroy_dev, dev); +#endif + dev->si_flags &= ~SI_CHEAPCLONE; + dev->si_drv2 = drv2; + } else { + NV_DEC_KLD_REFCNT(); return EBUSY; + } nv_lock_api(nv); status = nvidia_open_ctl(); nv_unlock_api(nv); - if (status == 0) { - /* XXX Fix me? (clear of SI_CHEAPCLONE) */ - dev->si_flags &= ~SI_CHEAPCLONE; + if (status != 0) { + NV_DEC_KLD_REFCNT(); +#if __FreeBSD_version >= 700000 + drv2 = dev->si_drv2; + taskqueue_enqueue(taskqueue_swi_giant, &drv2->dev_task); +#else + nvidia_destroy_dev(dev, 0); +#endif } return status; @@ -117,9 +154,16 @@ status = nvidia_close_ctl(dev, td); nv_unlock_api(nv); + NV_DEC_KLD_REFCNT(); + if (status == 0) { - /* XXX Fix me? (call to destroy_dev()) */ - destroy_dev(dev); +#if __FreeBSD_version >= 700000 + struct nvidia_drv2 *drv2; + drv2 = dev->si_drv2; + taskqueue_enqueue(taskqueue_swi_giant, &drv2->dev_task); +#else + nvidia_destroy_dev(dev, 0); +#endif } return status; @@ -236,8 +280,8 @@ nvidia_ctl_state.os_state = sc; sc->nv_state = (void *)&nvidia_ctl_state; - mtx_init(&sc->mtx_rm, "ctl.mtx_rm", NULL, MTX_SPIN | MTX_RECURSE); - sx_init(&sc->sx_api, "ctl.sx_api"); + mtx_init(&sc->rm_mtx, "ctl.rm_mtx", NULL, MTX_SPIN | MTX_RECURSE); + sx_init(&sc->api_sx, "ctl.api_sx"); } nvidia_count++; @@ -256,8 +300,8 @@ * Like nvidia_ctl_attach(), nvidia_ctl_detach() will also be * called more than once with multiple devices. */ - mtx_destroy(&sc->mtx_rm); - sx_destroy(&sc->sx_api); + mtx_destroy(&sc->rm_mtx); + sx_destroy(&sc->api_sx); } return 0; diff -ru src/nvidia_dev.c src.1729565/nvidia_dev.c --- src/nvidia_dev.c 2006-12-19 13:50:18.000000000 -0800 +++ src.1729565/nvidia_dev.c 2007-01-10 10:42:59.978631750 -0800 @@ -57,6 +57,8 @@ if (sc == NULL) return; + mtx_lock(&Giant); + /* * XXX clone_create() doesn't handle automatic clone * number allocation correctly at this point, it @@ -65,21 +67,37 @@ * allocate numbers that are still in use. Iterate over * the possible clone numbers until the first unused * one is found. + * + * XXX also, we may not receive an open() callback for + * each clone created here, e.g. stat() will prompt + * VFS lookups without subsequent open attempts. Try to + * reclaim clones orphaned this way. */ do { i = clone_create(&nvidia_dev_clones, &nvidia_dev_cdevsw, &clone, dev, 0); - if (i == 0) clone++; + if (i == 0) { + if ((*dev)->si_flags & SI_CHEAPCLONE) { + dev_ref(*dev); + mtx_unlock(&Giant); + return; + } + clone++; + } } while ((clone <= CLONE_UNITMASK) && (i == 0)); if ((i != 0) && (clone <= CLONE_UNITMASK)) { *dev = make_dev(&nvidia_dev_cdevsw, unit2minor(clone), UID_ROOT, GID_WHEEL, 0666, "nvidia%u.%u", unit, clone); if (*dev != NULL) { + dev_ref(*dev); (*dev)->si_drv1 = (void *)sc; (*dev)->si_flags |= SI_CHEAPCLONE; + NV_INC_KLD_REFCNT(); } } + + mtx_unlock(&Giant); } int nvidia_dev_open( @@ -95,24 +113,38 @@ struct nvidia_drv2 *drv2; + NV_INC_KLD_REFCNT(); + if (!dev->si_drv2) { drv2 = malloc(sizeof(struct nvidia_drv2), M_NVIDIA, M_WAITOK | M_ZERO); - if (drv2 == NULL) + if (drv2 == NULL) { + NV_DEC_KLD_REFCNT(); return ENOMEM; + } STAILQ_INIT(&drv2->event_queue); +#if __FreeBSD_version >= 700000 + TASK_INIT(&drv2->dev_task, M_NOWAIT, nvidia_destroy_dev, dev); +#endif + dev->si_flags &= ~SI_CHEAPCLONE; dev->si_drv2 = drv2; - } else + } else { + NV_DEC_KLD_REFCNT(); return EBUSY; + } nv_lock_api(nv); status = nvidia_open_dev(sc); nv_unlock_api(nv); - if (status == 0) { - /* XXX Fix me? (clear of SI_CHEAPCLONE) */ - dev->si_flags &= ~SI_CHEAPCLONE; - } else - free(drv2, M_NVIDIA); + if (status != 0) { + NV_DEC_KLD_REFCNT(); +#if __FreeBSD_version >= 700000 + drv2 = dev->si_drv2; + taskqueue_enqueue(taskqueue_swi_giant, &drv2->dev_task); +#else + nvidia_destroy_dev(dev, 0); +#endif + } return status; } @@ -141,10 +173,15 @@ free(et, M_NVIDIA); } + NV_DEC_KLD_REFCNT(); + if (status == 0) { - free(dev->si_drv2, M_NVIDIA); - /* XXX Fix me? (call to destroy_dev()) */ - destroy_dev(dev); +#if __FreeBSD_version >= 700000 + drv2 = dev->si_drv2; + taskqueue_enqueue(taskqueue_swi_giant, &drv2->dev_task); +#else + nvidia_destroy_dev(dev, 0); +#endif } return status; diff -ru src/nvidia_pci.c src.1729565/nvidia_pci.c --- src/nvidia_pci.c 2006-12-19 13:50:18.000000000 -0800 +++ src.1729565/nvidia_pci.c 2007-01-10 10:42:59.978631750 -0800 @@ -236,8 +236,8 @@ goto fail; } - mtx_init(&sc->mtx_rm, "dev.mtx_rm", NULL, MTX_SPIN | MTX_RECURSE); - sx_init(&sc->sx_api, "dev.sx_api"); + mtx_init(&sc->rm_mtx, "dev.rm_mtx", NULL, MTX_SPIN | MTX_RECURSE); + sx_init(&sc->api_sx, "dev.api_sx"); return 0; @@ -257,12 +257,13 @@ * detach request; this event can happen even when the module * usage count is non-zero! */ - sc = device_get_softc(dev); - if (sc->refcnt != 0) /* XXX Fix me? (refcnt) */ + if (kld_refcnt != 0) /* XXX Fix me? (refcnt) */ return EBUSY; - mtx_destroy(&sc->mtx_rm); - sx_destroy(&sc->sx_api); + sc = device_get_softc(dev); + + mtx_destroy(&sc->rm_mtx); + sx_destroy(&sc->api_sx); status = nvidia_pci_teardown_intr(dev); if (status) diff -ru src/nvidia_subr.c src.1729565/nvidia_subr.c --- src/nvidia_subr.c 2006-12-19 13:50:17.000000000 -0800 +++ src.1729565/nvidia_subr.c 2007-01-10 10:42:59.978631750 -0800 @@ -20,6 +20,9 @@ devclass_t nvidia_devclass; nv_state_t nvidia_ctl_state; +struct sx kld_refcnt_sx; +U032 kld_refcnt; + static eventhandler_tag nvidia_ctl_ehtag; static eventhandler_tag nvidia_dev_ehtag; @@ -322,6 +325,17 @@ return EINVAL; } +void nvidia_destroy_dev( + void *arg, + int pending +) +{ + struct cdev *dev = arg; + + free(dev->si_drv2, M_NVIDIA); + destroy_dev(dev); + NV_DEC_KLD_REFCNT(); +} int nvidia_open_ctl(void) { @@ -437,6 +451,9 @@ * this routine, further initialization takes place at attach * time. */ + sx_init(&kld_refcnt_sx, "kld.refcnt_sx"); + kld_refcnt = 0; + clone_setup(&nvidia_ctl_clones); clone_setup(&nvidia_dev_clones); @@ -468,15 +485,15 @@ * unload request if it is. This event can occur even when the * module usage count is non-zero! */ - EVENTHANDLER_DEREGISTER(dev_clone, nvidia_dev_ehtag); - EVENTHANDLER_DEREGISTER(dev_clone, nvidia_ctl_ehtag); - nv = &nvidia_ctl_state; sc = nv->os_state; - if (sc->refcnt != 0) /* XXX Fix me? (refcnt) */ + if (kld_refcnt != 0) /* XXX Fix me? (refcnt) */ return EBUSY; + EVENTHANDLER_DEREGISTER(dev_clone, nvidia_dev_ehtag); + EVENTHANDLER_DEREGISTER(dev_clone, nvidia_ctl_ehtag); + rm_shutdown_rm(); nvidia_sysctl_exit(); @@ -485,6 +502,8 @@ clone_cleanup(&nvidia_dev_clones); clone_cleanup(&nvidia_dev_clones); + sx_destroy(&kld_refcnt_sx); + break; default: @@ -713,25 +732,25 @@ * interrupts on the current processor. */ struct nvidia_softc *sc = nv->os_state; - mtx_lock_spin(&sc->mtx_rm); + mtx_lock_spin(&sc->rm_mtx); } void nv_unlock_rm(nv_state_t *nv) { struct nvidia_softc *sc = nv->os_state; - mtx_unlock_spin(&sc->mtx_rm); + mtx_unlock_spin(&sc->rm_mtx); } void nv_lock_api(nv_state_t *nv) { struct nvidia_softc *sc = nv->os_state; - sx_xlock(&sc->sx_api); + sx_xlock(&sc->api_sx); } void nv_unlock_api(nv_state_t *nv) { struct nvidia_softc *sc = nv->os_state; - sx_xunlock(&sc->sx_api); + sx_xunlock(&sc->api_sx); }