e6918515f5696cb126e38f2a0dc52ed51d94f983
[dragonfly.git] / sys / vfs / hammer / hammer_subs.c
1 /*
2  * Copyright (c) 2007-2008 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  * $DragonFly: src/sys/vfs/hammer/hammer_subs.c,v 1.17 2008/05/05 20:34:48 dillon Exp $
35  */
36 /*
37  * HAMMER structural locking
38  */
39
40 #include "hammer.h"
41 #include <sys/dirent.h>
42
43 void
44 hammer_lock_ex(struct hammer_lock *lock)
45 {
46         thread_t td = curthread;
47
48         KKASSERT(lock->refs > 0);
49         crit_enter();
50         if (lock->locktd != td) {
51                 while (lock->locktd != NULL || lock->lockcount) {
52                         lock->wanted = 1;
53                         if (hammer_debug_locks) {
54                                 kprintf("hammer_lock_ex: held by %p\n",
55                                         lock->locktd);
56                         }
57                         ++hammer_contention_count;
58                         tsleep(lock, 0, "hmrlck", 0);
59                         if (hammer_debug_locks)
60                                 kprintf("hammer_lock_ex: try again\n");
61                 }
62                 lock->locktd = td;
63         }
64         KKASSERT(lock->lockcount >= 0);
65         ++lock->lockcount;
66         crit_exit();
67 }
68
69 /*
70  * Try to obtain an exclusive lock
71  */
72 int
73 hammer_lock_ex_try(struct hammer_lock *lock)
74 {
75         thread_t td = curthread;
76
77         KKASSERT(lock->refs > 0);
78         crit_enter();
79         if (lock->locktd != td) {
80                 if (lock->locktd != NULL || lock->lockcount) {
81                         crit_exit();
82                         return(EAGAIN);
83                 }
84                 lock->locktd = td;
85         }
86         KKASSERT(lock->lockcount >= 0);
87         ++lock->lockcount;
88         crit_exit();
89         return(0);
90 }
91
92 void
93 hammer_lock_sh(struct hammer_lock *lock)
94 {
95         KKASSERT(lock->refs > 0);
96         crit_enter();
97         while (lock->locktd != NULL) {
98                 if (lock->locktd == curthread) {
99                         Debugger("hammer_lock_sh: lock_sh on exclusive");
100                         ++lock->lockcount;
101                         crit_exit();
102                         return;
103                 }
104                 lock->wanted = 1;
105                 tsleep(lock, 0, "hmrlck", 0);
106         }
107         KKASSERT(lock->lockcount <= 0);
108         --lock->lockcount;
109         crit_exit();
110 }
111
112 /*
113  * Upgrade a shared lock to an exclusively held lock.  This function will
114  * return EDEADLK If there is more then one shared holder.
115  *
116  * No error occurs and no action is taken if the lock is already exclusively
117  * held by the caller.  If the lock is not held at all or held exclusively
118  * by someone else, this function will panic.
119  */
120 int
121 hammer_lock_upgrade(struct hammer_lock *lock)
122 {
123         int error;
124
125         crit_enter();
126         if (lock->lockcount > 0) {
127                 if (lock->locktd != curthread)
128                         panic("hammer_lock_upgrade: illegal lock state");
129                 error = 0;
130         } else if (lock->lockcount == -1) {
131                 lock->lockcount = 1;
132                 lock->locktd = curthread;
133                 error = 0;
134         } else if (lock->lockcount != 0) {
135                 error = EDEADLK;
136         } else {
137                 panic("hammer_lock_upgrade: lock is not held");
138                 /* NOT REACHED */
139                 error = 0;
140         }
141         crit_exit();
142         return(error);
143 }
144
145 /*
146  * Downgrade an exclusively held lock to a shared lock.
147  */
148 void
149 hammer_lock_downgrade(struct hammer_lock *lock)
150 {
151         KKASSERT(lock->lockcount == 1 && lock->locktd == curthread);
152         crit_enter();
153         lock->lockcount = -1;
154         lock->locktd = NULL;
155         if (lock->wanted) {
156                 lock->wanted = 0;
157                 wakeup(lock);
158         }
159         crit_exit();
160         /* XXX memory barrier */
161 }
162
163 void
164 hammer_unlock(struct hammer_lock *lock)
165 {
166         crit_enter();
167         KKASSERT(lock->lockcount != 0);
168         if (lock->lockcount < 0) {
169                 if (++lock->lockcount == 0 && lock->wanted) {
170                         lock->wanted = 0;
171                         wakeup(lock);
172                 }
173         } else {
174                 KKASSERT(lock->locktd == curthread);
175                 if (--lock->lockcount == 0) {
176                         lock->locktd = NULL;
177                         if (lock->wanted) {
178                                 lock->wanted = 0;
179                                 wakeup(lock);
180                         }
181                 }
182
183         }
184         crit_exit();
185 }
186
187 void
188 hammer_ref(struct hammer_lock *lock)
189 {
190         KKASSERT(lock->refs >= 0);
191         crit_enter();
192         ++lock->refs;
193         crit_exit();
194 }
195
196 void
197 hammer_unref(struct hammer_lock *lock)
198 {
199         KKASSERT(lock->refs > 0);
200         crit_enter();
201         --lock->refs;
202         crit_exit();
203 }
204
205 u_int32_t
206 hammer_to_unix_xid(uuid_t *uuid)
207 {
208         return(*(u_int32_t *)&uuid->node[2]);
209 }
210
211 void
212 hammer_guid_to_uuid(uuid_t *uuid, u_int32_t guid)
213 {
214         bzero(uuid, sizeof(*uuid));
215         *(u_int32_t *)&uuid->node[2] = guid;
216 }
217
218 void
219 hammer_to_timespec(hammer_tid_t tid, struct timespec *ts)
220 {
221         ts->tv_sec = tid / 1000000000;
222         ts->tv_nsec = tid % 1000000000;
223 }
224
225 hammer_tid_t
226 hammer_timespec_to_transid(struct timespec *ts)
227 {
228         hammer_tid_t tid;
229
230         tid = ts->tv_nsec + (unsigned long)ts->tv_sec * 1000000000LL;
231         return(tid);
232 }
233
234
235 /*
236  * Convert a HAMMER filesystem object type to a vnode type
237  */
238 enum vtype
239 hammer_get_vnode_type(u_int8_t obj_type)
240 {
241         switch(obj_type) {
242         case HAMMER_OBJTYPE_DIRECTORY:
243                 return(VDIR);
244         case HAMMER_OBJTYPE_REGFILE:
245                 return(VREG);
246         case HAMMER_OBJTYPE_DBFILE:
247                 return(VDATABASE);
248         case HAMMER_OBJTYPE_FIFO:
249                 return(VFIFO);
250         case HAMMER_OBJTYPE_CDEV:
251                 return(VCHR);
252         case HAMMER_OBJTYPE_BDEV:
253                 return(VBLK);
254         case HAMMER_OBJTYPE_SOFTLINK:
255                 return(VLNK);
256         default:
257                 return(VBAD);
258         }
259         /* not reached */
260 }
261
262 int
263 hammer_get_dtype(u_int8_t obj_type)
264 {
265         switch(obj_type) {
266         case HAMMER_OBJTYPE_DIRECTORY:
267                 return(DT_DIR);
268         case HAMMER_OBJTYPE_REGFILE:
269                 return(DT_REG);
270         case HAMMER_OBJTYPE_DBFILE:
271                 return(DT_DBF);
272         case HAMMER_OBJTYPE_FIFO:
273                 return(DT_FIFO);
274         case HAMMER_OBJTYPE_CDEV:
275                 return(DT_CHR);
276         case HAMMER_OBJTYPE_BDEV:
277                 return(DT_BLK);
278         case HAMMER_OBJTYPE_SOFTLINK:
279                 return(DT_LNK);
280         default:
281                 return(DT_UNKNOWN);
282         }
283         /* not reached */
284 }
285
286 u_int8_t
287 hammer_get_obj_type(enum vtype vtype)
288 {
289         switch(vtype) {
290         case VDIR:
291                 return(HAMMER_OBJTYPE_DIRECTORY);
292         case VREG:
293                 return(HAMMER_OBJTYPE_REGFILE);
294         case VDATABASE:
295                 return(HAMMER_OBJTYPE_DBFILE);
296         case VFIFO:
297                 return(HAMMER_OBJTYPE_FIFO);
298         case VCHR:
299                 return(HAMMER_OBJTYPE_CDEV);
300         case VBLK:
301                 return(HAMMER_OBJTYPE_BDEV);
302         case VLNK:
303                 return(HAMMER_OBJTYPE_SOFTLINK);
304         default:
305                 return(HAMMER_OBJTYPE_UNKNOWN);
306         }
307         /* not reached */
308 }
309
310 /*
311  * Return a namekey hash.   The 64 bit namekey hash consists of a 32 bit
312  * crc in the MSB and 0 in the LSB.  The caller will use the low bits to
313  * generate a unique key and will scan all entries with the same upper
314  * 32 bits when issuing a lookup.
315  *
316  * We strip bit 63 in order to provide a positive key, this way a seek
317  * offset of 0 will represent the base of the directory.
318  *
319  * This function can never return 0.  We use the MSB-0 space to synthesize
320  * artificial directory entries such as "." and "..".
321  */
322 int64_t
323 hammer_directory_namekey(void *name, int len)
324 {
325         int64_t key;
326
327         key = (int64_t)(crc32(name, len) & 0x7FFFFFFF) << 32;
328         if (key == 0)
329                 key |= 0x100000000LL;
330         return(key);
331 }
332
333 hammer_tid_t
334 hammer_now_tid(void)
335 {
336         struct timespec ts;
337         hammer_tid_t tid;
338
339         getnanotime(&ts);
340         tid = ts.tv_sec * 1000000000LL + ts.tv_nsec;
341         return(tid);
342 }
343
344 hammer_tid_t
345 hammer_str_to_tid(const char *str)
346 {
347         hammer_tid_t tid;
348         int len = strlen(str);
349
350         if (len > 10)
351                 tid = strtouq(str, NULL, 0);                    /* full TID */
352         else
353                 tid = strtouq(str, NULL, 0) * 1000000000LL;     /* time_t */
354         return(tid);
355 }
356
357 void
358 hammer_crc_set_blockmap(hammer_blockmap_t blockmap)
359 {
360         blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE);
361 }
362
363 void
364 hammer_crc_set_volume(hammer_volume_ondisk_t ondisk)
365 {
366         ondisk->vol_crc = crc32(ondisk, HAMMER_VOL_CRCSIZE1) ^
367                           crc32(&ondisk->vol_crc + 1, HAMMER_VOL_CRCSIZE2);
368 }
369
370 int
371 hammer_crc_test_blockmap(hammer_blockmap_t blockmap)
372 {
373         hammer_crc_t crc;
374
375         crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE);
376         return (blockmap->entry_crc == crc);
377 }
378
379 int
380 hammer_crc_test_volume(hammer_volume_ondisk_t ondisk)
381 {
382         hammer_crc_t crc;
383
384         crc = crc32(ondisk, HAMMER_VOL_CRCSIZE1) ^
385               crc32(&ondisk->vol_crc + 1, HAMMER_VOL_CRCSIZE2);
386         return (ondisk->vol_crc == crc);
387 }
388
389 int
390 hammer_crc_test_record(hammer_record_ondisk_t ondisk)
391 {
392         hammer_crc_t crc;
393
394         crc = crc32(&ondisk->base.rec_crc + 1, HAMMER_RECORD_CRCSIZE);
395         return (ondisk->base.rec_crc == crc &&
396                 ondisk->base.signature == HAMMER_RECORD_SIGNATURE_GOOD);
397 }
398
399 int
400 hammer_crc_test_btree(hammer_node_ondisk_t ondisk)
401 {
402         hammer_crc_t crc;
403
404         crc = crc32(&ondisk->crc + 1, HAMMER_BTREE_CRCSIZE);
405         return (ondisk->crc == crc);
406 }
407