1 #
   2 # CDDL HEADER START
   3 #
   4 # The contents of this file are subject to the terms of the
   5 # Common Development and Distribution License, Version 1.0 only
   6 # (the "License").  You may not use this file except in compliance
   7 # with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 DHCP Service Library Synchronization
  23 Peter Memishian, Solaris Software, meem@east.sun.com
  24 
  25 #ident  "%Z%%M% %I%     %E% SMI"
  26 
  27 Introduction
  28 ============
  29 
  30 When writing DHCP service libraries (i.e., public modules) that provide
  31 access to locally-backed datastores (i.e., have their backing datastore on
  32 the same machine that the module is running on), it can be difficult for
  33 the module author to synchronize access to the underlying datastore between
  34 multiple processes, multiple threads within a single process, multiple
  35 threads within multiple processes, and multiple threads within multiple
  36 processes on multiple machines.
  37 
  38 The goal of DHCP Service Library Synchronization is to simplify the design
  39 of modules using locally-backed datastores by pushing these issues up into
  40 the DHCP service library framework: by designing your module to use this
  41 framework, your code becomes simpler and your design cleaner.
  42 
  43 What does DHCP Service Library Synchronization do for me?
  44 =========================================================
  45 
  46 It synchronizes access to several of the DHCP Service Library public-layer
  47 functions; the particular synchronization guarantees vary depending on the
  48 underlying function being called:
  49 
  50         add_d?()        per-container exclusive-perimeter
  51         delete_d?()     per-container exclusive-perimeter
  52         modify_d?()     per-container exclusive-perimeter
  53         lookup_d?()     per-container shared-perimeter
  54         all others      no synchronization provided
  55 
  56 The term `per-container exclusive perimeter' access means that only one
  57 thread may be inside the per-container "perimeter" at a time; that means
  58 that if one thread is inside add_dn() for a given container, no other thread
  59 may be inside add_dn() (or delete_dn(), modify_dn(), and lookup_dn() for
  60 that same container).  However, other threads may be within routines that
  61 provide no synchronization guarantees such as close_dn().
  62 
  63 The term `per-container shared perimeter' access means that multiple threads
  64 may be inside the perimeter, as long as they are all in routines which have
  65 either no synchronization guarantees or also have `per-container shared
  66 perimeter' access.  For instance, multiple threads may be within lookup_dt()
  67 concurrently, but another thread may not be in add_dt() at the same time.
  68 
  69 Note that the preceding discussion assumes that all the threads being
  70 serialized are all running on the same machine.  However, there's also an
  71 optional facility which provides synchronization across multiple threads on
  72 multiple machines as well; see the discussion on cross-host synchronization
  73 below.
  74 
  75 How do I write my module to use DHCP Service Library Synchronization?
  76 =====================================================================
  77 
  78 Write your module just as you normally would.  Of course, when writing your
  79 code, you get to take advantage of the synchronization guarantees this
  80 architecture makes for you.
  81 
  82 When you're done writing your module, then add the following to one of your
  83 C source files:
  84 
  85   /*
  86    * This symbol and its value tell the private layer that it must provide
  87    * synchronization guarantees via dsvclockd(1M) before calling our *_dn()
  88    * and *_dt() methods.  Please see $SRC/lib/libdhcpsvc/private/README.synch
  89    */
  90   int dsvc_synchtype = DSVC_SYNCH_DSVCD;
  91 
  92 Next, note that if you want to use cross-host synchronization, you'll need
  93 to bitwise-or in the DSVC_SYNCH_CROSSHOST flag as well -- however, please
  94 read the discussion below regarding cross-host synchronization first!
  95 
  96 The private layer synchronizes access to similarly named containers; that
  97 is, all requests for a given (location, container_name, container_version,
  98 datastore) tuple are synchronized with respect to one another.  One
  99 implication of this approach is that there must not be two tuples which
 100 identify the same container -- for instance, (/var/dhcp, dhcptab, 1,
 101 SUNWfiles) and (/var/dhcp/, dhcptab, 1, SUNWfiles) name the same container
 102 but are distinct tuples and thus would not be synchronized with respect to
 103 one another!
 104 
 105 To address this issue, the `location' field given in the above tuple is
 106 required to have the property that no two location names map to the same
 107 location.  Public modules whose `location' field does not meet this
 108 constraint must implement a mkloctoken() method, prototyped below, which
 109 maps a location into a token which does meet the constraints.  In the above
 110 scenario, mkloctoken() would use realpath(3C) to perform the mapping.
 111 
 112         int mkloctoken(const char *location, char *token, size_t tokensize);
 113 
 114 The location to map is passed in as `location', which must be mapped into an
 115 ASCII `token' of `tokensize' bytes or less.  The function should return
 116 DSVC_SUCCESS or a DSVC_* error code describing the problem on failure.  Note
 117 that modules which do not use synchronization or already have location names
 118 which meet the constraints need not provide mkloctoken().
 119 
 120 Cross-host Synchronization
 121 ==========================
 122 
 123 Datastores wishing to make use of cross-host synchronization have an
 124 additional constraint: the `location' must be the name of a directory which
 125 is shared and accessible by all hosts which are accessing the datastore.
 126 This constraint is because the code is uses NFS-based file locking to
 127 perform the synchronization.  While this is a severe limitation, only
 128 SUNWfiles currently uses this feature, and even that is only for backward
 129 compatibility.  We discourage use of this feature in future datastore
 130 implementations.
 131 
 132 How does it work?
 133 =================
 134 
 135 It is helpful but not necessary to understand how this architecture works.
 136 Furthermore, the internal details are still evolving; if you rely on any
 137 details here, the only guarantee is that your code will break someday.
 138 
 139 The easiest way to explain the architecture is by example; thus, assume you
 140 have a module `mymod' that you want to use with DHCP Service Library
 141 Synchronization.  Then, for each method specified in the DHCP Server
 142 Performance Project specification, the following happens:
 143 
 144         1. The private layer is called with the specified method
 145            (as specified in the DHCP Server Performance Project spec)
 146 
 147         2. The private layer locates the underlying public module
 148            to invoke, given the settings in /etc/inet/dhcpsvc.conf.
 149            (as specified in the DHCP Server Performance Project spec)
 150 
 151         3. The private layer detects that this module is one that
 152            requires use of DHCP Service Library Synchronization (by
 153            checking the value of the module's dsvc_synchtype symbol).
 154 
 155         4. If this method is one for which synchronization guarantees
 156            are provided, the private layer sends a "lock" request
 157            across a door to the DHCP service door server daemon (also
 158            known as the lock manager), dsvclockd.
 159 
 160         5. The dsvclockd daemon receives the lock request and attempts
 161            to lock a given container for either exclusive or shared
 162            access (depending on the request).  If the lock request was
 163            "nonblocking" and the lock cannot be immediately acquired,
 164            a DSVC_BUSY error is returned.  Otherwise, the daemon waits
 165            until it acquires the lock and sends a DSVC_SUCCESS reply
 166            back.
 167 
 168         6. Assuming the lock could be obtained (if it was necessary;
 169            see step 4), the private layer locates the appropriate
 170            method in `ds_mymod.so' module, and calls it.
 171 
 172         7. Once the method has completed (successfully or otherwise),
 173            if this was a method which required a "lock" request, the
 174            private layer sends an "unlock" request to the dsvclockd.
 175 
 176         8. The private layer returns the reply to the caller.