Merge branch 'vendor/AWK'
[dragonfly.git] / sys / vfs / hammer2 / hammer2_subr.c
1 /*
2  * Copyright (c) 2011-2013 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 #include <sys/cdefs.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/types.h>
39 #include <sys/lock.h>
40 #include <sys/uuid.h>
41 #include <sys/dirent.h>
42
43 #include "hammer2.h"
44
45 /*
46  * HAMMER2 inode locks
47  *
48  * HAMMER2 offers shared locks and exclusive locks on inodes.
49  *
50  * An inode's ip->chain pointer is resolved and stable while an inode is
51  * locked, and can be cleaned out at any time (become NULL) when an inode
52  * is not locked.
53  *
54  * The underlying chain is also locked and returned.
55  *
56  * NOTE: We don't combine the inode/chain lock because putting away an
57  *       inode would otherwise confuse multiple lock holders of the inode.
58  */
59 hammer2_chain_t *
60 hammer2_inode_lock_ex(hammer2_inode_t *ip)
61 {
62         hammer2_chain_t *chain;
63
64         hammer2_inode_ref(ip);
65         ccms_thread_lock(&ip->topo_cst, CCMS_STATE_EXCLUSIVE);
66
67         /*
68          * ip->chain fixup.  Certain duplications used to move inodes
69          * into indirect blocks (for example) can cause ip->chain to
70          * become stale.
71          */
72 again:
73         chain = ip->chain;
74         while (chain->duplink && (chain->flags & HAMMER2_CHAIN_DELETED))
75                 chain = chain->duplink;
76         if (ip->chain != chain) {
77                 hammer2_chain_ref(chain);
78                 hammer2_chain_drop(ip->chain);
79                 ip->chain = chain;
80         }
81
82         KKASSERT(chain != NULL);        /* for now */
83         hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS);
84
85         /*
86          * Resolve duplication races
87          */
88         if (chain->duplink && (chain->flags & HAMMER2_CHAIN_DELETED)) {
89                 hammer2_chain_unlock(chain);
90                 goto again;
91         }
92         return (chain);
93 }
94
95 void
96 hammer2_inode_unlock_ex(hammer2_inode_t *ip, hammer2_chain_t *chain)
97 {
98         /*
99          * XXX this will catch parent directories too which we don't
100          *     really want.
101          */
102         if (chain)
103                 hammer2_chain_unlock(chain);
104
105         /*
106          * Recalculate ip->chain on exclusive unlock too, it may
107          * allow us to free stale chains more quickly.
108          */
109         if ((chain = ip->chain) != NULL) {
110                 while (chain->duplink && (chain->flags & HAMMER2_CHAIN_DELETED))
111                         chain = chain->duplink;
112                 if (ip->chain != chain) {
113                         hammer2_chain_ref(chain);
114                         hammer2_chain_drop(ip->chain);
115                         ip->chain = chain;
116                 }
117         }
118
119         ccms_thread_unlock(&ip->topo_cst);
120         hammer2_inode_drop(ip);
121 }
122
123 /*
124  * NOTE: We don't combine the inode/chain lock because putting away an
125  *       inode would otherwise confuse multiple lock holders of the inode.
126  *
127  *       Shared locks are especially sensitive to having too many shared
128  *       lock counts (from the same thread) on certain paths which might
129  *       need to upgrade them.  Only one count of a shared lock can be
130  *       upgraded.
131  */
132 hammer2_chain_t *
133 hammer2_inode_lock_sh(hammer2_inode_t *ip)
134 {
135         hammer2_chain_t *chain;
136
137         hammer2_inode_ref(ip);
138 again:
139         ccms_thread_lock(&ip->topo_cst, CCMS_STATE_SHARED);
140
141         chain = ip->chain;
142         KKASSERT(chain != NULL);        /* for now */
143         hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS |
144                                   HAMMER2_RESOLVE_SHARED);
145
146         /*
147          * Resolve duplication races
148          */
149         if (chain->duplink && (chain->flags & HAMMER2_CHAIN_DELETED)) {
150                 hammer2_chain_unlock(chain);
151                 ccms_thread_unlock(&ip->topo_cst);
152                 chain = hammer2_inode_lock_ex(ip);
153                 hammer2_inode_unlock_ex(ip, chain);
154                 goto again;
155         }
156         return (chain);
157 }
158
159 void
160 hammer2_inode_unlock_sh(hammer2_inode_t *ip, hammer2_chain_t *chain)
161 {
162         if (chain)
163                 hammer2_chain_unlock(chain);
164         ccms_thread_unlock(&ip->topo_cst);
165         hammer2_inode_drop(ip);
166 }
167
168 ccms_state_t
169 hammer2_inode_lock_temp_release(hammer2_inode_t *ip)
170 {
171         return(ccms_thread_lock_temp_release(&ip->topo_cst));
172 }
173
174 void
175 hammer2_inode_lock_temp_restore(hammer2_inode_t *ip, ccms_state_t ostate)
176 {
177         ccms_thread_lock_temp_restore(&ip->topo_cst, ostate);
178 }
179
180 ccms_state_t
181 hammer2_inode_lock_upgrade(hammer2_inode_t *ip)
182 {
183         return(ccms_thread_lock_upgrade(&ip->topo_cst));
184 }
185
186 void
187 hammer2_inode_lock_downgrade(hammer2_inode_t *ip, ccms_state_t ostate)
188 {
189         ccms_thread_lock_downgrade(&ip->topo_cst, ostate);
190 }
191
192 /*
193  * Mount-wide locks
194  */
195
196 void
197 hammer2_mount_exlock(hammer2_mount_t *hmp)
198 {
199         ccms_thread_lock(&hmp->vchain.core->cst, CCMS_STATE_EXCLUSIVE);
200 }
201
202 void
203 hammer2_mount_shlock(hammer2_mount_t *hmp)
204 {
205         ccms_thread_lock(&hmp->vchain.core->cst, CCMS_STATE_SHARED);
206 }
207
208 void
209 hammer2_mount_unlock(hammer2_mount_t *hmp)
210 {
211         ccms_thread_unlock(&hmp->vchain.core->cst);
212 }
213
214 void
215 hammer2_voldata_lock(hammer2_mount_t *hmp)
216 {
217         lockmgr(&hmp->voldatalk, LK_EXCLUSIVE);
218 }
219
220 void
221 hammer2_voldata_unlock(hammer2_mount_t *hmp, int modify)
222 {
223         if (modify &&
224             (hmp->vchain.flags & HAMMER2_CHAIN_MODIFIED) == 0) {
225                 atomic_set_int(&hmp->vchain.flags, HAMMER2_CHAIN_MODIFIED);
226                 hammer2_chain_ref(&hmp->vchain);
227         }
228         lockmgr(&hmp->voldatalk, LK_RELEASE);
229 }
230
231 /*
232  * Return the directory entry type for an inode.
233  *
234  * ip must be locked sh/ex.
235  */
236 int
237 hammer2_get_dtype(hammer2_chain_t *chain)
238 {
239         uint8_t type;
240
241         KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
242
243         if ((type = chain->data->ipdata.type) == HAMMER2_OBJTYPE_HARDLINK)
244                 type = chain->data->ipdata.target_type;
245
246         switch(type) {
247         case HAMMER2_OBJTYPE_UNKNOWN:
248                 return (DT_UNKNOWN);
249         case HAMMER2_OBJTYPE_DIRECTORY:
250                 return (DT_DIR);
251         case HAMMER2_OBJTYPE_REGFILE:
252                 return (DT_REG);
253         case HAMMER2_OBJTYPE_FIFO:
254                 return (DT_FIFO);
255         case HAMMER2_OBJTYPE_CDEV:      /* not supported */
256                 return (DT_CHR);
257         case HAMMER2_OBJTYPE_BDEV:      /* not supported */
258                 return (DT_BLK);
259         case HAMMER2_OBJTYPE_SOFTLINK:
260                 return (DT_LNK);
261         case HAMMER2_OBJTYPE_HARDLINK:  /* (never directly associated w/vp) */
262                 return (DT_UNKNOWN);
263         case HAMMER2_OBJTYPE_SOCKET:
264                 return (DT_SOCK);
265         case HAMMER2_OBJTYPE_WHITEOUT:  /* not supported */
266                 return (DT_UNKNOWN);
267         default:
268                 return (DT_UNKNOWN);
269         }
270         /* not reached */
271 }
272
273 /*
274  * Return the directory entry type for an inode
275  */
276 int
277 hammer2_get_vtype(hammer2_chain_t *chain)
278 {
279         KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
280
281         switch(chain->data->ipdata.type) {
282         case HAMMER2_OBJTYPE_UNKNOWN:
283                 return (VBAD);
284         case HAMMER2_OBJTYPE_DIRECTORY:
285                 return (VDIR);
286         case HAMMER2_OBJTYPE_REGFILE:
287                 return (VREG);
288         case HAMMER2_OBJTYPE_FIFO:
289                 return (VFIFO);
290         case HAMMER2_OBJTYPE_CDEV:      /* not supported */
291                 return (VCHR);
292         case HAMMER2_OBJTYPE_BDEV:      /* not supported */
293                 return (VBLK);
294         case HAMMER2_OBJTYPE_SOFTLINK:
295                 return (VLNK);
296         case HAMMER2_OBJTYPE_HARDLINK:  /* XXX */
297                 return (VBAD);
298         case HAMMER2_OBJTYPE_SOCKET:
299                 return (VSOCK);
300         case HAMMER2_OBJTYPE_WHITEOUT:  /* not supported */
301                 return (DT_UNKNOWN);
302         default:
303                 return (DT_UNKNOWN);
304         }
305         /* not reached */
306 }
307
308 u_int8_t
309 hammer2_get_obj_type(enum vtype vtype)
310 {
311         switch(vtype) {
312         case VDIR:
313                 return(HAMMER2_OBJTYPE_DIRECTORY);
314         case VREG:
315                 return(HAMMER2_OBJTYPE_REGFILE);
316         case VFIFO:
317                 return(HAMMER2_OBJTYPE_FIFO);
318         case VSOCK:
319                 return(HAMMER2_OBJTYPE_SOCKET);
320         case VCHR:
321                 return(HAMMER2_OBJTYPE_CDEV);
322         case VBLK:
323                 return(HAMMER2_OBJTYPE_BDEV);
324         case VLNK:
325                 return(HAMMER2_OBJTYPE_SOFTLINK);
326         default:
327                 return(HAMMER2_OBJTYPE_UNKNOWN);
328         }
329         /* not reached */
330 }
331
332 /*
333  * Convert a hammer2 64-bit time to a timespec.
334  */
335 void
336 hammer2_time_to_timespec(u_int64_t xtime, struct timespec *ts)
337 {
338         ts->tv_sec = (unsigned long)(xtime / 1000000);
339         ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L;
340 }
341
342 u_int64_t
343 hammer2_timespec_to_time(struct timespec *ts)
344 {
345         u_int64_t xtime;
346
347         xtime = (unsigned)(ts->tv_nsec / 1000) +
348                 (unsigned long)ts->tv_sec * 1000000ULL;
349         return(xtime);
350 }
351
352 /*
353  * Convert a uuid to a unix uid or gid
354  */
355 u_int32_t
356 hammer2_to_unix_xid(uuid_t *uuid)
357 {
358         return(*(u_int32_t *)&uuid->node[2]);
359 }
360
361 void
362 hammer2_guid_to_uuid(uuid_t *uuid, u_int32_t guid)
363 {
364         bzero(uuid, sizeof(*uuid));
365         *(u_int32_t *)&uuid->node[2] = guid;
366 }
367
368 /*
369  * Borrow HAMMER1's directory hash algorithm #1 with a few modifications.
370  * The filename is split into fields which are hashed separately and then
371  * added together.
372  *
373  * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets
374  * it to 0), this is because bit63=0 is used for hidden hardlinked inodes.
375  * (This means we do not need to do a 0-check/or-with-0x100000000 either).
376  *
377  * Also, the iscsi crc code is used instead of the old crc32 code.
378  */
379 hammer2_key_t
380 hammer2_dirhash(const unsigned char *name, size_t len)
381 {
382         const unsigned char *aname = name;
383         uint32_t crcx;
384         uint64_t key;
385         size_t i;
386         size_t j;
387
388         key = 0;
389
390         /*
391          * m32
392          */
393         crcx = 0;
394         for (i = j = 0; i < len; ++i) {
395                 if (aname[i] == '.' ||
396                     aname[i] == '-' ||
397                     aname[i] == '_' ||
398                     aname[i] == '~') {
399                         if (i != j)
400                                 crcx += hammer2_icrc32(aname + j, i - j);
401                         j = i + 1;
402                 }
403         }
404         if (i != j)
405                 crcx += hammer2_icrc32(aname + j, i - j);
406
407         /*
408          * The directory hash utilizes the top 32 bits of the 64-bit key.
409          * Bit 63 must be set to 1.
410          */
411         crcx |= 0x80000000U;
412         key |= (uint64_t)crcx << 32;
413
414         /*
415          * l16 - crc of entire filename
416          *
417          * This crc reduces degenerate hash collision conditions
418          */
419         crcx = hammer2_icrc32(aname, len);
420         crcx = crcx ^ (crcx << 16);
421         key |= crcx & 0xFFFF0000U;
422
423         /*
424          * Set bit 15.  This allows readdir to strip bit 63 so a positive
425          * 64-bit cookie/offset can always be returned, and still guarantee
426          * that the values 0x0000-0x7FFF are available for artificial entries.
427          * ('.' and '..').
428          */
429         key |= 0x8000U;
430
431         return (key);
432 }
433
434 /*
435  * Return the power-of-2 radix greater or equal to
436  * the specified number of bytes.
437  *
438  * Always returns at least the minimum media allocation
439  * size radix, HAMMER2_MIN_RADIX (10), which is 1KB.
440  */
441 int
442 hammer2_allocsize(size_t bytes)
443 {
444         int radix;
445
446         if (bytes < HAMMER2_MIN_ALLOC)
447                 bytes = HAMMER2_MIN_ALLOC;
448         if (bytes == HAMMER2_PBUFSIZE)
449                 radix = HAMMER2_PBUFRADIX;
450         else if (bytes >= 16384)
451                 radix = 14;
452         else if (bytes >= 1024)
453                 radix = 10;
454         else
455                 radix = HAMMER2_MIN_RADIX;
456
457         while (((size_t)1 << radix) < bytes)
458                 ++radix;
459         return (radix);
460 }
461
462 /*
463  * ip must be locked sh/ex
464  */
465 int
466 hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff,
467                      hammer2_key_t *lbasep, hammer2_key_t *leofp)
468 {
469         hammer2_inode_data_t *ipdata = &ip->chain->data->ipdata;
470         int radix;
471
472         *lbasep = uoff & ~HAMMER2_PBUFMASK64;
473         *leofp = ipdata->size & ~HAMMER2_PBUFMASK64;
474         KKASSERT(*lbasep <= *leofp);
475         if (*lbasep == *leofp /*&& *leofp < 1024 * 1024*/) {
476                 radix = hammer2_allocsize((size_t)(ipdata->size - *leofp));
477                 if (radix < HAMMER2_MINALLOCRADIX)
478                         radix = HAMMER2_MINALLOCRADIX;
479                 *leofp += 1U << radix;
480                 return (1U << radix);
481         } else {
482                 return (HAMMER2_PBUFSIZE);
483         }
484 }
485
486 void
487 hammer2_update_time(uint64_t *timep)
488 {
489         struct timeval tv;
490
491         getmicrotime(&tv);
492         *timep = (unsigned long)tv.tv_sec * 1000000 + tv.tv_usec;
493 }