1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup dnsdbzone Zone related functions
36 * @ingroup dnsdb
37 * @brief Functions used to manipulate a zone
38 *
39 * Functions used to manipulate a zone
40 *
41 * @{
42 */
43
44 #include "dnsdb/dnsdb-config.h"
45 #include <unistd.h>
46 #include <arpa/inet.h>
47
48 #include <dnscore/mutex.h>
49
50 #include <dnscore/dnscore.h>
51
52 #include <dnscore/logger.h>
53 #include <dnsdb/zdb-zone-lock-monitor.h>
54
55 #include "dnsdb/zdb.h"
56
57 #include "dnsdb/zdb_zone.h"
58 #include "dnsdb/zdb_zone_label.h"
59 #include "dnsdb/zdb_rr_label.h"
60 #include "dnsdb/zdb_record.h"
61
62 #include "dnsdb/zdb_utils.h"
63 #include "dnsdb/zdb_error.h"
64
65 #include "dnsdb/dnsrdata.h"
66
67 #if ZDB_HAS_NSEC_SUPPORT
68 #include "dnsdb/nsec.h"
69 #endif
70 #if ZDB_HAS_NSEC3_SUPPORT
71 #include "dnsdb/nsec3.h"
72 #endif
73
74 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
75 #include <dnscore/ptr_set.h>
76 #include <dnsdb/zdb-zone-lock-monitor.h>
77 #endif
78
79 #if DEBUG
80 #define ZONE_MUTEX_LOG 0 // set this to 0 to disable in DEBUG
81 #else
82 #define ZONE_MUTEX_LOG 0
83 #endif
84
85 extern logger_handle* g_database_logger;
86 #define MODULE_MSG_HANDLE g_database_logger
87
88 #define MUTEX_LOCKED_TOO_MUCH_TIME_US 5000000
89 #define MUTEX_WAITED_TOO_MUCH_TIME_US 2000000
90
91 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
92
93 static mutex_t zdb_zone_lock_set_mtx = MUTEX_INITIALIZER;
94 static ptr_set zdb_zone_lock_set = PTR_SET_PTR_EMPTY;
95
96 void
zdb_zone_lock_set_add(zdb_zone * zone)97 zdb_zone_lock_set_add(zdb_zone *zone)
98 {
99 mutex_lock(&zdb_zone_lock_set_mtx);
100 ptr_node *node = ptr_set_insert(&zdb_zone_lock_set, zone);
101 node->value = zone;
102 mutex_unlock(&zdb_zone_lock_set_mtx);
103 }
104
105 void
zdb_zone_lock_set_del(zdb_zone * zone)106 zdb_zone_lock_set_del(zdb_zone *zone)
107 {
108 mutex_lock(&zdb_zone_lock_set_mtx);
109 ptr_set_delete(&zdb_zone_lock_set, zone);
110 mutex_unlock(&zdb_zone_lock_set_mtx);
111 }
112
113 static s64 zdb_zone_lock_set_monitor_last_duration = 0;
114 static s64 zdb_zone_lock_set_monitor_last_time = 0;
115
116 #if DEBUG
117 const char* zdb_zone_lock_names[11]=
118 {
119 "NOBODY", // 0x00
120 "SIMPLEREADER", // 0x01 non-conflicting
121 "RRSIG_UPDATER",// 0x82 conflicting
122 "3?",
123 "XFR", // 0x84 conflicting
124 "REFRESH", // 0x85 conflicting
125 "DYNUPDATE", // 0x86 conflicting
126 "UNFREEZE", // 0x87 conflicting
127 "INVALIDATE", // 0x88 conflicting
128 "REPLACE", // 0x89 conflicting
129 "LOAD" // 0x8a conflicting
130 // "DESTROY" // 0xFF conflicting, can never be launched more than once. The zone will be destroyed before unlock.
131 };
132
133 #endif
134
135 void
zdb_zone_lock_set_monitor()136 zdb_zone_lock_set_monitor()
137 {
138 s64 now = timeus();
139
140 if(now - zdb_zone_lock_set_monitor_last_time < zdb_zone_lock_set_monitor_last_duration)
141 {
142 return;
143 }
144
145 zdb_zone_lock_set_monitor_last_time = now;
146
147 mutex_lock(&zdb_zone_lock_set_mtx);
148 ptr_set_iterator iter;
149 ptr_set_iterator_init(&zdb_zone_lock_set, &iter);
150
151 while(ptr_set_iterator_hasnext(&iter))
152 {
153 ptr_node *node = ptr_set_iterator_next_node(&iter);
154 zdb_zone *zone = (zdb_zone*)node->key;
155
156 u8 owner = zone->lock_owner;
157 if(owner == GROUP_MUTEX_NOBODY)
158 {
159 continue;
160 }
161
162 s64 ts = zone->lock_timestamp;
163 stacktrace trace = zone->lock_trace;
164 thread_t id = zone->lock_id;
165 if(ts < now)
166 {
167 u64 dt = now - ts;
168 if(dt > MUTEX_LOCKED_TOO_MUCH_TIME_US)
169 {
170 // locked for 5 seconds ... trouble
171 #if !DEBUG
172 log_warn("zdb_zone_lock@%p: %{dnsname}: locked by %x for %lluus by %p", zone, zone->origin, owner, dt, (intptr)id);
173 #else
174 if(owner <= 10)
175 {
176 log_warn("zdb_zone_lock@%p: %{dnsname}: locked by %s for %lluus by %p", zone, zone->origin, zdb_zone_lock_names[owner], dt, (intptr)id);
177 }
178 else
179 {
180 log_warn("zdb_zone_lock@%p: %{dnsname}: locked by %x for %lluus by %p", zone, zone->origin, owner, dt, (intptr)id);
181 }
182 #endif
183 debug_stacktrace_log(MODULE_MSG_HANDLE, MSG_WARNING, trace);
184 }
185 }
186 }
187 mutex_unlock(&zdb_zone_lock_set_mtx);
188 s64 after = timeus();
189 if(after - now > zdb_zone_lock_set_monitor_last_duration)
190 {
191 zdb_zone_lock_set_monitor_last_duration = after - now;
192 }
193 }
194 #endif
195
196 bool
zdb_zone_islocked(zdb_zone * zone)197 zdb_zone_islocked(zdb_zone *zone)
198 {
199 mutex_t *mutex = &zone->lock_mutex;
200 mutex_lock(mutex);
201 u8 owner = zone->lock_owner;
202 mutex_unlock(mutex);
203
204 return owner != 0;
205 }
206
207 bool
zdb_zone_islocked_weak(const zdb_zone * zone)208 zdb_zone_islocked_weak(const zdb_zone *zone)
209 {
210 u8 owner = zone->lock_owner;
211
212 return owner != 0;
213 }
214
215 /**
216 * Returns TRUE iff the zone is locked by a writer (any other owner value than nobody and simple reader)
217 *
218 * @param zone
219 * @return
220 */
221
222 bool
zdb_zone_iswritelocked(zdb_zone * zone)223 zdb_zone_iswritelocked(zdb_zone *zone)
224 {
225 mutex_t *mutex = &zone->lock_mutex;
226 mutex_lock(mutex);
227 u8 owner = zone->lock_owner;
228 mutex_unlock(mutex);
229
230 return owner > ZDB_ZONE_MUTEX_SIMPLEREADER;
231 }
232
233 void
zdb_zone_lock(zdb_zone * zone,u8 owner)234 zdb_zone_lock(zdb_zone *zone, u8 owner)
235 {
236
237 #if ZONE_MUTEX_LOG
238 log_debug7("acquiring lock for zone %{dnsname}@%p for %x", zone->origin, zone, owner);
239 #endif
240
241 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
242 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
243 u64 start = timeus();
244 #endif
245 #endif
246
247 #if MUTEX_CONTENTION_MONITOR
248 struct mutex_contention_monitor_s *mcm = mutex_contention_lock_begin(thread_self(), zone, debug_stacktrace_get(), "zdb_zone");
249 #endif
250
251 mutex_t *mutex = &zone->lock_mutex;
252 mutex_lock(mutex);
253
254 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
255 struct zdb_zone_lock_monitor *holder = zdb_zone_lock_monitor_new(zone, owner, 0);
256 #endif
257
258 for(;;)
259 {
260 /*
261 A simple way to ensure that a lock can be shared
262 by similar entities or not.
263 Sharable entities have their msb off.
264 */
265
266 u8 co = zone->lock_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
267
268 if(co == GROUP_MUTEX_NOBODY || co == owner)
269 {
270 yassert(!SIGNED_VAR_VALUE_IS_MAX(zone->lock_count));
271
272 zone->lock_owner = owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
273 zone->lock_count++;
274
275 break;
276 }
277
278 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
279 zdb_zone_lock_monitor_waits(holder);
280 #endif
281
282 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
283 s64 d = timeus() - start;
284 if(d > MUTEX_WAITED_TOO_MUCH_TIME_US)
285 {
286 log_warn("zdb_zone_lock(%{dnsname},%x) : waited for %llius already ...", zone->origin, owner, d);
287 debug_log_stacktrace(MODULE_MSG_HANDLE, MSG_WARNING, "zdb_zone_double_lock:");
288 }
289 cond_timedwait(&zone->lock_cond, mutex, MUTEX_WAITED_TOO_MUCH_TIME_US);
290 #else
291 cond_wait(&zone->lock_cond, mutex);
292 #endif
293
294 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
295 zdb_zone_lock_monitor_resumes(holder);
296 #endif
297 }
298
299 #if ZDB_ZONE_LOCK_HAS_OWNER_ID
300 zone->lock_last_owner_id = thread_self();
301 #endif
302
303 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
304 zdb_zone_lock_monitor_locks(holder);
305 #endif
306
307 mutex_unlock(mutex);
308
309 #if MUTEX_CONTENTION_MONITOR
310 mutex_contention_lock_end(mcm);
311 #endif
312
313 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
314 zone->lock_trace = debug_stacktrace_get();
315 zone->lock_id = thread_self();
316 zone->lock_timestamp = timeus();
317
318 zdb_zone_lock_set_add(zone);
319 #endif
320 }
321
322 bool
zdb_zone_trylock(zdb_zone * zone,u8 owner)323 zdb_zone_trylock(zdb_zone *zone, u8 owner)
324 {
325 #if ZONE_MUTEX_LOG
326 log_debug7("trying to acquire lock for zone %{dnsname}@%p for %x", zone->origin, zone, owner);
327 #endif
328
329 #if MUTEX_CONTENTION_MONITOR
330 struct mutex_contention_monitor_s *mcm = mutex_contention_lock_begin(thread_self(), zone, debug_stacktrace_get(), "zdb_zone");
331 #endif
332
333 mutex_lock(&zone->lock_mutex);
334
335 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
336 struct zdb_zone_lock_monitor *holder = zdb_zone_lock_monitor_new(zone, owner, 0);
337 #endif
338
339 u8 co = zone->lock_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
340
341 if(co == ZDB_ZONE_MUTEX_NOBODY || co == owner)
342 {
343 yassert(!SIGNED_VAR_VALUE_IS_MAX(zone->lock_count));
344
345 zone->lock_owner = owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
346 zone->lock_count++;
347
348 #if ZONE_MUTEX_LOG
349 log_debug7("acquired lock for zone %{dnsname}@%p for %x (#%i)", zone->origin, zone, owner, zone->lock_count);
350 #endif
351
352 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
353 zdb_zone_lock_monitor_locks(holder);
354 #endif
355
356 #if ZDB_ZONE_LOCK_HAS_OWNER_ID
357 zone->lock_last_owner_id = thread_self();
358 #endif
359
360 mutex_unlock(&zone->lock_mutex);
361
362 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
363 zone->lock_trace = debug_stacktrace_get();
364 zone->lock_id = thread_self();
365 zone->lock_timestamp = timeus();
366
367 zdb_zone_lock_set_add(zone);
368 #endif
369
370 #if MUTEX_CONTENTION_MONITOR
371 mutex_contention_lock_end(mcm);
372 #endif
373 return TRUE;
374 }
375
376 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
377 zdb_zone_lock_monitor_cancels(holder);
378 #endif
379
380 mutex_unlock(&zone->lock_mutex);
381
382 #if ZONE_MUTEX_LOG
383 log_debug7("failed to acquire lock for zone %{dnsname}@%p for %x", zone->origin, zone, owner);
384 #endif
385
386 #if MUTEX_CONTENTION_MONITOR
387 mutex_contention_lock_fail(mcm);
388 #endif
389
390 return FALSE;
391 }
392
393 bool
zdb_zone_trylock_wait(zdb_zone * zone,u64 usec,u8 owner)394 zdb_zone_trylock_wait(zdb_zone *zone, u64 usec, u8 owner)
395 {
396 #if ZONE_MUTEX_LOG
397 log_debug7("trying to acquire lock for %lluus for zone %{dnsname}@%p for %x", usec, zone->origin, zone, owner);
398 #endif
399
400 #if MUTEX_CONTENTION_MONITOR
401 struct mutex_contention_monitor_s *mcm = mutex_contention_lock_begin(thread_self(), zone, debug_stacktrace_get(), "zdb_zone");
402 #endif
403
404 u64 start = timeus();
405 bool ret = FALSE;
406
407 mutex_t *mutex = &zone->lock_mutex;
408
409 mutex_lock(mutex);
410
411 for(;;)
412 {
413 /*
414 A simple way to ensure that a lock can be shared
415 by similar entities or not.
416 Sharable entities have their msb off.
417 */
418
419 u8 co = zone->lock_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
420
421 if(co == GROUP_MUTEX_NOBODY || co == owner)
422 {
423 yassert(!SIGNED_VAR_VALUE_IS_MAX(zone->lock_count));
424
425 zone->lock_owner = owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
426 zone->lock_count++;
427
428 ret = TRUE;
429 break;
430 }
431
432 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
433 s64 d = timeus() - start;
434 if(d > MUTEX_WAITED_TOO_MUCH_TIME_US)
435 {
436 log_warn("zdb_zone_lock(%{dnsname},%x) : waited for %llius already ...", zone->origin, owner, d);
437 debug_log_stacktrace(MODULE_MSG_HANDLE, MSG_WARNING, "zdb_zone_double_lock:");
438 }
439 cond_timedwait(&zone->lock_cond, mutex, MIN(MUTEX_WAITED_TOO_MUCH_TIME_US, usec));
440 #else
441 cond_timedwait(&zone->lock_cond, mutex, usec);
442 #endif
443 u64 now = timeus();
444
445 if(now - start >= usec)
446 {
447 break;
448 }
449 }
450
451 #if ZDB_ZONE_LOCK_HAS_OWNER_ID
452 if(ret)
453 {
454 zone->lock_last_owner_id = thread_self();
455 }
456 #endif
457
458 mutex_unlock(mutex);
459
460 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
461 zone->lock_trace = debug_stacktrace_get();
462 zone->lock_id = thread_self();
463 zone->lock_timestamp = timeus();
464
465 zdb_zone_lock_set_add(zone);
466 #endif
467
468 #if MUTEX_CONTENTION_MONITOR
469 if(ISOK(ret))
470 {
471 mutex_contention_lock_end(mcm);
472 }
473 else
474 {
475 mutex_contention_lock_fail(mcm);
476 }
477 #endif
478
479 return ret;
480 }
481
482 void
zdb_zone_unlock(zdb_zone * zone,u8 owner)483 zdb_zone_unlock(zdb_zone *zone, u8 owner)
484 {
485 #if ZONE_MUTEX_LOG
486 log_debug7("releasing lock for zone %{dnsname}@%p by %x (owned by %x)", zone->origin, zone, owner, zone->lock_owner);
487 #else
488 (void)owner;
489 #endif
490
491 mutex_lock(&zone->lock_mutex);
492
493 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
494 struct zdb_zone_lock_monitor *holder = zdb_zone_lock_monitor_get(zone);
495 #endif
496
497 #if DEBUG
498 if(((zone->lock_owner & ZDB_ZONE_MUTEX_UNLOCKMASK_FLAG) != (owner & ZDB_ZONE_MUTEX_UNLOCKMASK_FLAG)) || (zone->lock_count == 0))
499 {
500 yassert((zone->lock_owner == ZDB_ZONE_MUTEX_DESTROY) || (zone->lock_owner == (owner & ZDB_ZONE_MUTEX_UNLOCKMASK_FLAG)));
501 yassert(zone->lock_count != 0);
502 abort(); // unreachable
503 }
504 #endif
505
506 --zone->lock_count;
507
508 #if ZONE_MUTEX_LOG
509 log_debug7("released lock for zone %{dnsname}@%p by %x (#%i)", zone->origin, zone, owner, zone->lock_count);
510 #endif
511
512 if(zone->lock_count == 0)
513 {
514 zone->lock_owner = ZDB_ZONE_MUTEX_NOBODY;
515 cond_notify(&zone->lock_cond);
516 }
517
518 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
519 zdb_zone_lock_monitor_unlocks(holder);
520 #endif
521
522 #if ZDB_ZONE_LOCK_HAS_OWNER_ID
523 thread_t tid = thread_self();
524 if(zone->lock_last_owner_id == tid)
525 {
526 zone->lock_last_owner_id = 0;
527 }
528 #endif
529
530 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
531 zone->lock_trace = NULL;
532 zone->lock_id = 0;
533 zone->lock_timestamp = 0;
534
535 zdb_zone_lock_set_del(zone);
536 #endif
537
538 mutex_unlock(&zone->lock_mutex);
539
540 #if MUTEX_CONTENTION_MONITOR
541 mutex_contention_unlock(thread_self(), zone);
542 #endif
543 }
544
545 void
zdb_zone_double_lock(zdb_zone * zone,u8 owner,u8 secondary_owner)546 zdb_zone_double_lock(zdb_zone *zone, u8 owner, u8 secondary_owner)
547 {
548 #if MUTEX_CONTENTION_MONITOR
549 struct mutex_contention_monitor_s *mcm = mutex_contention_lock_begin(thread_self(), zone, debug_stacktrace_get(), "zdb_zone");
550 #endif
551
552 #if ZONE_MUTEX_LOG
553 log_debug7("acquiring lock for zone %{dnsname}@%p for %x", zone->origin, zone, owner);
554 #endif
555
556 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
557 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
558 u64 start = timeus();
559 #endif
560 #endif
561
562 mutex_lock(&zone->lock_mutex);
563
564 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
565 struct zdb_zone_lock_monitor *holder = zdb_zone_lock_monitor_new(zone, owner, secondary_owner);
566 #endif
567
568 for(;;)
569 {
570 /*
571 * A simple way to ensure that a lock can be shared
572 * by similar entities or not.
573 * Sharable entities have their msb off.
574 */
575
576 u8 so = zone->lock_reserved_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
577
578 if(so == ZDB_ZONE_MUTEX_NOBODY || so == secondary_owner)
579 {
580 u8 co = zone->lock_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
581
582 if(co == ZDB_ZONE_MUTEX_NOBODY || co == owner)
583 {
584 yassert(!SIGNED_VAR_VALUE_IS_MAX(zone->lock_count));
585
586 zone->lock_owner = owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
587 zone->lock_count++;
588 zone->lock_reserved_owner = secondary_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
589
590 #if ZDB_ZONE_LOCK_HAS_OWNER_ID
591 thread_t tid = thread_self();
592 zone->lock_last_owner_id = tid;
593 zone->lock_last_reserved_owner_id = tid;
594 #endif
595
596 #if ZONE_MUTEX_LOG
597 log_debug7("acquired lock for zone %{dnsname}@%p for %x (#%i)", zone->origin, zone, owner, zone->lock_count);
598 #endif
599
600 break;
601 }
602 }
603 else
604 {
605 // the secondary owner is already taken
606 }
607
608 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
609 zdb_zone_lock_monitor_waits(holder);
610 #endif
611
612 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
613 s64 d = timeus() - start;
614 if(d > MUTEX_WAITED_TOO_MUCH_TIME_US)
615 {
616 log_warn("zdb_zone_double_lock(%{dnsname},%x,%x) : waited for %llius already ...", zone->origin, owner, secondary_owner, d);
617 debug_log_stacktrace(MODULE_MSG_HANDLE, MSG_WARNING, "zdb_zone_double_lock:");
618 }
619 cond_timedwait(&zone->lock_cond, &zone->lock_mutex, MUTEX_WAITED_TOO_MUCH_TIME_US);
620 #else
621 cond_wait(&zone->lock_cond, &zone->lock_mutex);
622 #endif
623 }
624
625 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
626 zdb_zone_lock_monitor_locks(holder);
627 #endif
628
629 mutex_unlock(&zone->lock_mutex);
630
631 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
632 zone->lock_trace = debug_stacktrace_get();
633 zone->lock_id = thread_self();
634 zone->lock_timestamp = timeus();
635
636 zdb_zone_lock_set_add(zone);
637 #endif
638
639 #if MUTEX_CONTENTION_MONITOR
640 mutex_contention_lock_end(mcm);
641 #endif
642 }
643
644 bool
zdb_zone_try_double_lock(zdb_zone * zone,u8 owner,u8 secondary_owner)645 zdb_zone_try_double_lock(zdb_zone *zone, u8 owner, u8 secondary_owner)
646 {
647 #if MUTEX_CONTENTION_MONITOR
648 struct mutex_contention_monitor_s *mcm = mutex_contention_lock_begin(thread_self(), zone, debug_stacktrace_get(), "zdb_zone");
649 #endif
650
651 #if ZONE_MUTEX_LOG
652 log_debug7("trying to acquire lock for zone %{dnsname}@%p for %x", zone->origin, zone, owner);
653 #endif
654 mutex_lock(&zone->lock_mutex);
655
656 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
657 struct zdb_zone_lock_monitor *holder = zdb_zone_lock_monitor_new(zone, owner, secondary_owner);
658 #endif
659
660 u8 so = zone->lock_reserved_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
661
662 if(so == ZDB_ZONE_MUTEX_NOBODY || so == secondary_owner)
663 {
664 u8 co = zone->lock_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
665
666 if(co == ZDB_ZONE_MUTEX_NOBODY || co == owner)
667 {
668 yassert(!SIGNED_VAR_VALUE_IS_MAX(zone->lock_count));
669
670 zone->lock_owner = owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
671 zone->lock_count++;
672 zone->lock_reserved_owner = secondary_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
673
674 #if ZDB_ZONE_LOCK_HAS_OWNER_ID
675 thread_t tid = thread_self();
676 zone->lock_last_owner_id = tid;
677 zone->lock_last_reserved_owner_id = tid;
678 #endif
679
680 #if ZONE_MUTEX_LOG
681 log_debug7("acquired lock for zone %{dnsname}@%p for %x (#%i)", zone->origin, zone, owner, zone->lock_count);
682 #endif
683
684 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
685 zdb_zone_lock_monitor_locks(holder);
686 #endif
687
688 mutex_unlock(&zone->lock_mutex);
689
690 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
691 zone->lock_trace = debug_stacktrace_get();
692 zone->lock_id = thread_self();
693 zone->lock_timestamp = timeus();
694
695 zdb_zone_lock_set_add(zone);
696 #endif
697 #if MUTEX_CONTENTION_MONITOR
698 mutex_contention_lock_end(mcm);
699 #endif
700
701 return TRUE;
702 }
703 }
704 /*
705 else
706 {
707 // already double-owned
708 }
709 */
710
711 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
712 zdb_zone_lock_monitor_cancels(holder);
713 #endif
714
715 mutex_unlock(&zone->lock_mutex);
716
717 #if ZONE_MUTEX_LOG
718 log_debug7("failed to acquire lock for zone %{dnsname}@%p for %x", zone->origin, zone, owner);
719 #endif
720
721 #if MUTEX_CONTENTION_MONITOR
722 mutex_contention_lock_fail(mcm);
723 #endif
724
725 return FALSE;
726 }
727
728 bool
zdb_zone_try_double_lock_ex(zdb_zone * zone,u8 owner,u8 secondary_owner,u8 * current_ownerp,u8 * current_reserved_ownerp)729 zdb_zone_try_double_lock_ex(zdb_zone *zone, u8 owner, u8 secondary_owner, u8 *current_ownerp, u8 *current_reserved_ownerp)
730 {
731 #if MUTEX_CONTENTION_MONITOR
732 struct mutex_contention_monitor_s *mcm = mutex_contention_lock_begin(thread_self(), zone, debug_stacktrace_get(), "zdb_zone");
733 #endif
734
735 #if ZONE_MUTEX_LOG
736 log_debug7("trying to acquire lock for zone %{dnsname}@%p for %x", zone->origin, zone, owner);
737 #endif
738 mutex_lock(&zone->lock_mutex);
739
740 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
741 struct zdb_zone_lock_monitor *holder = zdb_zone_lock_monitor_new(zone, owner, secondary_owner);
742 #endif
743
744 *current_ownerp = zone->lock_owner;
745 *current_reserved_ownerp = zone->lock_reserved_owner;
746
747 u8 so = zone->lock_reserved_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
748
749 if(so == ZDB_ZONE_MUTEX_NOBODY || so == secondary_owner)
750 {
751 u8 co = zone->lock_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
752
753 if(co == ZDB_ZONE_MUTEX_NOBODY || co == owner)
754 {
755 yassert(!SIGNED_VAR_VALUE_IS_MAX(zone->lock_count));
756
757 zone->lock_owner = owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
758 zone->lock_count++;
759 zone->lock_reserved_owner = secondary_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
760
761 #if ZDB_ZONE_LOCK_HAS_OWNER_ID
762 thread_t tid = thread_self();
763 zone->lock_last_owner_id = tid;
764 zone->lock_last_reserved_owner_id = tid;
765 #endif
766
767 #if ZONE_MUTEX_LOG
768 log_debug7("acquired lock for zone %{dnsname}@%p for %x (#%i)", zone->origin, zone, owner, zone->lock_count);
769 #endif
770
771 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
772 zdb_zone_lock_monitor_locks(holder);
773 #endif
774
775 mutex_unlock(&zone->lock_mutex);
776
777 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
778 zone->lock_trace = debug_stacktrace_get();
779 zone->lock_id = thread_self();
780 zone->lock_timestamp = timeus();
781
782 zdb_zone_lock_set_add(zone);
783 #endif
784 #if MUTEX_CONTENTION_MONITOR
785 mutex_contention_lock_end(mcm);
786 #endif
787
788 return TRUE;
789 }
790 }
791 /*
792 else
793 {
794 // already double-owned
795 }
796 */
797
798 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
799 zdb_zone_lock_monitor_cancels(holder);
800 #endif
801
802 mutex_unlock(&zone->lock_mutex);
803
804 #if ZONE_MUTEX_LOG
805 log_debug7("failed to acquire lock for zone %{dnsname}@%p for %x", zone->origin, zone, owner);
806 #endif
807
808 #if MUTEX_CONTENTION_MONITOR
809 mutex_contention_lock_fail(mcm);
810 #endif
811
812 return FALSE;
813 }
814
815 void
zdb_zone_double_unlock(zdb_zone * zone,u8 owner,u8 secondary_owner)816 zdb_zone_double_unlock(zdb_zone *zone, u8 owner, u8 secondary_owner)
817 {
818 #if ZONE_MUTEX_LOG
819 log_debug7("releasing lock for zone %{dnsname}@%p by %x and %x (owned by %x and %x)", zone->origin, zone, owner, secondary_owner, zone->lock_owner, zone->lock_reserved_owner);
820 #else
821 (void)owner;
822 (void)secondary_owner;
823 #endif
824
825 mutex_lock(&zone->lock_mutex);
826
827 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
828 struct zdb_zone_lock_monitor *holder = zdb_zone_lock_monitor_get(zone);
829 #endif
830
831 #if ZDB_ZONE_LOCK_HAS_OWNER_ID
832 thread_t tid = thread_self();
833 if(zone->lock_last_owner_id == tid)
834 {
835 zone->lock_last_owner_id = 0;
836 }
837 if(zone->lock_last_reserved_owner_id == tid)
838 {
839 zone->lock_last_reserved_owner_id = 0;
840 }
841 #endif
842
843 assert(zone->lock_reserved_owner == (secondary_owner & ZDB_ZONE_MUTEX_UNLOCKMASK_FLAG));
844 assert(zone->lock_owner == (owner & ZDB_ZONE_MUTEX_UNLOCKMASK_FLAG));
845
846 zone->lock_reserved_owner = ZDB_ZONE_MUTEX_NOBODY;
847
848 --zone->lock_count;
849
850 #if ZONE_MUTEX_LOG
851 log_debug7("released lock for zone %{dnsname}@%p by %x (#%i)", zone->origin, zone, owner, zone->lock_count);
852 #endif
853
854 yassert((zone->lock_owner & 0xc0) == 0);
855 // NO, because it does not always to a transfer lock yassert(zone->lock_count == 0);
856
857 if(zone->lock_count == 0)
858 {
859 zone->lock_owner = ZDB_ZONE_MUTEX_NOBODY;
860 cond_notify(&zone->lock_cond);
861 }
862
863 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
864 zdb_zone_lock_monitor_unlocks(holder);
865 #endif
866
867 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
868 zone->lock_trace = NULL;
869 zone->lock_id = 0;
870 zone->lock_timestamp = 0;
871
872 zdb_zone_lock_set_del(zone);
873 #endif
874
875 mutex_unlock(&zone->lock_mutex);
876
877 #if MUTEX_CONTENTION_MONITOR
878 mutex_contention_unlock(thread_self(), zone);
879 #endif
880 }
881
882 void
zdb_zone_exchange_locks(zdb_zone * zone,u8 owner,u8 secondary_owner)883 zdb_zone_exchange_locks(zdb_zone *zone, u8 owner, u8 secondary_owner)
884 {
885 #if ZONE_MUTEX_LOG
886 log_debug7("exchanging locks for zone %{dnsname}@%p from %x to %x (owned by %x:%x)", zone->origin, zone, owner, secondary_owner, zone->lock_owner, zone->lock_reserved_owner);
887 #endif
888
889 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
890 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
891 u64 start = timeus();
892 #endif
893 #endif
894
895 mutex_lock(&zone->lock_mutex);
896
897 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
898 struct zdb_zone_lock_monitor *holder = zdb_zone_lock_monitor_new(zone, owner, secondary_owner);
899 #endif
900
901 #if DEBUG
902 if((zone->lock_owner != (owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG)) || (zone->lock_count == 0))
903 {
904 yassert(zone->lock_owner == (owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG));
905 yassert(zone->lock_count != 0);
906 abort(); // unreachable
907 }
908
909 if(zone->lock_reserved_owner != (secondary_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG))
910 {
911 yassert(zone->lock_reserved_owner != (secondary_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG));
912 abort(); // unreachable
913 }
914 #endif
915
916 // wait to be the last one
917
918 while(zone->lock_count != 1)
919 {
920 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
921 zdb_zone_lock_monitor_waits(holder);
922 #endif
923
924 #if ZDB_HAS_OLD_MUTEX_DEBUG_SUPPORT
925 s64 d = timeus() - start;
926 if(d > MUTEX_WAITED_TOO_MUCH_TIME_US)
927 {
928 log_warn("zdb_zone_transfer_lock(%{dnsname},%x,%x) : waited for %llius already ...", zone->origin, owner, secondary_owner, d);
929 debug_log_stacktrace(MODULE_MSG_HANDLE, MSG_WARNING, "zdb_zone_double_lock:");
930 }
931 #endif
932 cond_timedwait(&zone->lock_cond, &zone->lock_mutex, 100);
933
934 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
935 zdb_zone_lock_monitor_resumes(holder);
936 #endif
937 }
938
939 zone->lock_owner = secondary_owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
940 zone->lock_reserved_owner = owner & ZDB_ZONE_MUTEX_LOCKMASK_FLAG;
941
942 #if ZONE_MUTEX_LOG
943 log_debug7("exchanged locks for zone %{dnsname}@%p from %x to %x (#%i)", zone->origin, zone, owner, secondary_owner, zone->lock_count);
944 #endif
945
946 if((secondary_owner & ZDB_ZONE_MUTEX_EXCLUSIVE_FLAG) == 0)
947 {
948 cond_notify(&zone->lock_cond);
949 }
950
951 #if ZDB_HAS_LOCK_DEBUG_SUPPORT
952 zdb_zone_lock_monitor_exchanges(holder);
953 zdb_zone_lock_monitor_release(holder);
954 #endif
955
956 #if DNSCORE_HAS_MUTEX_DEBUG_SUPPORT
957 zone->lock_trace = debug_stacktrace_get();
958 zone->lock_id = thread_self();
959 zone->lock_timestamp = timeus();
960
961 zdb_zone_lock_set_add(zone);
962 #endif
963
964 mutex_unlock(&zone->lock_mutex);
965 }
966
967 /** @} */
968