Module Name: src Committed By: mbalmer Date: Wed Aug 31 12:07:26 UTC 2011
Modified Files: src/sys/dev/gpio: gpio.c Log Message: gpio(4) keeps track of child devices attached using the GPIOATTACH ioctl(), so remove those references and free the memory in gpio_childdetached(). Protect access to the list of child devices with a kcondvar. To generate a diff of this commit: cvs rdiff -u -r1.38 -r1.39 src/sys/dev/gpio/gpio.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/gpio/gpio.c diff -u src/sys/dev/gpio/gpio.c:1.38 src/sys/dev/gpio/gpio.c:1.39 --- src/sys/dev/gpio/gpio.c:1.38 Tue Aug 30 07:22:11 2011 +++ src/sys/dev/gpio/gpio.c Wed Aug 31 12:07:26 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: gpio.c,v 1.38 2011/08/30 07:22:11 mbalmer Exp $ */ +/* $NetBSD: gpio.c,v 1.39 2011/08/31 12:07:26 mbalmer Exp $ */ /* $OpenBSD: gpio.c,v 1.6 2006/01/14 12:33:49 grange Exp $ */ /* @@ -19,7 +19,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.38 2011/08/30 07:22:11 mbalmer Exp $"); +__KERNEL_RCSID(0, "$NetBSD: gpio.c,v 1.39 2011/08/31 12:07:26 mbalmer Exp $"); /* * General Purpose Input/Output framework. @@ -63,6 +63,8 @@ kmutex_t sc_mtx; kcondvar_t sc_ioctl; /* ioctl in progress */ int sc_ioctl_busy; /* ioctl is busy */ + kcondvar_t sc_attach; /* attach/detach in progress */ + int sc_attach_busy;/* busy in attach/detach */ LIST_HEAD(, gpio_dev) sc_devs; /* devices */ LIST_HEAD(, gpio_name) sc_names; /* named pins */ }; @@ -134,7 +136,40 @@ static void gpio_childdetached(device_t self, device_t child) { - /* gpio(4) keeps no references to its children, so do nothing. */ + struct gpio_dev *gdev; + struct gpio_softc *sc; + int error; + + /* + * gpio_childetached is serialized because it can be entered in + * different ways concurrently, e.g. via the GPIODETACH ioctl and + * drvctl(8) or modunload(8). + */ + sc = device_private(self); + error = 0; + mutex_enter(&sc->sc_mtx); + while (sc->sc_attach_busy) { + error = cv_wait_sig(&sc->sc_attach, &sc->sc_mtx); + if (error) + break; + } + if (!error) + sc->sc_attach_busy = 1; + mutex_exit(&sc->sc_mtx); + if (error) + return; + + LIST_FOREACH(gdev, &sc->sc_devs, sc_next) + if (gdev->sc_dev == child) { + LIST_REMOVE(gdev, sc_next); + kmem_free(gdev, sizeof(struct gpio_dev)); + break; + } + + mutex_enter(&sc->sc_mtx); + sc->sc_attach_busy = 0; + cv_signal(&sc->sc_attach); + mutex_exit(&sc->sc_mtx); } static int @@ -169,7 +204,7 @@ aprint_error_dev(self, "couldn't establish power handler\n"); mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_VM); cv_init(&sc->sc_ioctl, "gpioctl"); - + cv_init(&sc->sc_attach, "gpioatch"); /* * Attach all devices that can be connected to the GPIO pins * described in the kernel configuration file. @@ -478,7 +513,7 @@ cfdata_t cf; kauth_cred_t cred; int locs[GPIOCF_NLOCS]; - int pin, value, flags, npins; + int error, pin, value, flags, npins; gc = sc->sc_gc; @@ -642,6 +677,19 @@ if (!gpio_pin_can_map(sc, attach->ga_offset, attach->ga_mask)) return EBUSY; + error = 0; + mutex_enter(&sc->sc_mtx); + while (sc->sc_attach_busy) { + error = cv_wait_sig(&sc->sc_attach, &sc->sc_mtx); + if (error) + break; + } + if (!error) + sc->sc_attach_busy = 1; + mutex_exit(&sc->sc_mtx); + if (error) + return EBUSY; + ga.ga_gpio = sc; ga.ga_dvname = attach->ga_dvname; ga.ga_offset = attach->ga_offset; @@ -664,28 +712,51 @@ gdev->sc_dev = dv; LIST_INSERT_HEAD(&sc->sc_devs, gdev, sc_next); } else - return EINVAL; + error = EINVAL; } else - return EINVAL; - return 0; + error = EINVAL; + mutex_enter(&sc->sc_mtx); + sc->sc_attach_busy = 0; + cv_signal(&sc->sc_attach); + mutex_exit(&sc->sc_mtx); + return error; case GPIODETACH: if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET, NULL, NULL, NULL, NULL)) return EPERM; + mutex_enter(&sc->sc_mtx); + while (sc->sc_attach_busy) { + error = cv_wait_sig(&sc->sc_attach, &sc->sc_mtx); + if (error) + break; + } + if (!error) + sc->sc_attach_busy = 1; + mutex_exit(&sc->sc_mtx); + if (error) + return EBUSY; + attach = (struct gpio_attach *)data; LIST_FOREACH(gdev, &sc->sc_devs, sc_next) { if (strcmp(device_xname(gdev->sc_dev), attach->ga_dvname) == 0) { - if (config_detach(gdev->sc_dev, 0) == 0) { - LIST_REMOVE(gdev, sc_next); - kmem_free(gdev, - sizeof(struct gpio_dev)); + mutex_enter(&sc->sc_mtx); + sc->sc_attach_busy = 0; + cv_signal(&sc->sc_attach); + mutex_exit(&sc->sc_mtx); + + if (config_detach(gdev->sc_dev, 0) == 0) return 0; - } break; } } + if (gdev == NULL) { + mutex_enter(&sc->sc_mtx); + sc->sc_attach_busy = 0; + cv_signal(&sc->sc_attach); + mutex_exit(&sc->sc_mtx); + } return EINVAL; case GPIOSET: if (kauth_authorize_device(cred, KAUTH_DEVICE_GPIO_PINSET,