xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/dsvclockd/datastore.c (revision 3431:9f2d277dcffa)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stropts.h>
32 #include <synch.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <stdio.h>
36 #include <dhcp_svc_private.h>
37 #include <sys/time.h>
38 #include <dhcpmsg.h>
39 
40 #include "dsvclockd.h"
41 #include "datastore.h"
42 
43 static uint32_t		ds_hash(const char *);
44 
45 /*
46  * Create a datastore named `ds_name' and a door which will service requests
47  * for this datastore.  When the door is called, callback `ds_callback'.
48  * Returns the created datastore.
49  */
50 dsvcd_datastore_t *
ds_create(const char * ds_name,dsvcd_svc_t * ds_callback)51 ds_create(const char *ds_name, dsvcd_svc_t *ds_callback)
52 {
53 	char			door_path[MAXPATHLEN];
54 	dsvcd_datastore_t	*ds = NULL;
55 	int			fd;
56 	unsigned int		i;
57 	door_info_t		info;
58 
59 	dhcpmsg(MSG_VERBOSE, "managing locks for datastore `%s'", ds_name);
60 
61 	ds = malloc(sizeof (dsvcd_datastore_t));
62 	if (ds == NULL) {
63 		dhcpmsg(MSG_ERR, "cannot manage locks for datastore `%s'",
64 		    ds_name);
65 		return (NULL);
66 	}
67 
68 	ds->ds_name = strdup(ds_name);
69 	if (ds->ds_name == NULL) {
70 		dhcpmsg(MSG_ERR, "cannot manage locks for datastore `%s'",
71 		    ds_name);
72 		free(ds);
73 		return (NULL);
74 	}
75 
76 	ds->ds_doorfd = door_create((void (*)())ds_callback, ds,
77 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
78 	if (ds->ds_doorfd == -1) {
79 		dhcpmsg(MSG_ERR, "cannot create door for datastore `%s'",
80 		    ds_name);
81 		free(ds->ds_name);
82 		free(ds);
83 		return (NULL);
84 	}
85 
86 	for (i = 0; i < DSVCD_DS_HASH_SIZE; i++) {
87 		ds->ds_hash[i].cl_head = NULL;
88 		(void) mutex_init(&ds->ds_hash[i].cl_lock, USYNC_THREAD, 0);
89 	}
90 
91 	/*
92 	 * Create the door name in the filesystem.  First, check to see if
93 	 * a door already exists at the specified pathname.  If it does,
94 	 * and the server process (no doubt another copy of us) is already
95 	 * running, then fail.  Otherwise, unlink the old door and fattach
96 	 * a new one.
97 	 */
98 	(void) snprintf(door_path, sizeof (door_path), DSVCD_DOOR_FMT, ds_name);
99 
100 	fd = open(door_path, O_RDWR);
101 	if (fd != -1) {
102 		if (door_info(fd, &info) == 0 && info.di_target != -1) {
103 			dhcpmsg(MSG_ERROR, "%s is in use by process %lu",
104 			    door_path, info.di_target);
105 			(void) close(fd);
106 			(void) close(ds->ds_doorfd);
107 			free(ds->ds_name);
108 			free(ds);
109 			return (NULL);
110 		}
111 		(void) close(fd);
112 		(void) unlink(door_path);
113 	}
114 
115 	fd = open(door_path, O_CREAT|O_EXCL|O_RDWR, 0644);
116 	if (fd == -1) {
117 		dhcpmsg(MSG_ERR, "cannot create door rendezvous for datastore "
118 		    "`%s'", ds_name);
119 		(void) close(ds->ds_doorfd);
120 		free(ds->ds_name);
121 		free(ds);
122 		return (NULL);
123 	}
124 	(void) close(fd);
125 
126 	/*
127 	 * Attach the door onto the name
128 	 */
129 	if (fattach(ds->ds_doorfd, door_path) == -1) {
130 		dhcpmsg(MSG_ERR, "cannot fattach door rendezvous for datastore "
131 		    "`%s'", ds_name);
132 		(void) close(ds->ds_doorfd);
133 		free(ds->ds_name);
134 		free(ds);
135 		return (NULL);
136 	}
137 
138 	return (ds);
139 }
140 
141 /*
142  * Destroy a datastore `ds' and its associated containers, and remove
143  * its door from the filesystem.
144  */
145 void
ds_destroy(dsvcd_datastore_t * ds)146 ds_destroy(dsvcd_datastore_t *ds)
147 {
148 	unsigned int		i;
149 	char			door_path[MAXPATHLEN];
150 	dsvcd_container_t	*cn, *cn_next;
151 
152 	dhcpmsg(MSG_VERBOSE, "stopping lock management for datastore `%s'",
153 	    ds->ds_name);
154 
155 	/*
156 	 * Detach and revoke access to the door.  The detach makes it so
157 	 * new callers who open the door will fail; the revoke makes it
158 	 * so that callers that already have a door descriptor will fail.
159 	 * We do this prior to calling cn_destroy() to make it easier for
160 	 * the container lockcount to drain.
161 	 */
162 	(void) snprintf(door_path, MAXPATHLEN, DSVCD_DOOR_FMT, ds->ds_name);
163 	(void) fdetach(door_path);
164 	(void) unlink(door_path);
165 	(void) door_revoke(ds->ds_doorfd);
166 	(void) close(ds->ds_doorfd);
167 
168 	/*
169 	 * Destroy all the underlying containers.  We're single-threaded at
170 	 * this point, so don't worry about locks.
171 	 */
172 	for (i = 0; i < DSVCD_DS_HASH_SIZE; i++) {
173 		for (cn = ds->ds_hash[i].cl_head; cn != NULL; cn = cn_next) {
174 			cn_next = cn->cn_next;
175 			cn_destroy(cn);
176 		}
177 		(void) mutex_destroy(&ds->ds_hash[i].cl_lock);
178 	}
179 
180 	free(ds->ds_name);
181 	free(ds);
182 }
183 
184 /*
185  * Get a container with id `cn_id' from datastore `ds'; create the
186  * container if it does not exist.  If `crosshost' is set and the container
187  * does not yet exist, then the container will synchronize across hosts.  .
188  * If the container cannot be found or created, NULL is returned.  When the
189  * calling thread is done with the container, ds_release_container() must
190  * be called.
191  */
192 dsvcd_container_t *
ds_get_container(dsvcd_datastore_t * ds,const char * cn_id,boolean_t crosshost)193 ds_get_container(dsvcd_datastore_t *ds, const char *cn_id, boolean_t crosshost)
194 {
195 	dsvcd_container_list_t	*cn_list;
196 	dsvcd_container_t	*cn;
197 	uint32_t		idhash = ds_hash(cn_id);
198 
199 	cn_list = &ds->ds_hash[idhash % DSVCD_DS_HASH_SIZE];
200 	(void) mutex_lock(&cn_list->cl_lock);
201 
202 	for (cn = cn_list->cl_head; cn != NULL; cn = cn->cn_next) {
203 		if (idhash == cn->cn_idhash && strcmp(cn_id, cn->cn_id) == 0)
204 			break;
205 	}
206 
207 	if (cn == NULL) {
208 		cn = cn_create(cn_id, crosshost);
209 		if (cn != NULL) {
210 			if (cn_list->cl_head != NULL)
211 				cn_list->cl_head->cn_prev = cn;
212 
213 			cn->cn_next	 = cn_list->cl_head;
214 			cn->cn_prev	 = NULL;
215 			cn_list->cl_head = cn;
216 			cn->cn_idhash	 = idhash;
217 			cn->cn_nout	 = 0;
218 			cn->cn_lastrel	 = 0;
219 		}
220 	}
221 
222 	if (cn != NULL)
223 		cn->cn_nout++;
224 
225 	(void) mutex_unlock(&cn_list->cl_lock);
226 	return (cn);
227 }
228 
229 /*
230  * Release a container `cn' belonging to datastore `ds'.  Once a container
231  * has been released, it can no longer be used by the releasing thread.
232  * Used to track the number of active instances of a container.
233  */
234 void
ds_release_container(dsvcd_datastore_t * ds,dsvcd_container_t * cn)235 ds_release_container(dsvcd_datastore_t *ds, dsvcd_container_t *cn)
236 {
237 	dsvcd_container_list_t	*cn_list;
238 	uint32_t		idhash = ds_hash(cn->cn_id);
239 
240 	cn_list = &ds->ds_hash[idhash % DSVCD_DS_HASH_SIZE];
241 
242 	(void) mutex_lock(&cn_list->cl_lock);
243 
244 	cn->cn_nout--;
245 	cn->cn_lastrel = time(NULL);
246 
247 	(void) mutex_unlock(&cn_list->cl_lock);
248 }
249 
250 /*
251  * Destroy any containers in datastore `ds' that have not been accessed in
252  * the last `idle' seconds.  Return the number of destroyed (reaped)
253  * containers.
254  */
255 unsigned int
ds_reap_containers(dsvcd_datastore_t * ds,unsigned int idle)256 ds_reap_containers(dsvcd_datastore_t *ds, unsigned int idle)
257 {
258 	dsvcd_container_list_t	*cn_list;
259 	dsvcd_container_t	*cn, *cn_next;
260 	unsigned int		i, nreaped = 0;
261 
262 	for (i = 0; i < DSVCD_DS_HASH_SIZE; i++) {
263 		cn_list = &ds->ds_hash[i];
264 
265 		(void) mutex_lock(&cn_list->cl_lock);
266 		for (cn = cn_list->cl_head; cn != NULL; cn = cn_next) {
267 			cn_next = cn->cn_next;
268 
269 			/*
270 			 * Since a container is not checked out across a
271 			 * lock operation, we must check if the lock is
272 			 * held as well as the number of instances checked
273 			 * out.
274 			 */
275 			if (cn->cn_nout != 0 ||
276 			    cn_locktype(cn) != DSVCD_NOLOCK ||
277 			    cn->cn_lastrel + idle >= time(NULL))
278 				continue;
279 
280 			if (cn == cn_list->cl_head)
281 				cn_list->cl_head = cn->cn_next;
282 			else
283 				cn->cn_prev->cn_next = cn->cn_next;
284 
285 			if (cn->cn_next != NULL)
286 				cn->cn_next->cn_prev = cn->cn_prev;
287 
288 			cn_destroy(cn);
289 			nreaped++;
290 		}
291 		(void) mutex_unlock(&cn_list->cl_lock);
292 	}
293 
294 	return (nreaped);
295 }
296 
297 /*
298  * Hash a container identified by `cn_id' into a 32-bit unsigned integer
299  * suitable for use as a key in a hash table.
300  */
301 static uint32_t
ds_hash(const char * cn_id)302 ds_hash(const char *cn_id)
303 {
304 	uint32_t	result = 0;
305 	unsigned int	i;
306 
307 	for (i = 0; cn_id[i] != '\0'; i++)
308 		result += cn_id[i] << i;
309 
310 	return (result);
311 }
312