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.