HAMMER 56A/Many: Performance tuning - MEDIA STRUCTURES CHANGED!
[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.24 2008/06/10 22:30:21 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_ident(struct hammer_lock *lock, const char *ident)
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, ident, 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 int
113 hammer_lock_sh_try(struct hammer_lock *lock)
114 {
115         KKASSERT(lock->refs > 0);
116         crit_enter();
117         if (lock->locktd) {
118                 crit_exit();
119                 return(EAGAIN);
120         }
121         KKASSERT(lock->lockcount <= 0);
122         --lock->lockcount;
123         crit_exit();
124         return(0);
125 }
126
127 /*
128  * Upgrade a shared lock to an exclusively held lock.  This function will
129  * return EDEADLK If there is more then one shared holder.
130  *
131  * No error occurs and no action is taken if the lock is already exclusively
132  * held by the caller.  If the lock is not held at all or held exclusively
133  * by someone else, this function will panic.
134  */
135 int
136 hammer_lock_upgrade(struct hammer_lock *lock)
137 {
138         int error;
139
140         crit_enter();
141         if (lock->lockcount > 0) {
142                 if (lock->locktd != curthread)
143                         panic("hammer_lock_upgrade: illegal lock state");
144                 error = 0;
145         } else if (lock->lockcount == -1) {
146                 lock->lockcount = 1;
147                 lock->locktd = curthread;
148                 error = 0;
149         } else if (lock->lockcount != 0) {
150                 error = EDEADLK;
151         } else {
152                 panic("hammer_lock_upgrade: lock is not held");
153                 /* NOT REACHED */
154                 error = 0;
155         }
156         crit_exit();
157         return(error);
158 }
159
160 /*
161  * Downgrade an exclusively held lock to a shared lock.
162  */
163 void
164 hammer_lock_downgrade(struct hammer_lock *lock)
165 {
166         KKASSERT(lock->lockcount == 1 && lock->locktd == curthread);
167         crit_enter();
168         lock->lockcount = -1;
169         lock->locktd = NULL;
170         if (lock->wanted) {
171                 lock->wanted = 0;
172                 wakeup(lock);
173         }
174         crit_exit();
175         /* XXX memory barrier */
176 }
177
178 void
179 hammer_unlock(struct hammer_lock *lock)
180 {
181         crit_enter();
182         KKASSERT(lock->lockcount != 0);
183         if (lock->lockcount < 0) {
184                 if (++lock->lockcount == 0 && lock->wanted) {
185                         lock->wanted = 0;
186                         wakeup(lock);
187                 }
188         } else {
189                 KKASSERT(lock->locktd == curthread);
190                 if (--lock->lockcount == 0) {
191                         lock->locktd = NULL;
192                         if (lock->wanted) {
193                                 lock->wanted = 0;
194                                 wakeup(lock);
195                         }
196                 }
197
198         }
199         crit_exit();
200 }
201
202 void
203 hammer_ref(struct hammer_lock *lock)
204 {
205         KKASSERT(lock->refs >= 0);
206         crit_enter();
207         ++lock->refs;
208         crit_exit();
209 }
210
211 void
212 hammer_unref(struct hammer_lock *lock)
213 {
214         KKASSERT(lock->refs > 0);
215         crit_enter();
216         --lock->refs;
217         crit_exit();
218 }
219
220 /*
221  * The sync_lock must be held when doing any modifying operations on
222  * meta-data.  The flusher holds the lock exclusively while the reblocker
223  * and pruner use a shared lock.
224  *
225  * Modifying operations can run in parallel until the flusher needs to
226  * sync the disk media.
227  */
228 void
229 hammer_sync_lock_ex(hammer_transaction_t trans)
230 {
231         ++trans->sync_lock_refs;
232         hammer_lock_ex(&trans->hmp->sync_lock);
233 }
234
235 void
236 hammer_sync_lock_sh(hammer_transaction_t trans)
237 {
238         ++trans->sync_lock_refs;
239         hammer_lock_sh(&trans->hmp->sync_lock);
240 }
241
242 int
243 hammer_sync_lock_sh_try(hammer_transaction_t trans)
244 {
245         int error;
246
247         ++trans->sync_lock_refs;
248         if ((error = hammer_lock_sh_try(&trans->hmp->sync_lock)) != 0)
249                 --trans->sync_lock_refs;
250         return (error);
251 }
252
253 void
254 hammer_sync_unlock(hammer_transaction_t trans)
255 {
256         --trans->sync_lock_refs;
257         hammer_unlock(&trans->hmp->sync_lock);
258 }
259
260 /*
261  * Misc
262  */
263 u_int32_t
264 hammer_to_unix_xid(uuid_t *uuid)
265 {
266         return(*(u_int32_t *)&uuid->node[2]);
267 }
268
269 void
270 hammer_guid_to_uuid(uuid_t *uuid, u_int32_t guid)
271 {
272         bzero(uuid, sizeof(*uuid));
273         *(u_int32_t *)&uuid->node[2] = guid;
274 }
275
276 void
277 hammer_to_timespec(hammer_tid_t tid, struct timespec *ts)
278 {
279         ts->tv_sec = tid / 1000000000;
280         ts->tv_nsec = tid % 1000000000;
281 }
282
283 hammer_tid_t
284 hammer_timespec_to_transid(struct timespec *ts)
285 {
286         hammer_tid_t tid;
287
288         tid = ts->tv_nsec + (unsigned long)ts->tv_sec * 1000000000LL;
289         return(tid);
290 }
291
292
293 /*
294  * Convert a HAMMER filesystem object type to a vnode type
295  */
296 enum vtype
297 hammer_get_vnode_type(u_int8_t obj_type)
298 {
299         switch(obj_type) {
300         case HAMMER_OBJTYPE_DIRECTORY:
301                 return(VDIR);
302         case HAMMER_OBJTYPE_REGFILE:
303                 return(VREG);
304         case HAMMER_OBJTYPE_DBFILE:
305                 return(VDATABASE);
306         case HAMMER_OBJTYPE_FIFO:
307                 return(VFIFO);
308         case HAMMER_OBJTYPE_CDEV:
309                 return(VCHR);
310         case HAMMER_OBJTYPE_BDEV:
311                 return(VBLK);
312         case HAMMER_OBJTYPE_SOFTLINK:
313                 return(VLNK);
314         default:
315                 return(VBAD);
316         }
317         /* not reached */
318 }
319
320 int
321 hammer_get_dtype(u_int8_t obj_type)
322 {
323         switch(obj_type) {
324         case HAMMER_OBJTYPE_DIRECTORY:
325                 return(DT_DIR);
326         case HAMMER_OBJTYPE_REGFILE:
327                 return(DT_REG);
328         case HAMMER_OBJTYPE_DBFILE:
329                 return(DT_DBF);
330         case HAMMER_OBJTYPE_FIFO:
331                 return(DT_FIFO);
332         case HAMMER_OBJTYPE_CDEV:
333                 return(DT_CHR);
334         case HAMMER_OBJTYPE_BDEV:
335                 return(DT_BLK);
336         case HAMMER_OBJTYPE_SOFTLINK:
337                 return(DT_LNK);
338         default:
339                 return(DT_UNKNOWN);
340         }
341         /* not reached */
342 }
343
344 u_int8_t
345 hammer_get_obj_type(enum vtype vtype)
346 {
347         switch(vtype) {
348         case VDIR:
349                 return(HAMMER_OBJTYPE_DIRECTORY);
350         case VREG:
351                 return(HAMMER_OBJTYPE_REGFILE);
352         case VDATABASE:
353                 return(HAMMER_OBJTYPE_DBFILE);
354         case VFIFO:
355                 return(HAMMER_OBJTYPE_FIFO);
356         case VCHR:
357                 return(HAMMER_OBJTYPE_CDEV);
358         case VBLK:
359                 return(HAMMER_OBJTYPE_BDEV);
360         case VLNK:
361                 return(HAMMER_OBJTYPE_SOFTLINK);
362         default:
363                 return(HAMMER_OBJTYPE_UNKNOWN);
364         }
365         /* not reached */
366 }
367
368 int
369 hammer_nohistory(hammer_inode_t ip)
370 {
371         if (ip->hmp->hflags & HMNT_NOHISTORY)
372                 return(1);
373         if (ip->ino_data.uflags & (SF_NOHISTORY|UF_NOHISTORY))
374                 return(1);
375         return(0);
376 }
377
378 /*
379  * Return a namekey hash.   The 64 bit namekey hash consists of a 32 bit
380  * crc in the MSB and 0 in the LSB.  The caller will use the low bits to
381  * generate a unique key and will scan all entries with the same upper
382  * 32 bits when issuing a lookup.
383  *
384  * We strip bit 63 in order to provide a positive key, this way a seek
385  * offset of 0 will represent the base of the directory.
386  *
387  * This function can never return 0.  We use the MSB-0 space to synthesize
388  * artificial directory entries such as "." and "..".
389  */
390 int64_t
391 hammer_directory_namekey(void *name, int len)
392 {
393         int64_t key;
394
395         key = (int64_t)(crc32(name, len) & 0x7FFFFFFF) << 32;
396         if (key == 0)
397                 key |= 0x100000000LL;
398         return(key);
399 }
400
401 hammer_tid_t
402 hammer_now_tid(void)
403 {
404         struct timespec ts;
405         hammer_tid_t tid;
406
407         getnanotime(&ts);
408         tid = ts.tv_sec * 1000000000LL + ts.tv_nsec;
409         return(tid);
410 }
411
412 hammer_tid_t
413 hammer_str_to_tid(const char *str)
414 {
415         hammer_tid_t tid;
416         int len = strlen(str);
417
418         if (len > 10)
419                 tid = strtouq(str, NULL, 0);                    /* full TID */
420         else
421                 tid = strtouq(str, NULL, 0) * 1000000000LL;     /* time_t */
422         return(tid);
423 }
424
425 void
426 hammer_crc_set_blockmap(hammer_blockmap_t blockmap)
427 {
428         blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE);
429 }
430
431 void
432 hammer_crc_set_volume(hammer_volume_ondisk_t ondisk)
433 {
434         ondisk->vol_crc = crc32(ondisk, HAMMER_VOL_CRCSIZE1) ^
435                           crc32(&ondisk->vol_crc + 1, HAMMER_VOL_CRCSIZE2);
436 }
437
438 int
439 hammer_crc_test_blockmap(hammer_blockmap_t blockmap)
440 {
441         hammer_crc_t crc;
442
443         crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE);
444         return (blockmap->entry_crc == crc);
445 }
446
447 int
448 hammer_crc_test_volume(hammer_volume_ondisk_t ondisk)
449 {
450         hammer_crc_t crc;
451
452         crc = crc32(ondisk, HAMMER_VOL_CRCSIZE1) ^
453               crc32(&ondisk->vol_crc + 1, HAMMER_VOL_CRCSIZE2);
454         return (ondisk->vol_crc == crc);
455 }
456
457 int
458 hammer_crc_test_btree(hammer_node_ondisk_t ondisk)
459 {
460         hammer_crc_t crc;
461
462         crc = crc32(&ondisk->crc + 1, HAMMER_BTREE_CRCSIZE);
463         return (ondisk->crc == crc);
464 }
465
466 void
467 hkprintf(const char *ctl, ...)
468 {
469         __va_list va;
470
471         if (hammer_debug_debug) {
472                 __va_start(va, ctl);
473                 kvprintf(ctl, va);
474                 __va_end(va);
475         }
476 }
477