]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/lock.c
kes Rework the reservation system to take into account that the Director
[bacula/bacula] / bacula / src / stored / lock.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Collection of Bacula Storage daemon locking software
30  *
31  *  Kern Sibbald, 2000-2007.  June 2007
32  *
33  *   Version $Id$
34  */
35
36 #include "bacula.h"                   /* pull in global headers */
37 #include "stored.h"                   /* pull in Storage Deamon headers */
38
39 #ifdef SD_DEBUG_LOCK
40 const int dbglvl = 0;
41 #else
42 const int dbglvl = 500;
43 #endif
44
45
46 /*
47  *
48  * The Storage daemon has three locking concepts that must be
49  *   understood:
50  *
51  *  1. dblock    blocking the device, which means that the device
52  *               is "marked" in use.  When setting and removing the
53                  block, the device is locked, but after dblock is
54                  called the device is unlocked.
55  *  2. dlock()   simple mutex that locks the device structure. A dlock
56  *               can be acquired while a device is blocked if it is not
57  *               locked.      
58  *  3. r_dlock   "recursive" dlock, when means that a dlock (mutex)
59  *               will be acquired on the device if it is not blocked
60  *               by some other thread. If the device was blocked by
61  *               the current thread, it will acquire the lock.
62  *               If some other thread has set a block on the device,
63  *               this call will wait until the device is unblocked.
64  *
65  *  A lock is normally set when modifying the device structure.
66  *  A r_lock is normally acquired when you want to block the device
67  *    i.e. it will wait until the device is not blocked.
68  *  A block is normally set during long operations like writing to
69  *    the device.
70  *  If you are writing the device, you will normally block and 
71  *    lock it.
72  *  A lock cannot be violated. No other thread can touch the
73  *    device while a lock is set.  
74  *  When a block is set, every thread accept the thread that set
75  *    the block will block if r_dlock is called.
76  *  A device can be blocked for multiple reasons, labeling, writing,
77  *    acquiring (opening) the device, waiting for the operator, unmounted,
78  *    ...
79  *  Under certain conditions the block that is set on a device can be    
80  *    stolen and the device can be used by another thread. For example,
81  *    a device is blocked because it is waiting for the operator to  
82  *    mount a tape.  The operator can then unmount the device, and label
83  *    a tape, re-mount it, give back the block, and the job will continue.
84  *
85  *
86  * Functions:
87  *
88  *   DEVICE::dlock()   does P(m_mutex)     (in dev.h)
89  *   DEVICE::dunlock() does V(m_mutex)
90  *
91  *   DEVICE::r_dlock() does recursive locking
92  *                    dlock()
93  *                    if blocked and not same thread that locked
94  *                       pthread_cond_wait
95  *                    leaves device locked 
96  *
97  *   DEVICE::r_dunlock()
98  *                    same as dunlock();
99  *
100  *   DEVICE::dblock(why)  does 
101  *                    r_dlock();         (recursive device lock)
102  *                    block_device(this, why) 
103  *                    r_dunlock()
104  *
105  *   DEVICE::dunblock does
106  *                    dlock()
107  *                    unblock_device()
108  *                    dunlock()
109  *
110  *   block_device() does  (must be locked and not blocked at entry)  
111  *                    set blocked status
112  *                    set our pid
113  *
114  *   unblock_device() does (must be blocked at entry)
115  *                        (locked on entry)
116  *                        (locked on exit)
117  *                    set unblocked status
118  *                    clear pid
119  *                    if waiting threads
120  *                       pthread_cond_broadcast
121  *
122  *   steal_device_lock() does (must be locked and blocked at entry)
123  *                    save status
124  *                    set new blocked status
125  *                    set new pid
126  *                    unlock()
127  *
128  *   give_back_device_lock() does (must be blocked but not locked)
129  *                    dlock()
130  *                    reset blocked status
131  *                    save previous blocked
132  *                    reset pid
133  *                    if waiting threads
134  *                       pthread_cond_broadcast
135  *
136  */
137
138
139 void DEVICE::dblock(int why)
140 {
141    r_dlock();              /* need recursive lock to block */
142    block_device(this, why);
143    r_dunlock();
144 }
145
146 void DEVICE::dunblock(bool locked)
147 {
148    if (!locked) {
149       dlock();
150    }
151    unblock_device(this);
152    dunlock();
153 }
154
155
156 #ifdef SD_DEBUG_LOCK
157 void DEVICE::_dlock(const char *file, int line)
158 {
159    Dmsg4(sd_dbglvl, "dlock from %s:%d precnt=%d JobId=%u\n", file, line,
160          m_count, get_jobid_from_tid()); 
161    /* Note, this *really* should be protected by a mutex, but
162     *  since it is only debug code we don't worry too much.  
163     */
164    if (m_count > 0 && pthread_equal(m_pid, pthread_self())) {
165       Dmsg5(sd_dbglvl, "Possible DEADLOCK!! lock held by JobId=%u from %s:%d m_count=%d JobId=%u\n", 
166             get_jobid_from_tid(m_pid),
167             file, line, m_count, get_jobid_from_tid());
168    }
169    P(m_mutex);
170    m_pid = pthread_self();
171    m_count++; 
172 }
173
174 void DEVICE::_dunlock(const char *file, int line)
175 {
176    m_count--; 
177    Dmsg4(sd_dbglvl+1, "dunlock from %s:%d postcnt=%d JobId=%u\n", file, line,
178          m_count, get_jobid_from_tid()); 
179    V(m_mutex);   
180 }
181
182 void DEVICE::_r_dunlock(const char *file, int line)
183 {
184    this->_dunlock(file, line);
185 }
186
187 #endif
188
189
190 /*
191  * This is a recursive lock that checks if the device is blocked.
192  *
193  * When blocked is set, all threads EXCEPT thread with id no_wait_id
194  * must wait. The no_wait_id thread is out obtaining a new volume
195  * and preparing the label.
196  */
197 #ifdef SD_DEBUG_LOCK
198 void DEVICE::_r_dlock(const char *file, int line)
199 {
200    Dmsg4(sd_dbglvl+1, "r_dlock blked=%s from %s:%d JobId=%u\n", this->print_blocked(),
201          file, line, get_jobid_from_tid());
202 #else
203 void DEVICE::r_dlock()
204 {
205 #endif
206    int stat;
207    this->dlock();   
208    if (this->blocked() && !pthread_equal(this->no_wait_id, pthread_self())) {
209       this->num_waiting++;             /* indicate that I am waiting */
210       while (this->blocked()) {
211          Dmsg3(sd_dbglvl, "r_dlock blked=%s no_wait=%p me=%p\n", this->print_blocked(),
212                this->no_wait_id, pthread_self());
213          if ((stat = pthread_cond_wait(&this->wait, &m_mutex)) != 0) {
214             berrno be;
215             this->dunlock();
216             Emsg1(M_ABORT, 0, _("pthread_cond_wait failure. ERR=%s\n"),
217                be.bstrerror(stat));
218          }
219       }
220       this->num_waiting--;             /* no longer waiting */
221    }
222 }
223
224 /*
225  * Block all other threads from using the device
226  *  Device must already be locked.  After this call,
227  *  the device is blocked to any thread calling dev->r_lock(),
228  *  but the device is not locked (i.e. no P on device).  Also,
229  *  the current thread can do slip through the dev->r_lock()
230  *  calls without blocking.
231  */
232 void _block_device(const char *file, int line, DEVICE *dev, int state)
233 {
234    ASSERT(dev->blocked() == BST_NOT_BLOCKED);
235    dev->set_blocked(state);           /* make other threads wait */
236    dev->no_wait_id = pthread_self();  /* allow us to continue */
237    Dmsg3(sd_dbglvl, "set blocked=%s from %s:%d\n", dev->print_blocked(), file, line);
238 }
239
240 /*
241  * Unblock the device, and wake up anyone who went to sleep.
242  * Enter: device locked
243  * Exit:  device locked
244  */
245 void _unblock_device(const char *file, int line, DEVICE *dev)
246 {
247    Dmsg3(sd_dbglvl, "unblock %s from %s:%d\n", dev->print_blocked(), file, line);
248    ASSERT(dev->blocked());
249    dev->set_blocked(BST_NOT_BLOCKED);
250    dev->no_wait_id = 0;
251    if (dev->num_waiting > 0) {
252       pthread_cond_broadcast(&dev->wait); /* wake them up */
253    }
254 }
255
256 /*
257  * Enter with device locked and blocked
258  * Exit with device unlocked and blocked by us.
259  */
260 void _steal_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state)
261 {
262
263    Dmsg3(sd_dbglvl, "steal lock. old=%s from %s:%d\n", dev->print_blocked(),
264       file, line);
265    hold->dev_blocked = dev->blocked();
266    hold->dev_prev_blocked = dev->dev_prev_blocked;
267    hold->no_wait_id = dev->no_wait_id;
268    dev->set_blocked(state);
269    Dmsg1(sd_dbglvl, "steal lock. new=%s\n", dev->print_blocked());
270    dev->no_wait_id = pthread_self();
271    dev->dunlock();
272 }
273
274 /*
275  * Enter with device blocked by us but not locked
276  * Exit with device locked, and blocked by previous owner
277  */
278 void _give_back_device_lock(const char *file, int line, DEVICE *dev, bsteal_lock_t *hold)
279 {
280    Dmsg3(sd_dbglvl, "return lock. old=%s from %s:%d\n",
281       dev->print_blocked(), file, line);
282    dev->dlock();
283    dev->set_blocked(hold->dev_blocked);
284    dev->dev_prev_blocked = hold->dev_prev_blocked;
285    dev->no_wait_id = hold->no_wait_id;
286    Dmsg1(sd_dbglvl, "return lock. new=%s\n", dev->print_blocked());
287    if (dev->num_waiting > 0) {
288       pthread_cond_broadcast(&dev->wait); /* wake them up */
289    }
290 }
291
292 const char *DEVICE::print_blocked() const 
293 {
294    switch (m_blocked) {
295    case BST_NOT_BLOCKED:
296       return "BST_NOT_BLOCKED";
297    case BST_UNMOUNTED:
298       return "BST_UNMOUNTED";
299    case BST_WAITING_FOR_SYSOP:
300       return "BST_WAITING_FOR_SYSOP";
301    case BST_DOING_ACQUIRE:
302       return "BST_DOING_ACQUIRE";
303    case BST_WRITING_LABEL:
304       return "BST_WRITING_LABEL";
305    case BST_UNMOUNTED_WAITING_FOR_SYSOP:
306       return "BST_UNMOUNTED_WAITING_FOR_SYSOP";
307    case BST_MOUNT:
308       return "BST_MOUNT";
309    case BST_DESPOOLING:
310       return "BST_DESPOOLING";
311    default:
312       return _("unknown blocked code");
313    }
314 }
315
316
317 /*
318  * Check if the device is blocked or not
319  */
320 bool is_device_unmounted(DEVICE *dev)
321 {
322    bool stat;
323    int blocked = dev->blocked();
324    stat = (blocked == BST_UNMOUNTED) ||
325           (blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
326    return stat;
327 }