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.
177