1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * Collection of Bacula Storage daemon locking software
21  *
22  *  Kern Sibbald, June 2007
23  *
24  */
25 
26 #include "bacula.h"                   /* pull in global headers */
27 #include "stored.h"                   /* pull in Storage Deamon headers */
28 
29 #ifdef SD_DEBUG_LOCK
30 const int dbglvl = DT_LOCK|30;
31 #else
32 const int dbglvl = DT_LOCK|50;
33 #endif
34 
35 
36 /*
37  *
38  * The Storage daemon has three locking concepts that must be
39  *   understood:
40  *
41  *  1. dblock    blocking the device, which means that the device
42  *               is "marked" in use.  When setting and removing the
43                  block, the device is locked, but after dblock is
44                  called the device is unlocked.
45  *  2. Lock()    simple mutex that locks the device structure. A Lock
46  *               can be acquired while a device is blocked if it is not
47  *               locked.
48  *  3. rLock(locked)  "recursive" Lock, when means that a Lock (mutex)
49  *               will be acquired on the device if it is not blocked
50  *               by some other thread. If the device was blocked by
51  *               the current thread, it will acquire the lock.
52  *               If some other thread has set a block on the device,
53  *               this call will wait until the device is unblocked.
54  *               Can be called with locked true, which means the
55  *               Lock is already set
56  *
57  *  A lock is normally set when modifying the device structure.
58  *  A rLock is normally acquired when you want to block the device
59  *    i.e. it will wait until the device is not blocked.
60  *  A block is normally set during long operations like writing to
61  *    the device.
62  *  If you are writing the device, you will normally block and
63  *    lock it.
64  *  A lock cannot be violated. No other thread can touch the
65  *    device while a lock is set.
66  *  When a block is set, every thread accept the thread that set
67  *    the block will block if rLock is called.
68  *  A device can be blocked for multiple reasons, labeling, writing,
69  *    acquiring (opening) the device, waiting for the operator, unmounted,
70  *    ...
71  *  Under certain conditions the block that is set on a device can be
72  *    stolen and the device can be used by another thread. For example,
73  *    a device is blocked because it is waiting for the operator to
74  *    mount a tape.  The operator can then unmount the device, and label
75  *    a tape, re-mount it, give back the block, and the job will continue.
76  *
77  *
78  * Functions:
79  *
80  *   DEVICE::Lock()   does P(m_mutex)     (in dev.h)
81  *   DEVICE::Unlock() does V(m_mutex)
82  *
83  *   DEVICE::rLock(locked) allows locking the device when this thread
84  *                     already has the device blocked.
85  *                    if (!locked)
86  *                       Lock()
87  *                    if blocked and not same thread that locked
88  *                       pthread_cond_wait
89  *                    leaves device locked
90  *
91  *   DEVICE::rUnlock() unlocks but does not unblock
92  *                    same as Unlock();
93  *
94  *   DEVICE::dblock(why)  does
95  *                    rLock();         (recursive device lock)
96  *                    block_device(this, why)
97  *                    rUnlock()
98  *
99  *   DEVICE::dunblock does
100  *                    Lock()
101  *                    unblock_device()
102  *                    Unlock()
103  *
104  *   block_device() does  (must be locked and not blocked at entry)
105  *                    set blocked status
106  *                    set our pid
107  *
108  *   unblock_device() does (must be blocked at entry)
109  *                        (locked on entry)
110  *                        (locked on exit)
111  *                    set unblocked status
112  *                    clear pid
113  *                    if waiting threads
114  *                       pthread_cond_broadcast
115  *
116  *   obtain_device_block() does (must be locked and blocked at entry)
117  *                    save status
118  *                    set new blocked status
119  *                    set new pid
120  *
121  *   give_back_device_block() does (must be blocked and locked)
122  *                    reset blocked status
123  *                    save previous blocked
124  *                    reset pid
125  *                    if waiting threads
126  *                       pthread_cond_broadcast
127  *
128  */
129 
dblock(int why)130 void DEVICE::dblock(int why)
131 {
132    rLock(false);              /* need recursive lock to block */
133    block_device(this, why);
134    rUnlock();
135 }
136 
dunblock(bool locked)137 void DEVICE::dunblock(bool locked)
138 {
139    if (!locked) {
140       Lock();
141    }
142    unblock_device(this);
143    Unlock();
144 }
145 
146 
147 
148 /*
149  * Debug DEVICE locks  N.B.
150  *
151  */
152 
153 #ifdef DEV_DEBUG_LOCK
154 
dbg_Lock(const char * file,int line)155 void DEVICE::dbg_Lock(const char *file, int line)
156 {
157    Dmsg4(sd_dbglvl, "Lock %s from %s:%d precnt=%d\n", device->hdr.name, file, line, m_count);
158    bthread_mutex_lock_p(&m_mutex, file, line);
159    m_pid = pthread_self();
160    m_count++;
161 }
162 
dbg_Unlock(const char * file,int line)163 void DEVICE::dbg_Unlock(const char *file, int line)
164 {
165    m_count--;
166    clear_thread_id(m_pid);
167    Dmsg4(sd_dbglvl, "Unlock %s from %s:%d postcnt=%d\n", device->hdr.name, file, line, m_count);
168    bthread_mutex_unlock_p(&m_mutex, file, line);
169 }
170 
dbg_rUnlock(const char * file,int line)171 void DEVICE::dbg_rUnlock(const char *file, int line)
172 {
173    Dmsg2(sd_dbglvl, "rUnlock from %s:%d\n", file, line);
174    dbg_Unlock(file, line);
175 }
176 
177 #else
178 
179 /*
180  * DEVICE locks  N.B.
181  *
182  */
183 
184 
rUnlock()185 void DEVICE::rUnlock()
186 {
187    Unlock();
188 }
189 
Lock()190 void DEVICE::Lock()
191 {
192    P(m_mutex);
193 }
194 
Unlock()195 void DEVICE::Unlock()
196 {
197    V(m_mutex);
198 }
199 
200 #endif /* DEV_DEBUG_LOCK */
201 
202 /*
203  * This is a recursive lock that checks if the device is blocked.
204  *
205  * When blocked is set, all threads EXCEPT thread with id no_wait_id
206  * must wait. The no_wait_id thread is out obtaining a new volume
207  * and preparing the label.
208  */
209 #ifdef DEV_DEBUG_LOCK
dbg_rLock(const char * file,int line,bool locked)210 void DEVICE::dbg_rLock(const char *file, int line, bool locked)
211 {
212    Dmsg3(sd_dbglvl, "Enter rLock blked=%s from %s:%d\n", print_blocked(),
213          file, line);
214    if (!locked) {
215       /* lockmgr version of P(m_mutex) */
216       Dmsg4(sd_dbglvl, "Lock %s in rLock %s from %s:%d\n",
217          device->hdr.name, print_blocked(), file, line);
218       bthread_mutex_lock_p(&m_mutex, file, line);
219       m_count++;
220    }
221 
222    if (blocked() && !pthread_equal(no_wait_id, pthread_self())) {
223       num_waiting++;             /* indicate that I am waiting */
224       while (blocked()) {
225          int stat;
226 #ifndef HAVE_WIN32
227          /* thread id on Win32 may be a struct */
228          Dmsg5(sd_dbglvl, "Blocked by %d %s in rLock blked=%s no_wait=%p me=%p\n",
229             blocked_by, device->hdr.name, print_blocked(), no_wait_id, pthread_self());
230 #endif
231          if ((stat = bthread_cond_wait_p(&this->wait, &m_mutex, file, line)) != 0) {
232             berrno be;
233             this->dbg_Unlock(file, line);
234             Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
235                be.bstrerror(stat));
236          }
237       }
238       num_waiting--;             /* no longer waiting */
239    }
240 }
241 #else /* DEV_DEBUG_LOCK */
242 
rLock(bool locked)243 void DEVICE::rLock(bool locked)
244 {
245    if (!locked) {
246       Lock();
247       m_count++;
248    }
249 
250    if (blocked() && !pthread_equal(no_wait_id, pthread_self())) {
251       num_waiting++;             /* indicate that I am waiting */
252       while (blocked()) {
253          int stat;
254 #ifndef HAVE_WIN32
255          /* thread id on Win32 may be a struct */
256          Dmsg5(sd_dbglvl, "Blocked by %d rLock %s blked=%s no_wait=%p me=%p\n",
257             blocked_by, device->hdr.name, print_blocked(), no_wait_id, pthread_self());
258 #endif
259          if ((stat = pthread_cond_wait(&this->wait, &m_mutex)) != 0) {
260             berrno be;
261             this->Unlock();
262             Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
263                be.bstrerror(stat));
264          }
265       }
266       num_waiting--;             /* no longer waiting */
267    }
268 }
269 
270 #endif  /* DEV_DEBUG_LOCK */
271 
272 #ifdef SD_DEBUG_LOCK
273 
dbg_Lock_acquire(const char * file,int line)274 void DEVICE::dbg_Lock_acquire(const char *file, int line)
275 {
276    Dmsg2(sd_dbglvl, "Lock_acquire from %s:%d\n", file, line);
277    bthread_mutex_lock_p(&acquire_mutex, file, line);
278 }
279 
dbg_Unlock_acquire(const char * file,int line)280 void DEVICE::dbg_Unlock_acquire(const char *file, int line)
281 {
282    Dmsg2(sd_dbglvl, "Unlock_acquire from %s:%d\n", file, line);
283    bthread_mutex_unlock_p(&acquire_mutex, file, line);
284 }
285 
dbg_Lock_read_acquire(const char * file,int line)286 void DEVICE::dbg_Lock_read_acquire(const char *file, int line)
287 {
288    Dmsg2(sd_dbglvl, "Lock_read_acquire from %s:%d\n", file, line);
289    bthread_mutex_lock_p(&read_acquire_mutex, file, line);
290 }
291 
dbg_Unlock_read_acquire(const char * file,int line)292 void DEVICE::dbg_Unlock_read_acquire(const char *file, int line)
293 {
294    Dmsg2(sd_dbglvl, "Unlock_read_acquire from %s:%d\n", file, line);
295    bthread_mutex_unlock_p(&read_acquire_mutex, file, line);
296 }
297 
dbg_Lock_VolCatInfo(const char * file,int line)298 void DEVICE::dbg_Lock_VolCatInfo(const char *file, int line)
299 {
300    bthread_mutex_lock_p(&volcat_mutex, file, line);
301 }
302 
dbg_Unlock_VolCatInfo(const char * file,int line)303 void DEVICE::dbg_Unlock_VolCatInfo(const char *file, int line)
304 {
305    bthread_mutex_unlock_p(&volcat_mutex, file, line);
306 }
307 
308 #else
309 
Lock_acquire()310 void DEVICE::Lock_acquire()
311 {
312    P(acquire_mutex);
313 }
314 
Unlock_acquire()315 void DEVICE::Unlock_acquire()
316 {
317    V(acquire_mutex);
318 }
319 
Lock_read_acquire()320 void DEVICE::Lock_read_acquire()
321 {
322    P(read_acquire_mutex);
323 }
324 
Unlock_read_acquire()325 void DEVICE::Unlock_read_acquire()
326 {
327    V(read_acquire_mutex);
328 }
329 
Lock_VolCatInfo()330 void DEVICE::Lock_VolCatInfo()
331 {
332    P(volcat_mutex);
333 }
334 
Unlock_VolCatInfo()335 void DEVICE::Unlock_VolCatInfo()
336 {
337    V(volcat_mutex);
338 }
339 
340 
341 
342 #endif
343 
344 /* Main device access control */
init_mutex()345 int DEVICE::init_mutex()
346 {
347    return pthread_mutex_init(&m_mutex, NULL);
348 }
349 
350 /* Mutex around the freespace command */
init_freespace_mutex()351 int DEVICE::init_freespace_mutex()
352 {
353    return pthread_mutex_init(&freespace_mutex, NULL);
354 }
355 
356 /* Write device acquire mutex */
init_acquire_mutex()357 int DEVICE::init_acquire_mutex()
358 {
359    return pthread_mutex_init(&acquire_mutex, NULL);
360 }
361 
362 /* Read device acquire mutex */
init_read_acquire_mutex()363 int DEVICE::init_read_acquire_mutex()
364 {
365    return pthread_mutex_init(&read_acquire_mutex, NULL);
366 }
367 
368 /* VolCatInfo mutex */
init_volcat_mutex()369 int DEVICE::init_volcat_mutex()
370 {
371    return pthread_mutex_init(&volcat_mutex, NULL);
372 }
373 
374 /* dcrs mutex */
init_dcrs_mutex()375 int DEVICE::init_dcrs_mutex()
376 {
377    return pthread_mutex_init(&dcrs_mutex, NULL);
378 }
379 
380 /* Set order in which device locks must be acquired */
set_mutex_priorities()381 void DEVICE::set_mutex_priorities()
382 {
383    /* Ensure that we respect this order in P/V operations */
384    bthread_mutex_set_priority(&m_mutex,       PRIO_SD_DEV_ACCESS);
385    bthread_mutex_set_priority(&spool_mutex,   PRIO_SD_DEV_SPOOL);
386    bthread_mutex_set_priority(&acquire_mutex, PRIO_SD_DEV_ACQUIRE);
387 }
388 
next_vol_timedwait(const struct timespec * timeout)389 int DEVICE::next_vol_timedwait(const struct timespec *timeout)
390 {
391    return pthread_cond_timedwait(&wait_next_vol, &m_mutex, timeout);
392 }
393 
394 
395 /*
396  * Block all other threads from using the device
397  *  Device must already be locked.  After this call,
398  *  the device is blocked to any thread calling dev->rLock(),
399  *  but the device is not locked (i.e. no P on device).  Also,
400  *  the current thread can do slip through the dev->rLock()
401  *  calls without blocking.
402  */
_block_device(const char * file,int line,DEVICE * dev,int state)403 void _block_device(const char *file, int line, DEVICE *dev, int state)
404 {
405    ASSERT2(dev->blocked() == BST_NOT_BLOCKED, "Block request of device already blocked");
406    dev->set_blocked(state);           /* make other threads wait */
407    dev->no_wait_id = pthread_self();  /* allow us to continue */
408    dev->blocked_by = get_jobid_from_tsd();
409    Dmsg4(sd_dbglvl, "Blocked %s %s from %s:%d\n",
410       dev->device->hdr.name, dev->print_blocked(), file, line);
411 }
412 
413 /*
414  * Unblock the device, and wake up anyone who went to sleep.
415  * Enter: device locked
416  * Exit:  device locked
417  */
_unblock_device(const char * file,int line,DEVICE * dev)418 void _unblock_device(const char *file, int line, DEVICE *dev)
419 {
420    Dmsg4(sd_dbglvl, "Unblocked %s %s from %s:%d\n", dev->device->hdr.name,
421       dev->print_blocked(), file, line);
422    ASSERT2(dev->blocked(), "Unblock request of device not blocked");
423    dev->set_blocked(BST_NOT_BLOCKED);
424    dev->blocked_by = 0;
425    clear_thread_id(dev->no_wait_id);
426    if (dev->num_waiting > 0) {
427       pthread_cond_broadcast(&dev->wait); /* wake them up */
428    }
429 }
430 
431 static pthread_mutex_t block_mutex = PTHREAD_MUTEX_INITIALIZER;
432 /*
433  * Enter and leave with device locked
434  *
435  * Note: actually this routine:
436  *   returns true if it can either set or steal the device block
437  *   returns false if it cannot block the device
438  */
_obtain_device_block(const char * file,int line,bsteal_lock_t * hold,int retry,int state)439 bool DEVICE::_obtain_device_block(const char *file, int line,
440                                   bsteal_lock_t *hold, int retry, int state)
441 {
442    int ret;
443    int r = retry;
444 
445    if (!can_obtain_block() && !pthread_equal(no_wait_id, pthread_self())) {
446       num_waiting++;             /* indicate that I am waiting */
447       while ((retry == 0 || r-- > 0) && !can_obtain_block()) {
448          if ((ret = bthread_cond_wait_p(&wait, &m_mutex, file, line)) != 0) {
449             berrno be;
450             Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
451                   be.bstrerror(ret));
452          }
453       }
454       num_waiting--;             /* no longer waiting */
455    }
456 
457    P(block_mutex);
458    Dmsg4(sd_dbglvl, "Steal lock %s old=%s from %s:%d\n",
459       device->hdr.name, print_blocked(), file, line);
460 
461    if (!can_obtain_block() && !pthread_equal(no_wait_id, pthread_self())) {
462       V(block_mutex);
463       return false;
464    }
465    hold->dev_blocked = blocked();
466    hold->dev_prev_blocked = dev_prev_blocked;
467    hold->no_wait_id = no_wait_id;
468    hold->blocked_by = blocked_by;
469    set_blocked(state);
470    Dmsg1(sd_dbglvl, "steal block. new=%s\n", print_blocked());
471    no_wait_id = pthread_self();
472    blocked_by = get_jobid_from_tsd();
473    V(block_mutex);
474    return true;
475 }
476 
477 /*
478  * Enter with device blocked and locked by us
479  * Exit with device locked, and blocked by previous owner
480  */
_give_back_device_block(const char * file,int line,DEVICE * dev,bsteal_lock_t * hold)481 void _give_back_device_block(const char *file, int line,
482                              DEVICE *dev, bsteal_lock_t *hold)
483 {
484    Dmsg4(sd_dbglvl, "Return lock %s old=%s from %s:%d\n",
485       dev->device->hdr.name, dev->print_blocked(), file, line);
486    P(block_mutex);
487    dev->set_blocked(hold->dev_blocked);
488    dev->dev_prev_blocked = hold->dev_prev_blocked;
489    dev->no_wait_id = hold->no_wait_id;
490    dev->blocked_by = hold->blocked_by;
491    Dmsg1(sd_dbglvl, "return lock. new=%s\n", dev->print_blocked());
492    if (dev->num_waiting > 0) {
493       pthread_cond_broadcast(&dev->wait); /* wake them up */
494    }
495    V(block_mutex);
496 }
497 
print_blocked()498 const char *DEVICE::print_blocked() const
499 {
500    switch (m_blocked) {
501    case BST_NOT_BLOCKED:
502       return "BST_NOT_BLOCKED";
503    case BST_UNMOUNTED:
504       return "BST_UNMOUNTED";
505    case BST_WAITING_FOR_SYSOP:
506       return "BST_WAITING_FOR_SYSOP";
507    case BST_DOING_ACQUIRE:
508       return "BST_DOING_ACQUIRE";
509    case BST_WRITING_LABEL:
510       return "BST_WRITING_LABEL";
511    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
512       return "BST_UNMOUNTED_WAITING_FOR_SYSOP";
513    case BST_MOUNT:
514       return "BST_MOUNT";
515    case BST_DESPOOLING:
516       return "BST_DESPOOLING";
517    case BST_RELEASING:
518       return "BST_RELEASING";
519    default:
520       return _("unknown blocked code");
521    }
522 }
523 
524 
525 /*
526  * Check if the device is blocked or not
527  */
is_device_unmounted()528 bool DEVICE::is_device_unmounted()
529 {
530    bool stat;
531 
532    int blk = blocked();
533    stat = (blk == BST_UNMOUNTED) ||
534           (blk == BST_UNMOUNTED_WAITING_FOR_SYSOP);
535    return stat;
536 }
537