2N/A#
2N/A# CDDL HEADER START
2N/A#
2N/A# The contents of this file are subject to the terms of the
2N/A# Common Development and Distribution License, Version 1.0 only
2N/A# (the "License"). You may not use this file except in compliance
2N/A# with the License.
2N/A#
2N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A# or http://www.opensolaris.org/os/licensing.
2N/A# See the License for the specific language governing permissions
2N/A# and limitations under the License.
2N/A#
2N/A# When distributing Covered Code, include this CDDL HEADER in each
2N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A# If applicable, add the following below this CDDL HEADER, with the
2N/A# fields enclosed by brackets "[]" replaced with your own identifying
2N/A# information: Portions Copyright [yyyy] [name of copyright owner]
2N/A#
2N/A# CDDL HEADER END
2N/A#
2N/ADHCP Service Library Synchronization
2N/APeter Memishian, Solaris Software, meem@east.sun.com
2N/A
2N/A#ident "%Z%%M% %I% %E% SMI"
2N/A
2N/AIntroduction
2N/A============
2N/A
2N/AWhen writing DHCP service libraries (i.e., public modules) that provide
2N/Aaccess to locally-backed datastores (i.e., have their backing datastore on
2N/Athe same machine that the module is running on), it can be difficult for
2N/Athe module author to synchronize access to the underlying datastore between
2N/Amultiple processes, multiple threads within a single process, multiple
2N/Athreads within multiple processes, and multiple threads within multiple
2N/Aprocesses on multiple machines.
2N/A
2N/AThe goal of DHCP Service Library Synchronization is to simplify the design
2N/Aof modules using locally-backed datastores by pushing these issues up into
2N/Athe DHCP service library framework: by designing your module to use this
2N/Aframework, your code becomes simpler and your design cleaner.
2N/A
2N/AWhat does DHCP Service Library Synchronization do for me?
2N/A=========================================================
2N/A
2N/AIt synchronizes access to several of the DHCP Service Library public-layer
2N/Afunctions; the particular synchronization guarantees vary depending on the
2N/Aunderlying function being called:
2N/A
2N/A add_d?() per-container exclusive-perimeter
2N/A delete_d?() per-container exclusive-perimeter
2N/A modify_d?() per-container exclusive-perimeter
2N/A lookup_d?() per-container shared-perimeter
2N/A all others no synchronization provided
2N/A
2N/AThe term `per-container exclusive perimeter' access means that only one
2N/Athread may be inside the per-container "perimeter" at a time; that means
2N/Athat if one thread is inside add_dn() for a given container, no other thread
2N/Amay be inside add_dn() (or delete_dn(), modify_dn(), and lookup_dn() for
2N/Athat same container). However, other threads may be within routines that
2N/Aprovide no synchronization guarantees such as close_dn().
2N/A
2N/AThe term `per-container shared perimeter' access means that multiple threads
2N/Amay be inside the perimeter, as long as they are all in routines which have
2N/Aeither no synchronization guarantees or also have `per-container shared
2N/Aperimeter' access. For instance, multiple threads may be within lookup_dt()
2N/Aconcurrently, but another thread may not be in add_dt() at the same time.
2N/A
2N/ANote that the preceding discussion assumes that all the threads being
2N/Aserialized are all running on the same machine. However, there's also an
2N/Aoptional facility which provides synchronization across multiple threads on
2N/Amultiple machines as well; see the discussion on cross-host synchronization
2N/Abelow.
2N/A
2N/AHow do I write my module to use DHCP Service Library Synchronization?
2N/A=====================================================================
2N/A
2N/AWrite your module just as you normally would. Of course, when writing your
2N/Acode, you get to take advantage of the synchronization guarantees this
2N/Aarchitecture makes for you.
2N/A
2N/AWhen you're done writing your module, then add the following to one of your
2N/AC source files:
2N/A
2N/A /*
2N/A * This symbol and its value tell the private layer that it must provide
2N/A * synchronization guarantees via dsvclockd(1M) before calling our *_dn()
2N/A * and *_dt() methods. Please see $SRC/lib/libdhcpsvc/private/README.synch
2N/A */
2N/A int dsvc_synchtype = DSVC_SYNCH_DSVCD;
2N/A
2N/ANext, note that if you want to use cross-host synchronization, you'll need
2N/Ato bitwise-or in the DSVC_SYNCH_CROSSHOST flag as well -- however, please
2N/Aread the discussion below regarding cross-host synchronization first!
2N/A
2N/AThe private layer synchronizes access to similarly named containers; that
2N/Ais, all requests for a given (location, container_name, container_version,
2N/Adatastore) tuple are synchronized with respect to one another. One
2N/Aimplication of this approach is that there must not be two tuples which
2N/Aidentify the same container -- for instance, (/var/dhcp, dhcptab, 1,
2N/ASUNWfiles) and (/var/dhcp/, dhcptab, 1, SUNWfiles) name the same container
2N/Abut are distinct tuples and thus would not be synchronized with respect to
2N/Aone another!
2N/A
2N/ATo address this issue, the `location' field given in the above tuple is
2N/Arequired to have the property that no two location names map to the same
2N/Alocation. Public modules whose `location' field does not meet this
2N/Aconstraint must implement a mkloctoken() method, prototyped below, which
2N/Amaps a location into a token which does meet the constraints. In the above
2N/Ascenario, mkloctoken() would use realpath(3C) to perform the mapping.
2N/A
2N/A int mkloctoken(const char *location, char *token, size_t tokensize);
2N/A
2N/AThe location to map is passed in as `location', which must be mapped into an
2N/AASCII `token' of `tokensize' bytes or less. The function should return
2N/ADSVC_SUCCESS or a DSVC_* error code describing the problem on failure. Note
2N/Athat modules which do not use synchronization or already have location names
2N/Awhich meet the constraints need not provide mkloctoken().
2N/A
2N/ACross-host Synchronization
2N/A==========================
2N/A
2N/ADatastores wishing to make use of cross-host synchronization have an
2N/Aadditional constraint: the `location' must be the name of a directory which
2N/Ais shared and accessible by all hosts which are accessing the datastore.
2N/AThis constraint is because the code is uses NFS-based file locking to
2N/Aperform the synchronization. While this is a severe limitation, only
2N/ASUNWfiles currently uses this feature, and even that is only for backward
2N/Acompatibility. We discourage use of this feature in future datastore
2N/Aimplementations.
2N/A
2N/AHow does it work?
2N/A=================
2N/A
2N/AIt is helpful but not necessary to understand how this architecture works.
2N/AFurthermore, the internal details are still evolving; if you rely on any
2N/Adetails here, the only guarantee is that your code will break someday.
2N/A
2N/AThe easiest way to explain the architecture is by example; thus, assume you
2N/Ahave a module `mymod' that you want to use with DHCP Service Library
2N/ASynchronization. Then, for each method specified in the DHCP Server
2N/APerformance Project specification, the following happens:
2N/A
2N/A 1. The private layer is called with the specified method
2N/A (as specified in the DHCP Server Performance Project spec)
2N/A
2N/A 2. The private layer locates the underlying public module
2N/A to invoke, given the settings in /etc/inet/dhcpsvc.conf.
2N/A (as specified in the DHCP Server Performance Project spec)
2N/A
2N/A 3. The private layer detects that this module is one that
2N/A requires use of DHCP Service Library Synchronization (by
2N/A checking the value of the module's dsvc_synchtype symbol).
2N/A
2N/A 4. If this method is one for which synchronization guarantees
2N/A are provided, the private layer sends a "lock" request
2N/A across a door to the DHCP service door server daemon (also
2N/A known as the lock manager), dsvclockd.
2N/A
2N/A 5. The dsvclockd daemon receives the lock request and attempts
2N/A to lock a given container for either exclusive or shared
2N/A access (depending on the request). If the lock request was
2N/A "nonblocking" and the lock cannot be immediately acquired,
2N/A a DSVC_BUSY error is returned. Otherwise, the daemon waits
2N/A until it acquires the lock and sends a DSVC_SUCCESS reply
2N/A back.
2N/A
2N/A 6. Assuming the lock could be obtained (if it was necessary;
2N/A see step 4), the private layer locates the appropriate
2N/A method in `ds_mymod.so' module, and calls it.
2N/A
2N/A 7. Once the method has completed (successfully or otherwise),
2N/A if this was a method which required a "lock" request, the
2N/A private layer sends an "unlock" request to the dsvclockd.
2N/A
2N/A 8. The private layer returns the reply to the caller.