Correct assertion in hammer2_freei.
[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/cdefs.h>
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/types.h>
40 #include <sys/lock.h>
41 #include <sys/uuid.h>
42
43 #include "hammer2.h"
44
45 /*
46  * HAMMER2 inode locks
47  *
48  * HAMMER2 offers shared locks, update locks, and exclusive locks on inodes.
49  *
50  * Shared locks allow concurrent access to an inode's fields, but exclude
51  * access by concurrent exclusive locks.
52  *
53  * Update locks are interesting -- an update lock will be taken after all
54  * shared locks on an inode are released, but once it is in place, shared
55  * locks may proceed. The update field is signalled by a busy flag in the
56  * inode. Only one update lock may be in place at a given time on an inode.
57  *
58  * Exclusive locks prevent concurrent access to the inode.
59  *
60  * XXX: What do we use each for? How is visibility to the inode controlled?
61  */
62
63 void
64 hammer2_inode_lock_sh(hammer2_inode_t *ip)
65 {
66         lockmgr(&ip->lk, LK_SHARED);
67 }
68
69 void
70 hammer2_inode_lock_up(hammer2_inode_t *ip)
71 {
72         lockmgr(&ip->lk, LK_EXCLUSIVE);
73         ++ip->busy;
74         lockmgr(&ip->lk, LK_DOWNGRADE);
75 }
76
77 void
78 hammer2_inode_lock_ex(hammer2_inode_t *ip)
79 {
80         lockmgr(&ip->lk, LK_EXCLUSIVE);
81 }
82
83 void
84 hammer2_inode_unlock_ex(hammer2_inode_t *ip)
85 {
86         lockmgr(&ip->lk, LK_RELEASE);
87 }
88
89 void
90 hammer2_inode_unlock_up(hammer2_inode_t *ip)
91 {
92         lockmgr(&ip->lk, LK_UPGRADE);
93         --ip->busy;
94         lockmgr(&ip->lk, LK_RELEASE);
95 }
96
97 void
98 hammer2_inode_unlock_sh(hammer2_inode_t *ip)
99 {
100         lockmgr(&ip->lk, LK_RELEASE);
101 }
102
103 /*
104  * Mount-wide locks
105  */
106
107 void
108 hammer2_mount_exlock(hammer2_mount_t *hmp)
109 {
110         lockmgr(&hmp->lk, LK_EXCLUSIVE);
111 }
112
113 void
114 hammer2_mount_shlock(hammer2_mount_t *hmp)
115 {
116         lockmgr(&hmp->lk, LK_SHARED);
117 }
118
119 void
120 hammer2_mount_unlock(hammer2_mount_t *hmp)
121 {
122         lockmgr(&hmp->lk, LK_RELEASE);
123 }
124
125 /*
126  * Inode/vnode subroutines
127  */
128
129 /*
130  * Get the vnode associated with the given inode, allocating the vnode if
131  * necessary.
132  *
133  * Great care must be taken to avoid deadlocks and vnode acquisition/reclaim
134  * races.
135  *
136  * The vnode will be returned exclusively locked and referenced.  The
137  * reference on the vnode prevents it from being reclaimed.
138  *
139  * The inode (ip) must be referenced by the caller and not locked to avoid
140  * it getting ripped out from under us or deadlocked.
141  */
142 struct vnode *
143 hammer2_igetv(hammer2_inode_t *ip, int *errorp)
144 {
145         struct vnode *vp;
146         hammer2_mount_t *hmp;
147
148         hmp = ip->hmp;
149         *errorp = 0;
150
151         for (;;) {
152                 /*
153                  * Attempt to reuse an existing vnode assignment.  It is
154                  * possible to race a reclaim so the vget() may fail.  The
155                  * inode must be unlocked during the vget() to avoid a
156                  * deadlock against a reclaim.
157                  */
158                 vp = ip->vp;
159                 if (vp) {
160                         /*
161                          * Lock the inode and check for a reclaim race
162                          */
163                         hammer2_inode_lock_ex(ip);
164                         if (ip->vp != vp) {
165                                 hammer2_inode_unlock_ex(ip);
166                                 continue;
167                         }
168
169                         /*
170                          * Inode must be unlocked during the vget() to avoid
171                          * possible deadlocks, vnode is held to prevent
172                          * destruction during the vget().  The vget() can
173                          * still fail if we lost a reclaim race on the vnode.
174                          */
175                         vhold_interlocked(vp);
176                         hammer2_inode_unlock_ex(ip);
177                         if (vget(vp, LK_EXCLUSIVE)) {
178                                 vdrop(vp);
179                                 continue;
180                         }
181                         vdrop(vp);
182                         /* vp still locked and ref from vget */
183                         *errorp = 0;
184                         break;
185                 }
186
187                 /*
188                  * No vnode exists, allocate a new vnode.  Beware of
189                  * allocation races.  This function will return an
190                  * exclusively locked and referenced vnode.
191                  */
192                 *errorp = getnewvnode(VT_HAMMER2, H2TOMP(hmp), &vp, 0, 0);
193                 if (*errorp) {
194                         vp = NULL;
195                         break;
196                 }
197
198                 /*
199                  * Lock the inode and check for an allocation race.
200                  */
201                 hammer2_inode_lock_ex(ip);
202                 if (ip->vp != NULL) {
203                         vp->v_type = VBAD;
204                         vx_put(vp);
205                         hammer2_inode_unlock_ex(ip);
206                         continue;
207                 }
208
209                 kprintf("igetv new\n");
210                 switch (ip->type & HAMMER2_INODE_TYPE_MASK) {
211                 case HAMMER2_INODE_TYPE_DIR:
212                         vp->v_type = VDIR;
213                         break;
214                 case HAMMER2_INODE_TYPE_FILE:
215                         vp->v_type = VREG;
216                         vinitvmio(vp, 0, HAMMER2_LBUFSIZE,
217                                   (int)ip->data.size & HAMMER2_LBUFMASK);
218                         break;
219                 /* XXX FIFO */
220                 default:
221                         break;
222                 }
223
224                 if (ip->type & HAMMER2_INODE_TYPE_ROOT)
225                         vsetflags(vp, VROOT);
226
227                 vp->v_data = ip;
228                 ip->vp = vp;
229                 hammer2_inode_unlock_ex(ip);
230                 break;
231         }
232
233         /*
234          * Return non-NULL vp and *errorp == 0, or NULL vp and *errorp != 0.
235          */
236         return (vp);
237 }
238
239 /*
240  * Allocate a HAMMER2 inode memory structure.
241  *
242  * The returned inode is locked exclusively and referenced.
243  * The HAMMER2 mountpoint must be locked on entry.
244  */
245 hammer2_inode_t *
246 hammer2_alloci(hammer2_mount_t *hmp)
247 {
248         hammer2_inode_t *ip;
249
250         kprintf("alloci\n");
251
252         ip = kmalloc(sizeof(hammer2_inode_t), hmp->inodes, M_WAITOK | M_ZERO);
253         if (!ip) {
254                 /* XXX */
255         }
256
257         ++hmp->ninodes;
258
259         ip->type = 0;
260         ip->hmp = hmp;
261         lockinit(&ip->lk, "h2inode", 0, 0);
262         ip->vp = NULL;
263         ip->refs = 1;
264         hammer2_inode_lock_ex(ip);
265
266         return (ip);
267 }
268
269 /*
270  * Free a HAMMER2 inode memory structure.
271  *
272  * The inode must be locked exclusively with one reference and will
273  * be destroyed on return.
274  */
275 void
276 hammer2_freei(hammer2_inode_t *ip)
277 {
278         hammer2_mount_t *hmp = ip->hmp;
279         
280         KKASSERT(ip->hmp != NULL);
281         KKASSERT(ip->vp == NULL);
282         KKASSERT(ip->refs == 1);
283         hammer2_inode_unlock_ex(ip);
284         kfree(ip, hmp->inodes);
285 }