hammer2 - serialized flush work part 1
[dragonfly.git] / sys / vfs / hammer2 / hammer2_subr.c
1 /*
2  * Copyright (c) 2011-2012 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.
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 void
60 hammer2_inode_lock_ex(hammer2_inode_t *ip)
61 {
62         hammer2_inode_ref(ip);
63         ccms_thread_lock(&ip->topo_cst, CCMS_STATE_EXCLUSIVE);
64         KKASSERT(ip->chain != NULL);    /* for now */
65         hammer2_chain_lock(ip->hmp, ip->chain, HAMMER2_RESOLVE_ALWAYS);
66 }
67
68 void
69 hammer2_inode_unlock_ex(hammer2_inode_t *ip)
70 {
71         if (ip->chain)
72                 hammer2_chain_unlock(ip->hmp, ip->chain);
73         ccms_thread_unlock(&ip->topo_cst);
74         hammer2_inode_drop(ip);
75 }
76
77 void
78 hammer2_inode_lock_sh(hammer2_inode_t *ip)
79 {
80         hammer2_inode_ref(ip);
81         ccms_thread_lock(&ip->topo_cst, CCMS_STATE_SHARED);
82         KKASSERT(ip->chain != NULL);    /* for now */
83         hammer2_chain_lock(ip->hmp, ip->chain, HAMMER2_RESOLVE_ALWAYS |
84                                                HAMMER2_RESOLVE_SHARED);
85 }
86
87 void
88 hammer2_inode_unlock_sh(hammer2_inode_t *ip)
89 {
90         if (ip->chain)
91                 hammer2_chain_unlock(ip->hmp, ip->chain);
92         ccms_thread_unlock(&ip->topo_cst);
93         hammer2_inode_drop(ip);
94 }
95
96 ccms_state_t
97 hammer2_inode_lock_temp_release(hammer2_inode_t *ip)
98 {
99         return(ccms_thread_lock_temp_release(&ip->topo_cst));
100 }
101
102 ccms_state_t
103 hammer2_inode_lock_upgrade(hammer2_inode_t *ip)
104 {
105         return(ccms_thread_lock_upgrade(&ip->topo_cst));
106 }
107
108 void
109 hammer2_inode_lock_restore(hammer2_inode_t *ip, ccms_state_t ostate)
110 {
111         ccms_thread_lock_restore(&ip->topo_cst, ostate);
112 }
113
114 /*
115  * Mount-wide locks
116  */
117
118 void
119 hammer2_mount_exlock(hammer2_mount_t *hmp)
120 {
121         ccms_thread_lock(&hmp->vchain.cst, CCMS_STATE_EXCLUSIVE);
122 }
123
124 void
125 hammer2_mount_shlock(hammer2_mount_t *hmp)
126 {
127         ccms_thread_lock(&hmp->vchain.cst, CCMS_STATE_SHARED);
128 }
129
130 void
131 hammer2_mount_unlock(hammer2_mount_t *hmp)
132 {
133         ccms_thread_unlock(&hmp->vchain.cst);
134 }
135
136 void
137 hammer2_voldata_lock(hammer2_mount_t *hmp)
138 {
139         lockmgr(&hmp->voldatalk, LK_EXCLUSIVE);
140 }
141
142 void
143 hammer2_voldata_unlock(hammer2_mount_t *hmp)
144 {
145         lockmgr(&hmp->voldatalk, LK_RELEASE);
146 }
147
148 /*
149  * Return the directory entry type for an inode.
150  *
151  * ip must be locked sh/ex.
152  */
153 int
154 hammer2_get_dtype(hammer2_chain_t *chain)
155 {
156         uint8_t type;
157
158         KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
159
160         if ((type = chain->data->ipdata.type) == HAMMER2_OBJTYPE_HARDLINK)
161                 type = chain->data->ipdata.target_type;
162
163         switch(type) {
164         case HAMMER2_OBJTYPE_UNKNOWN:
165                 return (DT_UNKNOWN);
166         case HAMMER2_OBJTYPE_DIRECTORY:
167                 return (DT_DIR);
168         case HAMMER2_OBJTYPE_REGFILE:
169                 return (DT_REG);
170         case HAMMER2_OBJTYPE_FIFO:
171                 return (DT_FIFO);
172         case HAMMER2_OBJTYPE_CDEV:      /* not supported */
173                 return (DT_CHR);
174         case HAMMER2_OBJTYPE_BDEV:      /* not supported */
175                 return (DT_BLK);
176         case HAMMER2_OBJTYPE_SOFTLINK:
177                 return (DT_LNK);
178         case HAMMER2_OBJTYPE_HARDLINK:  /* (never directly associated w/vp) */
179                 return (DT_UNKNOWN);
180         case HAMMER2_OBJTYPE_SOCKET:
181                 return (DT_SOCK);
182         case HAMMER2_OBJTYPE_WHITEOUT:  /* not supported */
183                 return (DT_UNKNOWN);
184         default:
185                 return (DT_UNKNOWN);
186         }
187         /* not reached */
188 }
189
190 /*
191  * Return the directory entry type for an inode
192  */
193 int
194 hammer2_get_vtype(hammer2_chain_t *chain)
195 {
196         KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
197
198         switch(chain->data->ipdata.type) {
199         case HAMMER2_OBJTYPE_UNKNOWN:
200                 return (VBAD);
201         case HAMMER2_OBJTYPE_DIRECTORY:
202                 return (VDIR);
203         case HAMMER2_OBJTYPE_REGFILE:
204                 return (VREG);
205         case HAMMER2_OBJTYPE_FIFO:
206                 return (VFIFO);
207         case HAMMER2_OBJTYPE_CDEV:      /* not supported */
208                 return (VCHR);
209         case HAMMER2_OBJTYPE_BDEV:      /* not supported */
210                 return (VBLK);
211         case HAMMER2_OBJTYPE_SOFTLINK:
212                 return (VLNK);
213         case HAMMER2_OBJTYPE_HARDLINK:  /* XXX */
214                 return (VBAD);
215         case HAMMER2_OBJTYPE_SOCKET:
216                 return (VSOCK);
217         case HAMMER2_OBJTYPE_WHITEOUT:  /* not supported */
218                 return (DT_UNKNOWN);
219         default:
220                 return (DT_UNKNOWN);
221         }
222         /* not reached */
223 }
224
225 u_int8_t
226 hammer2_get_obj_type(enum vtype vtype)
227 {
228         switch(vtype) {
229         case VDIR:
230                 return(HAMMER2_OBJTYPE_DIRECTORY);
231         case VREG:
232                 return(HAMMER2_OBJTYPE_REGFILE);
233         case VFIFO:
234                 return(HAMMER2_OBJTYPE_FIFO);
235         case VSOCK:
236                 return(HAMMER2_OBJTYPE_SOCKET);
237         case VCHR:
238                 return(HAMMER2_OBJTYPE_CDEV);
239         case VBLK:
240                 return(HAMMER2_OBJTYPE_BDEV);
241         case VLNK:
242                 return(HAMMER2_OBJTYPE_SOFTLINK);
243         default:
244                 return(HAMMER2_OBJTYPE_UNKNOWN);
245         }
246         /* not reached */
247 }
248
249 /*
250  * Convert a hammer2 64-bit time to a timespec.
251  */
252 void
253 hammer2_time_to_timespec(u_int64_t xtime, struct timespec *ts)
254 {
255         ts->tv_sec = (unsigned long)(xtime / 1000000);
256         ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L;
257 }
258
259 u_int64_t
260 hammer2_timespec_to_time(struct timespec *ts)
261 {
262         u_int64_t xtime;
263
264         xtime = (unsigned)(ts->tv_nsec / 1000) +
265                 (unsigned long)ts->tv_sec * 1000000ULL;
266         return(xtime);
267 }
268
269 /*
270  * Convert a uuid to a unix uid or gid
271  */
272 u_int32_t
273 hammer2_to_unix_xid(uuid_t *uuid)
274 {
275         return(*(u_int32_t *)&uuid->node[2]);
276 }
277
278 void
279 hammer2_guid_to_uuid(uuid_t *uuid, u_int32_t guid)
280 {
281         bzero(uuid, sizeof(*uuid));
282         *(u_int32_t *)&uuid->node[2] = guid;
283 }
284
285 /*
286  * Borrow HAMMER1's directory hash algorithm #1 with a few modifications.
287  * The filename is split into fields which are hashed separately and then
288  * added together.
289  *
290  * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets
291  * it to 0), this is because bit63=0 is used for hidden hardlinked inodes.
292  * (This means we do not need to do a 0-check/or-with-0x100000000 either).
293  *
294  * Also, the iscsi crc code is used instead of the old crc32 code.
295  */
296 hammer2_key_t
297 hammer2_dirhash(const unsigned char *name, size_t len)
298 {
299         const unsigned char *aname = name;
300         uint32_t crcx;
301         uint64_t key;
302         size_t i;
303         size_t j;
304
305         key = 0;
306
307         /*
308          * m32
309          */
310         crcx = 0;
311         for (i = j = 0; i < len; ++i) {
312                 if (aname[i] == '.' ||
313                     aname[i] == '-' ||
314                     aname[i] == '_' ||
315                     aname[i] == '~') {
316                         if (i != j)
317                                 crcx += hammer2_icrc32(aname + j, i - j);
318                         j = i + 1;
319                 }
320         }
321         if (i != j)
322                 crcx += hammer2_icrc32(aname + j, i - j);
323
324         /*
325          * The directory hash utilizes the top 32 bits of the 64-bit key.
326          * Bit 63 must be set to 1.
327          */
328         crcx |= 0x80000000U;
329         key |= (uint64_t)crcx << 32;
330
331         /*
332          * l16 - crc of entire filename
333          *
334          * This crc reduces degenerate hash collision conditions
335          */
336         crcx = hammer2_icrc32(aname, len);
337         crcx = crcx ^ (crcx << 16);
338         key |= crcx & 0xFFFF0000U;
339
340         /*
341          * Set bit 15.  This allows readdir to strip bit 63 so a positive
342          * 64-bit cookie/offset can always be returned, and still guarantee
343          * that the values 0x0000-0x7FFF are available for artificial entries.
344          * ('.' and '..').
345          */
346         key |= 0x8000U;
347
348         return (key);
349 }
350
351 /*
352  * Return the power-of-2 radix greater or equal to
353  * the specified number of bytes.
354  *
355  * Always returns at least the minimum media allocation
356  * size radix, HAMMER2_MIN_RADIX (10), which is 1KB.
357  */
358 int
359 hammer2_allocsize(size_t bytes)
360 {
361         int radix;
362
363         if (bytes < HAMMER2_MIN_ALLOC)
364                 bytes = HAMMER2_MIN_ALLOC;
365         if (bytes == HAMMER2_PBUFSIZE)
366                 radix = HAMMER2_PBUFRADIX;
367         else if (bytes >= 16384)
368                 radix = 14;
369         else if (bytes >= 1024)
370                 radix = 10;
371         else
372                 radix = HAMMER2_MIN_RADIX;
373
374         while (((size_t)1 << radix) < bytes)
375                 ++radix;
376         return (radix);
377 }
378
379 /*
380  * ip must be locked sh/ex
381  */
382 int
383 hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff,
384                      hammer2_key_t *lbasep, hammer2_key_t *leofp)
385 {
386         hammer2_inode_data_t *ipdata = &ip->chain->data->ipdata;
387         int radix;
388
389         *lbasep = uoff & ~HAMMER2_PBUFMASK64;
390         *leofp = ipdata->size & ~HAMMER2_PBUFMASK64;
391         KKASSERT(*lbasep <= *leofp);
392         if (*lbasep == *leofp /*&& *leofp < 1024 * 1024*/) {
393                 radix = hammer2_allocsize((size_t)(ipdata->size - *leofp));
394                 if (radix < HAMMER2_MINALLOCRADIX)
395                         radix = HAMMER2_MINALLOCRADIX;
396                 *leofp += 1U << radix;
397                 return (1U << radix);
398         } else {
399                 return (HAMMER2_PBUFSIZE);
400         }
401 }
402
403 void
404 hammer2_update_time(uint64_t *timep)
405 {
406         struct timeval tv;
407
408         getmicrotime(&tv);
409         *timep = (unsigned long)tv.tv_sec * 1000000 + tv.tv_usec;
410 }