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