xref: /dragonfly/sys/vfs/hammer2/hammer2_ccms.c (revision 548a3528)
1 /*
2  * Copyright (c) 2006,2012-2014 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 /*
35  * The Cache Coherency Management System (CCMS)
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/objcache.h>
43 #include <sys/sysctl.h>
44 #include <sys/uio.h>
45 #include <machine/limits.h>
46 
47 #include <sys/spinlock2.h>
48 
49 #include "hammer2_ccms.h"
50 
51 int ccms_debug = 0;
52 
53 /*
54  * Initialize a new CCMS dataspace.  Create a new RB tree with a single
55  * element covering the entire 64 bit offset range.  This simplifies
56  * algorithms enormously by removing a number of special cases.
57  */
58 void
59 ccms_domain_init(ccms_domain_t *dom)
60 {
61 	bzero(dom, sizeof(*dom));
62 	/*kmalloc_create(&dom->mcst, "CCMS-cst");*/
63 	/*dom->root.domain = dom;*/
64 }
65 
66 void
67 ccms_domain_uninit(ccms_domain_t *dom)
68 {
69 	/*kmalloc_destroy(&dom->mcst);*/
70 }
71 
72 void
73 ccms_cst_init(ccms_cst_t *cst, void *handle)
74 {
75 	bzero(cst, sizeof(*cst));
76 	spin_init(&cst->spin);
77 	cst->handle = handle;
78 }
79 
80 void
81 ccms_cst_uninit(ccms_cst_t *cst)
82 {
83 	KKASSERT(cst->count == 0);
84 	if (cst->state != CCMS_STATE_INVALID) {
85 		/* XXX */
86 	}
87 	cst->handle = NULL;
88 }
89 
90 #if 0
91 /*
92  * Acquire an operational CCMS lock on multiple CSTs.
93  *
94  * This code is in the critical path and highly streamlined.
95  */
96 void
97 ccms_lock_get(ccms_lock_t *lock)
98 {
99 	ccms_inode_t *cino = lock->cino;
100 
101 again:
102 	lock->flags &= ~CCMS_LOCK_FAILED;
103 
104 	/*
105 	 * Acquire all local locks first, then resolve them against the
106 	 * remote cache state.  Order is important here.
107 	 */
108 	if (lock->req_t) {
109 		KKASSERT(lock->req_d <= lock->req_t);
110 		KKASSERT(lock->req_a <= lock->req_t);
111 		ccms_thread_lock(&cino->topo_cst, lock->req_t);
112 	}
113 	if (lock->req_a)
114 		ccms_thread_lock(&cino->attr_cst, lock->req_a);
115 	if (lock->req_d)
116 		ccms_thread_lock(&cino->data_cst[0], lock->req_d);
117 
118 	/*
119 	 * Once the local locks are established the CST grant state cannot
120 	 * be pulled out from under us.  However, it is entirely possible
121 	 * to deadlock on it so when CST grant state cannot be obtained
122 	 * trivially we have to unwind our local locks, then get the state,
123 	 * and then loop.
124 	 */
125 	if (lock->req_t > cino->topo_cst.state) {
126 		ccms_rstate_get(lock, &cino->topo_cst, lock->req_t);
127 	} else if (cino->topo_cst.state == CCMS_STATE_INVALID) {
128 		ccms_rstate_get(lock, &cino->topo_cst, CCMS_STATE_ALLOWED);
129 	} else if (cino->topo_cst.state == CCMS_STATE_SHARED &&
130 		    (lock->req_d > CCMS_STATE_SHARED ||
131 		     lock->req_a > CCMS_STATE_SHARED)) {
132 		ccms_rstate_get(lock, &cino->topo_cst, CCMS_STATE_ALLOWED);
133 	}
134 	/* else the rstate is compatible */
135 
136 	if (lock->req_a > cino->attr_cst.state)
137 		ccms_rstate_get(lock, &cino->attr_cst, lock->req_a);
138 
139 	if (lock->req_d > cino->data_cst[0].state)
140 		ccms_rstate_get(lock, &cino->data_cst[0], lock->req_d);
141 
142 	/*
143 	 * If the ccms_rstate_get() code deadlocks (or even if it just
144 	 * blocks), it will release all local locks and set the FAILED
145 	 * bit.  The routine will still acquire the requested remote grants
146 	 * before returning but since the local locks are lost at that
147 	 * point the remote grants are no longer protected and we have to
148 	 * retry.
149 	 */
150 	if (lock->flags & CCMS_LOCK_FAILED) {
151 		goto again;
152 	}
153 }
154 
155 /*
156  * Release a previously acquired CCMS lock.
157  */
158 void
159 ccms_lock_put(ccms_lock_t *lock)
160 {
161 	ccms_inode_t *cino = lock->cino;
162 
163 	if (lock->req_d) {
164 		ccms_thread_unlock(&cino->data_cst[0]);
165 	}
166 	if (lock->req_a) {
167 		ccms_thread_unlock(&cino->attr_cst);
168 	}
169 	if (lock->req_t) {
170 		ccms_thread_unlock(&cino->topo_cst);
171 	}
172 }
173 
174 #endif
175 
176 /************************************************************************
177  *			    CST SUPPORT FUNCTIONS			*
178  ************************************************************************/
179 
180 /*
181  * Acquire local cache state & lock.  If the current thread already holds
182  * the lock exclusively we bump the exclusive count, even if the thread is
183  * trying to get a shared lock.
184  */
185 void
186 ccms_thread_lock(ccms_cst_t *cst, ccms_state_t state)
187 {
188 	/*
189 	 * Regardless of the type of lock requested if the current thread
190 	 * already holds an exclusive lock we bump the exclusive count and
191 	 * return.  This requires no spinlock.
192 	 */
193 	if (cst->count < 0 && cst->td == curthread) {
194 		--cst->count;
195 		return;
196 	}
197 
198 	/*
199 	 * Otherwise use the spinlock to interlock the operation and sleep
200 	 * as necessary.
201 	 */
202 	spin_lock(&cst->spin);
203 	if (state == CCMS_STATE_SHARED) {
204 		while (cst->count < 0 || cst->upgrade) {
205 			cst->blocked = 1;
206 			ssleep(cst, &cst->spin, 0, "ccmslck", hz);
207 		}
208 		++cst->count;
209 		KKASSERT(cst->td == NULL);
210 	} else if (state == CCMS_STATE_EXCLUSIVE) {
211 		while (cst->count != 0 || cst->upgrade) {
212 			cst->blocked = 1;
213 			ssleep(cst, &cst->spin, 0, "ccmslck", hz);
214 		}
215 		cst->count = -1;
216 		cst->td = curthread;
217 	} else {
218 		spin_unlock(&cst->spin);
219 		panic("ccms_thread_lock: bad state %d\n", state);
220 	}
221 	spin_unlock(&cst->spin);
222 }
223 
224 /*
225  * Same as ccms_thread_lock() but acquires the lock non-blocking.  Returns
226  * 0 on success, EBUSY on failure.
227  */
228 int
229 ccms_thread_lock_nonblock(ccms_cst_t *cst, ccms_state_t state)
230 {
231 	if (cst->count < 0 && cst->td == curthread) {
232 		--cst->count;
233 		return(0);
234 	}
235 
236 	spin_lock(&cst->spin);
237 	if (state == CCMS_STATE_SHARED) {
238 		if (cst->count < 0 || cst->upgrade) {
239 			spin_unlock(&cst->spin);
240 			return (EBUSY);
241 		}
242 		++cst->count;
243 		KKASSERT(cst->td == NULL);
244 	} else if (state == CCMS_STATE_EXCLUSIVE) {
245 		if (cst->count != 0 || cst->upgrade) {
246 			spin_unlock(&cst->spin);
247 			return (EBUSY);
248 		}
249 		cst->count = -1;
250 		cst->td = curthread;
251 	} else {
252 		spin_unlock(&cst->spin);
253 		panic("ccms_thread_lock_nonblock: bad state %d\n", state);
254 	}
255 	spin_unlock(&cst->spin);
256 	return(0);
257 }
258 
259 ccms_state_t
260 ccms_thread_lock_temp_release(ccms_cst_t *cst)
261 {
262 	if (cst->count < 0) {
263 		ccms_thread_unlock(cst);
264 		return(CCMS_STATE_EXCLUSIVE);
265 	}
266 	if (cst->count > 0) {
267 		ccms_thread_unlock(cst);
268 		return(CCMS_STATE_SHARED);
269 	}
270 	return (CCMS_STATE_INVALID);
271 }
272 
273 void
274 ccms_thread_lock_temp_restore(ccms_cst_t *cst, ccms_state_t ostate)
275 {
276 	ccms_thread_lock(cst, ostate);
277 }
278 
279 /*
280  * Temporarily upgrade a thread lock for making local structural changes.
281  * No new shared or exclusive locks can be acquired by others while we are
282  * upgrading, but other upgraders are allowed.
283  */
284 ccms_state_t
285 ccms_thread_lock_upgrade(ccms_cst_t *cst)
286 {
287 	/*
288 	 * Nothing to do if already exclusive
289 	 */
290 	if (cst->count < 0) {
291 		KKASSERT(cst->td == curthread);
292 		return(CCMS_STATE_EXCLUSIVE);
293 	}
294 
295 	/*
296 	 * Convert a shared lock to exclusive.
297 	 */
298 	if (cst->count > 0) {
299 		spin_lock(&cst->spin);
300 		++cst->upgrade;
301 		--cst->count;
302 		while (cst->count) {
303 			cst->blocked = 1;
304 			ssleep(cst, &cst->spin, 0, "ccmsupg", hz);
305 		}
306 		cst->count = -1;
307 		cst->td = curthread;
308 		spin_unlock(&cst->spin);
309 		return(CCMS_STATE_SHARED);
310 	}
311 	panic("ccms_thread_lock_upgrade: not locked");
312 	/* NOT REACHED */
313 	return(0);
314 }
315 
316 void
317 ccms_thread_lock_downgrade(ccms_cst_t *cst, ccms_state_t ostate)
318 {
319 	if (ostate == CCMS_STATE_SHARED) {
320 		KKASSERT(cst->td == curthread);
321 		KKASSERT(cst->count == -1);
322 		spin_lock(&cst->spin);
323 		--cst->upgrade;
324 		cst->count = 1;
325 		cst->td = NULL;
326 		if (cst->blocked) {
327 			cst->blocked = 0;
328 			spin_unlock(&cst->spin);
329 			wakeup(cst);
330 		} else {
331 			spin_unlock(&cst->spin);
332 		}
333 	}
334 	/* else nothing to do if excl->excl */
335 }
336 
337 /*
338  * Release a local thread lock
339  */
340 void
341 ccms_thread_unlock(ccms_cst_t *cst)
342 {
343 	if (cst->count < 0) {
344 		/*
345 		 * Exclusive
346 		 */
347 		KKASSERT(cst->td == curthread);
348 		if (cst->count < -1) {
349 			++cst->count;
350 			return;
351 		}
352 		spin_lock(&cst->spin);
353 		KKASSERT(cst->count == -1);
354 		cst->count = 0;
355 		cst->td = NULL;
356 		if (cst->blocked) {
357 			cst->blocked = 0;
358 			spin_unlock(&cst->spin);
359 			wakeup(cst);
360 			return;
361 		}
362 		spin_unlock(&cst->spin);
363 	} else if (cst->count > 0) {
364 		/*
365 		 * Shared
366 		 */
367 		spin_lock(&cst->spin);
368 		if (--cst->count == 0 && cst->blocked) {
369 			cst->blocked = 0;
370 			spin_unlock(&cst->spin);
371 			wakeup(cst);
372 			return;
373 		}
374 		spin_unlock(&cst->spin);
375 	} else {
376 		panic("ccms_thread_unlock: bad zero count\n");
377 	}
378 }
379 
380 void
381 ccms_thread_lock_setown(ccms_cst_t *cst)
382 {
383 	KKASSERT(cst->count < 0);
384 	cst->td = curthread;
385 }
386 
387 /*
388  * Release a previously upgraded local thread lock
389  */
390 void
391 ccms_thread_unlock_upgraded(ccms_cst_t *cst, ccms_state_t ostate)
392 {
393 	if (ostate == CCMS_STATE_SHARED) {
394 		KKASSERT(cst->td == curthread);
395 		KKASSERT(cst->count == -1);
396 		spin_lock(&cst->spin);
397 		--cst->upgrade;
398 		cst->count = 0;
399 		cst->td = NULL;
400 		if (cst->blocked) {
401 			cst->blocked = 0;
402 			spin_unlock(&cst->spin);
403 			wakeup(cst);
404 		} else {
405 			spin_unlock(&cst->spin);
406 		}
407 	} else {
408 		ccms_thread_unlock(cst);
409 	}
410 }
411 
412 #if 0
413 /*
414  * Release a local thread lock with special handling of the last lock
415  * reference.
416  *
417  * If no upgrades are in progress then the last reference to the lock will
418  * upgrade the lock to exclusive (if it was shared) and return 0 without
419  * unlocking it.
420  *
421  * If more than one reference remains, or upgrades are in progress,
422  * we drop the reference and return non-zero to indicate that more
423  * locks are present or pending.
424  */
425 int
426 ccms_thread_unlock_zero(ccms_cst_t *cst)
427 {
428 	if (cst->count < 0) {
429 		/*
430 		 * Exclusive owned by us, no races possible as long as it
431 		 * remains negative.  Return 0 and leave us locked on the
432 		 * last lock.
433 		 */
434 		KKASSERT(cst->td == curthread);
435 		if (cst->count == -1) {
436 			spin_lock(&cst->spin);
437 			if (cst->upgrade) {
438 				cst->count = 0;
439 				if (cst->blocked) {
440 					cst->blocked = 0;
441 					spin_unlock(&cst->spin);
442 					wakeup(cst);
443 				} else {
444 					spin_unlock(&cst->spin);
445 				}
446 				return(1);
447 			}
448 			spin_unlock(&cst->spin);
449 			return(0);
450 		}
451 		++cst->count;
452 	} else {
453 		/*
454 		 * Convert the last shared lock to an exclusive lock
455 		 * and return 0.
456 		 *
457 		 * If there are upgrades pending the cst is unlocked and
458 		 * the upgrade waiters are woken up.  The upgrade count
459 		 * prevents new exclusive holders for the duration.
460 		 */
461 		spin_lock(&cst->spin);
462 		KKASSERT(cst->count > 0);
463 		if (cst->count == 1) {
464 			if (cst->upgrade) {
465 				cst->count = 0;
466 				if (cst->blocked) {
467 					cst->blocked = 0;
468 					spin_unlock(&cst->spin);
469 					wakeup(cst);
470 				} else {
471 					spin_unlock(&cst->spin);
472 				}
473 				return(1);
474 			} else {
475 				cst->count = -1;
476 				cst->td = curthread;
477 				spin_unlock(&cst->spin);
478 				return(0);
479 			}
480 		}
481 		--cst->count;
482 		spin_unlock(&cst->spin);
483 	}
484 	return(1);
485 }
486 #endif
487 
488 int
489 ccms_thread_lock_owned(ccms_cst_t *cst)
490 {
491 	return(cst->count < 0 && cst->td == curthread);
492 }
493 
494 
495 #if 0
496 /*
497  * Acquire remote grant state.  This routine can be used to upgrade or
498  * downgrade the state.  If it blocks it will release any local locks
499  * acquired via (lock) but then it will continue getting the requested
500  * remote grant.
501  */
502 static
503 void
504 ccms_rstate_get(ccms_lock_t *lock, ccms_cst_t *cst, ccms_state_t state)
505 {
506 	/* XXX */
507 	cst->state = state;
508 }
509 
510 #endif
511