Module Name: src Committed By: pgoyette Date: Sat Jul 16 22:35:34 UTC 2016
Modified Files: src/sys/kern [pgoyette-localcount]: subr_devsw.c Log Message: Fix some locking sequences. Thanks to ristradh@ for the review! To generate a diff of this commit: cvs rdiff -u -r1.34.2.1 -r1.34.2.2 src/sys/kern/subr_devsw.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/kern/subr_devsw.c diff -u src/sys/kern/subr_devsw.c:1.34.2.1 src/sys/kern/subr_devsw.c:1.34.2.2 --- src/sys/kern/subr_devsw.c:1.34.2.1 Sat Jul 16 07:54:13 2016 +++ src/sys/kern/subr_devsw.c Sat Jul 16 22:35:34 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: subr_devsw.c,v 1.34.2.1 2016/07/16 07:54:13 pgoyette Exp $ */ +/* $NetBSD: subr_devsw.c,v 1.34.2.2 2016/07/16 22:35:34 pgoyette Exp $ */ /*- * Copyright (c) 2001, 2002, 2007, 2008 The NetBSD Foundation, Inc. @@ -69,7 +69,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: subr_devsw.c,v 1.34.2.1 2016/07/16 07:54:13 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: subr_devsw.c,v 1.34.2.2 2016/07/16 22:35:34 pgoyette Exp $"); #ifdef _KERNEL_OPT #include "opt_dtrace.h" @@ -85,8 +85,10 @@ __KERNEL_RCSID(0, "$NetBSD: subr_devsw.c #include <sys/buf.h> #include <sys/reboot.h> #include <sys/sdt.h> +#include <sys/atomic.h> #include <sys/condvar.h> #include <sys/localcount.h> +#include <sys/pserialize.h> #ifdef DEVSW_DEBUG #define DPRINTF(x) printf x @@ -139,6 +141,13 @@ devsw_attach(const char *devname, mutex_enter(&device_lock); + if (bdev != NULL) { + KASSERT(bdev->d_localcount != NULL); + KASSERT(bdev->d_localcount != cdev->d_localcount); + } + if (cdev != NULL) + KASSERT(cdev->d_localcount != NULL); + for (i = 0 ; i < max_devsw_convs ; i++) { conv = &devsw_conv[i]; if (conv->d_name == NULL || strcmp(devname, conv->d_name) != 0) @@ -164,13 +173,14 @@ devsw_attach(const char *devname, goto fail; } + /* use membar_producer() to ensure visibility of the xdevsw */ if (bdev != NULL) { - KASSERT(bdev->d_localcount != NULL); localcount_init(bdev->d_localcount); + membar_producer(); bdevsw[*bmajor] = bdev; } - KASSERT(cdev->d_localcount != NULL); localcount_init(cdev->d_localcount); + membar_producer(); cdevsw[*cmajor] = cdev; mutex_exit(&device_lock); @@ -278,6 +288,9 @@ bdevsw_attach(const struct bdevsw *devsw if (bdevsw[*devmajor] != NULL) return (EEXIST); + /* ensure visibility of the bdevsw */ + membar_producer(); + bdevsw[*devmajor] = devsw; KASSERT(devsw->d_localcount != NULL); localcount_init(devsw->d_localcount); @@ -327,6 +340,9 @@ cdevsw_attach(const struct cdevsw *devsw if (cdevsw[*devmajor] != NULL) return (EEXIST); + /* ensure visibility of the bdevsw */ + membar_producer(); + cdevsw[*devmajor] = devsw; KASSERT(devsw->d_localcount != NULL); localcount_init(devsw->d_localcount); @@ -335,16 +351,15 @@ cdevsw_attach(const struct cdevsw *devsw } /* - * First, look up both bdev and cdev indices, and confirm that the - * localcount pointer(s) exist. Then drain any existing references, - * deactivate the localcount(s). Finally, remove the {b,c}devsw[] - * entries. + * First, look up both bdev and cdev indices, and remove the + * {b,c]devsw[] entries so no new references can be taken. Then + * drain any existing references. */ static void devsw_detach_locked(const struct bdevsw *bdev, const struct cdevsw *cdev) { - int i, j; + int i, j, s; KASSERT(mutex_owned(&device_lock)); @@ -370,24 +385,21 @@ devsw_detach_locked(const struct bdevsw break; } } - if (i < max_bdevsws) { + if (i < max_bdevsws) + bdevsw[i] = NULL; + if (j < max_cdevsws ) + cdevsw[j] = NULL; + + s = pserialize_read_enter(); + if (i < max_bdevsws && bdev->d_localcount != NULL) { localcount_drain(bdev->d_localcount, &device_cv, &device_lock); localcount_fini(bdev->d_localcount); - bdevsw[i] = NULL; } - if (j < max_cdevsws ) { - /* - * Take care not to drain/fini the d_localcount if the same - * one was used for both cdev and bdev! - */ - if (i >= max_bdevsws || - bdev->d_localcount != cdev->d_localcount) { - localcount_drain(cdev->d_localcount, &device_cv, - &device_lock); - localcount_fini(cdev->d_localcount); - } - cdevsw[j] = NULL; + if (j < max_cdevsws && cdev->d_localcount != NULL ) { + localcount_drain(cdev->d_localcount, &device_cv, &device_lock); + localcount_fini(cdev->d_localcount); } + pserialize_read_exit(s); } int @@ -423,6 +435,8 @@ const struct bdevsw * bdevsw_lookup_acquire(dev_t dev) { devmajor_t bmajor; + const struct bdevsw *bdev = NULL; + int s; if (dev == NODEV) return (NULL); @@ -430,20 +444,35 @@ bdevsw_lookup_acquire(dev_t dev) if (bmajor < 0 || bmajor >= max_bdevsws) return (NULL); + /* Prevent any concurrent attempts to detach the device */ + mutex_enter(&device_lock); + + /* Start a read transaction to block localcount_drain() */ + s = pserialize_read_enter(); + + /* Get the struct bdevsw pointer */ + bdev = bdevsw[bmajor]; + if (bdev == NULL) + goto out; + + /* Wait for the content of the struct bdevsw to become visible */ + membar_datadep_consumer(); + + /* If the devsw is not statically linked, acquire a reference */ if (bdevsw[bmajor]->d_localcount != NULL) localcount_acquire(bdevsw[bmajor]->d_localcount); - return (bdevsw[bmajor]); +out: pserialize_read_exit(s); + mutex_exit(&device_lock); + + return bdev; } void bdevsw_release(const struct bdevsw *bd) { - devmajor_t bmaj; - - bmaj = bdevsw_lookup_major(bd); - KASSERTMSG(bmaj != NODEVMAJOR, "%s: no bmajor to release!", __func__); + KASSERT(bd != NULL); if (bd->d_localcount != NULL) localcount_release(bd->d_localcount, &device_cv, &device_lock); } @@ -471,6 +500,8 @@ const struct cdevsw * cdevsw_lookup_acquire(dev_t dev) { devmajor_t cmajor; + const struct cdevsw *cdev = NULL; + int s; if (dev == NODEV) return (NULL); @@ -478,20 +509,35 @@ cdevsw_lookup_acquire(dev_t dev) if (cmajor < 0 || cmajor >= max_cdevsws) return (NULL); + /* Prevent any concurrent attempts to detach the device */ + mutex_enter(&device_lock); + + /* Start a read transaction to block localcount_drain() */ + s = pserialize_read_enter(); + + /* Get the struct bdevsw pointer */ + cdev = cdevsw[cmajor]; + if (cdev == NULL) + goto out; + + /* Wait for the content of the struct bdevsw to become visible */ + membar_datadep_consumer(); + + /* If the devsw is not statically linked, acquire a reference */ if (cdevsw[cmajor]->d_localcount != NULL) localcount_acquire(cdevsw[cmajor]->d_localcount); - return (cdevsw[cmajor]); +out: pserialize_read_exit(s); + mutex_exit(&device_lock); + + return cdev; } void cdevsw_release(const struct cdevsw *cd) { - devmajor_t cmaj; - - cmaj = cdevsw_lookup_major(cd); - KASSERTMSG(cmaj != NODEVMAJOR, "%s: no cmajor to release!", __func__); + KASSERT(cd != NULL); if (cd->d_localcount != NULL) localcount_release(cd->d_localcount, &device_cv, &device_lock); }