Now
MAIN commitmail json YAML
driver(9): Fix synchronization of devsw_attach/lookup/detach.
(`dev' means either `bdev' or `cdev' for brevity here, e.g. in
`devsw_lookup' (bdevsw_lookup, cdevsw_lookup), `dev_open' (bdev_open,
cdev_open), `maxdevsws', &c., except for `devsw_attach' and
`devsw_detach' which are taken literally.)
- Use atomic_store_release and atomic_load_consume for devsw and
tables and their entries, which are read unlocked and thus require
memory barriers to ensure ordering between initialization in
devsw_attach and use in dev_lookup.
- Use pserialize(9) and localcount(9) to synchronize dev_open and
devsw_detach.
=> Driver must ensure d_open fails and all open instances have been
closed by the time it calls devsw_detach.
=> Bonus: dev_open is no longer globally serialized through
device_lock.
- Use atomic_store_release and atomic_load_acquire for max_devsws,
which is used in conditionals in the new devsw_lookup_acquire.
=> It is safe to use atomic_load_relaxed in devsw_lookup because
the caller must guarantee the entry is stable, so any increase
of max_devsws must have already happened.
=> devsw_lookup and devsw_lookup_acquire assume that max_devsws
never goes down. If you change this you must find some way to
adapt the users, preferably without adding much overhead so that
devsw operations are cheap.
This change introduces an auxiliary table devswref mapping device
majors to localcounts of opens in progress. The auxiliary table only
occupies one pointer's worth of memory in a monolithic kernel, and is
allocated on the fly for dynamically loaded modules. We could ask
the module itself to reserve storage for it, but I don't see much
value in that, and it would require some changes to the ABI and to
config(8).
- Omit needless boolean indirection.
(`dev' means either `bdev' or `cdev' for brevity here, e.g. in
`devsw_lookup' (bdevsw_lookup, cdevsw_lookup), `dev_open' (bdev_open,
cdev_open), `maxdevsws', &c., except for `devsw_attach' and
`devsw_detach' which are taken literally.)
- Use atomic_store_release and atomic_load_consume for devsw and
tables and their entries, which are read unlocked and thus require
memory barriers to ensure ordering between initialization in
devsw_attach and use in dev_lookup.
- Use pserialize(9) and localcount(9) to synchronize dev_open and
devsw_detach.
=> Driver must ensure d_open fails and all open instances have been
closed by the time it calls devsw_detach.
=> Bonus: dev_open is no longer globally serialized through
device_lock.
- Use atomic_store_release and atomic_load_acquire for max_devsws,
which is used in conditionals in the new devsw_lookup_acquire.
=> It is safe to use atomic_load_relaxed in devsw_lookup because
the caller must guarantee the entry is stable, so any increase
of max_devsws must have already happened.
=> devsw_lookup and devsw_lookup_acquire assume that max_devsws
never goes down. If you change this you must find some way to
adapt the users, preferably without adding much overhead so that
devsw operations are cheap.
This change introduces an auxiliary table devswref mapping device
majors to localcounts of opens in progress. The auxiliary table only
occupies one pointer's worth of memory in a monolithic kernel, and is
allocated on the fly for dynamically loaded modules. We could ask
the module itself to reserve storage for it, but I don't see much
value in that, and it would require some changes to the ABI and to
config(8).
- Omit needless boolean indirection.