tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Introducing localcount(9)



Some time ago, a discussion arose regarding a better mechanism for
reference-counting device instances and device drivers, to prevent
drivers or devices from "disappearing out from under" the user.  In
response to that discussion, Taylor Campbell (riastradh@) proposed a
new synchronization method called localcount(9).  (You can see the
original discussion at [1] and [2].)  And I had already filed PR
kern/48536 for a similar issue more than two years earlier [3].

Not having enough free time, Taylor asked me to volunteer to actually
implement his design.  This was initially done on pgoyette-localcount
branch, until I managed to screw things up during a sync-with-head.
The work has since been continued on the prg-localcount2 branch [5],
and we believe it is ready for commit to HEAD.

The localcount(9) feature implements a medium-weight reference counting
mechanism.  During normal operations, localcounts do not need to use
inter-processor atomic_ops(3) atomic memory operations, and unlike
psref(9), localcount references can be held across sleeps and migrate
between CPUs.  Draining a localcount requires more expensive inter-
processor synchronization than atomic_ops(3) (similar to psref(9)).  And
localcount references require eight bytes of memory per object per-CPU,
significantly more than atomic_ops(3) and almost always more than
psref(9).  More details can be found in the new localcount(9) manual
page [4].

The existing device autoconfiguration and devsw insertion/removal code
has been enhanced to use localcount(9), to provide greater protection
against having a device or driver "disappear" while it is being used.
Existing non-modular drivers will continue to work without any changes;
see below for modular drivers.

To take advantage of the new reference-counting mechanisms, you need
to make the following changes:

For autoconf usage, localcount(9) is used to protect against individual
device units disappearing while being used.  The traditional mechanisms
still have small "windows" during which the device can be destroyed,
since the check-for-busy code typically resides within the device driver
itself, and depend on mutexes stored within the device's own data.  The
localcount(9) changes check the reference count within the enclosing
framweork rather than in the device driver.  Additionally, the final
reference to a device is not released until immediately before its
localcount is drained, eliminating any timing window.

To implement the improved locking/protection, make the following changes:

     replace                        with
     ----------------------------   ------------------------------------
     device_lookup()                device_lookup_acquire()
     device_find_by_driver_unit()   device_find_by_driver_unit_acquire()
     device_lookup_private()        device_lookup_private_acquire()

     All of the new routines acquire a reference to the device.  When
     the caller is finished with the device, it needs to release the
     reference by calling device_release().  Once device_release() is
     called, the pointer to the struct device_t should not be referenced
     (and thus should not be stored elsewhere for later use).  Routines
     such as I/O completion routines should use the original dev_t value
     and repeat the device_lookup_acquire() call.

     NOTE: For device_lookup_private_acquire(), the caller must ensure
     that it is capable of calling device_release() at some later point
     in time with the proper device_t value.  Thus, the device _must_
     have non-NULL private data, and presumably this data includes the
     necessary device_t value or other means to obtain the value.

     When detaching a device, instead of calling device_detach(), you
     should acquire a reference to the device, perform whatever checks
     you need, and then call device_detach_release()

For devsw usage, localcount(9) is used to protect against the modular
driver itself from being removed while it is active.  Once again,
traditional methods of detecting in-use drivers rely on code within the
driver, and on mutexes contained within the driver.  The localcount(9)
changes maintain a reference count against the bdevsw or cdevsw;  any
attempt to detach the {b,c}devsw waits until all references have been
released.

To obtain the improved device driver locking/protection, make the
following changes:

     replace                        with
     -----------------------------  ------------------------------------
     bdevsw_lookup()                bdevsw_lookup_acquire()
     cdevsw_lookup()                cdevsw_lookup_acquire()

     And, when finished, call bdevsw_release() or cdevsw_release().

These changes are only meaningful for drivers which can be dynamically
added to and/or removed from the kernel, including rump components.  For
drivers which are "built-in" to the kernel, the new *_acquire routines
function identically to the old routines, and the *_release() routines
are no-ops.

In addition to the above routine name changes, update the driver's bdevsw
and/or cdevsw initializers to include an additional field:

       DEVSW_MODULE_INIT

For modular drivers, this macro allocates and initializes the required
localcount structure.

NOTE: The DEVSW_MODULE_INIT macro is required for _all_ modular device
drivers, whether or not the body of the driver has been updated to use
the new *_acquire routines!  Attempting to use modctl(2) to load a modular
drivers that does not include this macro will return EINVAL.

NOTE: The DEVSW_MODULE_INIT macro currently includes a trailing comma,
so don't add your own comma!  An extra comma will cause a compiler error.

Many device drivers have already been updated for both of these new
reference-count mechanisms; you can use them as examples for your own
work.  Also, all modular device drivers have been updated to use the
DEVSW_MODULE_INIT macro as necessary.

If you want to look at the implementation of localcount(9), you can see
the code in the CVS repository [5].  Files sys/kern/subr_localcount.c
and sys/sys/localcount.h contain the localcount functionality;  files
sys/kern/subr_autoconf.c and sys/kern/subr_devsw.c have been updated to
use the localcount functionality;  and many device drivers throughout
the system have been updated to provide improved reference counting.
(Don't forget to look for any newly created files in the Attic!)

Unless there are substantial objections, I'd like to commit localcount
in a couple of weeks.  Constructive comments and code review welcomed!


References:
[1] https://mail-index.netbsd.org/tech-kern/2016/06/08/msg020691.html
[2] https://mail-index.netbsd.org/tech-kern/2016/06/13/msg020713.html
[3] http://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=48536
[4] http://www.netbsd.org/~pgoyette/localcount.9
[5] http://cvsweb.netbsd.org/bsdweb.cgi/src/?only_with_tag=prg-localcount2

+------------------+--------------------------+----------------------------+
| Paul Goyette     | PGP Key fingerprint:     | E-mail addresses:          |
| (Retired)        | FA29 0E3B 35AF E8AE 6651 | paul at whooppee dot com   |
| Kernel Developer | 0786 F758 55DE 53BA 7731 | pgoyette at netbsd dot org |
+------------------+--------------------------+----------------------------+


Home | Main Index | Thread Index | Old Index