2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 * $DragonFly: src/sys/vfs/hammer/hammer_inode.c,v 1.4 2007/11/19 00:53:40 dillon Exp $
42 hammer_vop_inactive(struct vop_inactive_args *ap)
44 struct hammer_inode *ip = VTOI(ap->a_vp);
52 hammer_vop_reclaim(struct vop_reclaim_args *ap)
54 struct hammer_inode *ip;
58 if ((ip = vp->v_data) != NULL)
59 hammer_unload_inode(ip, NULL);
64 * Obtain a vnode for the specified inode number. An exclusively locked
68 hammer_vfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
70 struct hammer_mount *hmp = (void *)mp->mnt_data;
71 struct hammer_inode *ip;
75 * Get/allocate the hammer_inode structure. The structure must be
76 * unlocked while we manipulate the related vnode to avoid a
79 ip = hammer_get_inode(hmp, ino, &error);
84 error = hammer_get_vnode(ip, LK_EXCLUSIVE, vpp);
90 * Return a locked vnode for the specified inode. The inode must be
91 * referenced but NOT LOCKED on entry and will remain referenced on
95 hammer_get_vnode(struct hammer_inode *ip, int lktype, struct vnode **vpp)
101 if ((vp = ip->vp) == NULL) {
102 error = getnewvnode(VT_HAMMER, ip->hmp->mp, vpp, 0, 0);
105 hammer_lock_ex(&ip->lock);
106 if (ip->vp != NULL) {
107 hammer_unlock(&ip->lock);
112 hammer_ref(&ip->lock);
115 vp->v_type = hammer_get_vnode_type(
116 ip->ino_rec.base.base.obj_type);
117 vp->v_data = (void *)ip;
118 /* vnode locked by getnewvnode() */
119 /* make related vnode dirty if inode dirty? */
120 hammer_unlock(&ip->lock);
125 * loop if the vget fails (aka races), or if the vp
126 * no longer matches ip->vp.
128 if (vget(vp, LK_EXCLUSIVE) == 0) {
138 * Acquire a HAMMER inode. The returned inode is not locked. These functions
139 * do not attach or detach the related vnode (use hammer_get_vnode() for
142 struct hammer_inode *
143 hammer_get_inode(struct hammer_mount *hmp, u_int64_t obj_id, int *errorp)
145 struct hammer_inode_info iinfo;
146 struct hammer_cursor cursor;
147 struct hammer_inode *ip;
150 * Determine if we already have an inode cached. If we do then
153 iinfo.obj_id = obj_id;
154 iinfo.obj_asof = HAMMER_MAX_TID; /* XXX */
156 ip = hammer_ino_rb_tree_RB_LOOKUP_INFO(&hmp->rb_inos_root, &iinfo);
158 hammer_ref(&ip->lock);
163 ip = kmalloc(sizeof(*ip), M_HAMMER, M_WAITOK|M_ZERO);
165 ip->obj_asof = iinfo.obj_asof;
167 RB_INIT(&ip->rec_tree);
170 * Locate the on-disk inode.
171 * If we do not have an inode cached search the HAMMER on-disk B-Tree
175 hammer_init_cursor_hmp(&cursor, hmp);
176 cursor.key_beg.obj_id = ip->obj_id;
177 cursor.key_beg.key = 0;
178 cursor.key_beg.create_tid = iinfo.obj_asof;
179 cursor.key_beg.delete_tid = 0;
180 cursor.key_beg.rec_type = HAMMER_RECTYPE_INODE;
181 cursor.key_beg.obj_type = 0;
182 cursor.flags = HAMMER_BTREE_GET_RECORD | HAMMER_BTREE_GET_DATA;
184 *errorp = hammer_btree_lookup(&cursor);
187 * On success the B-Tree lookup will hold the appropriate
188 * buffer cache buffers and provide a pointer to the requested
189 * information. Copy the information to the in-memory inode.
192 ip->ino_rec = cursor.record->inode;
193 ip->ino_data = cursor.data->inode;
195 hammer_cache_node(cursor.node, &ip->cache);
196 hammer_done_cursor(&cursor);
199 * On success load the inode's record and data and insert the
200 * inode into the B-Tree. It is possible to race another lookup
201 * insertion of the same inode so deal with that condition too.
204 hammer_ref(&ip->lock);
205 if (RB_INSERT(hammer_ino_rb_tree, &hmp->rb_inos_root, ip)) {
206 hammer_uncache_node(&ip->cache);
207 hammer_unref(&ip->lock);
219 * Create a new filesystem object, returning the inode in *ipp. The
220 * returned inode will be referenced but not locked.
222 * The inode is created in-memory and will be delay-synchronized to the
226 hammer_create_inode(struct hammer_transaction *trans, struct vattr *vap,
227 struct ucred *cred, struct hammer_inode *dip,
228 struct hammer_inode **ipp)
230 struct hammer_mount *hmp;
231 struct hammer_inode *ip;
234 ip = kmalloc(sizeof(*ip), M_HAMMER, M_WAITOK|M_ZERO);
235 ip->obj_id = ++hmp->last_ino;
236 KKASSERT(ip->obj_id != 0);
237 ip->obj_asof = HAMMER_MAX_TID; /* XXX */
239 ip->flags = HAMMER_INODE_DDIRTY | HAMMER_INODE_RDIRTY |
241 ip->last_tid = trans->tid;
243 RB_INIT(&ip->rec_tree);
245 ip->ino_rec.ino_atime = trans->tid;
246 ip->ino_rec.ino_mtime = trans->tid;
247 ip->ino_rec.ino_size = 0;
248 ip->ino_rec.ino_nlinks = 0;
250 ip->ino_rec.base.rec_id = ++hmp->rootvol->ondisk->vol0_recid;
251 hammer_modify_volume(hmp->rootvol);
252 KKASSERT(ip->ino_rec.base.rec_id != 0);
253 ip->ino_rec.base.base.obj_id = ip->obj_id;
254 ip->ino_rec.base.base.key = 0;
255 ip->ino_rec.base.base.create_tid = trans->tid;
256 ip->ino_rec.base.base.delete_tid = 0;
257 ip->ino_rec.base.base.rec_type = HAMMER_RECTYPE_INODE;
258 ip->ino_rec.base.base.obj_type = hammer_get_obj_type(vap->va_type);
260 ip->ino_data.version = HAMMER_INODE_DATA_VERSION;
261 ip->ino_data.mode = vap->va_mode;
262 ip->ino_data.ctime = trans->tid;
263 ip->ino_data.parent_obj_id = (dip) ? dip->ino_rec.base.base.obj_id : 0;
264 if (vap->va_vaflags & VA_UID_UUID_VALID)
265 ip->ino_data.uid = vap->va_uid_uuid;
267 hammer_guid_to_uuid(&ip->ino_data.uid, vap->va_uid);
268 if (vap->va_vaflags & VA_GID_UUID_VALID)
269 ip->ino_data.gid = vap->va_gid_uuid;
271 hammer_guid_to_uuid(&ip->ino_data.gid, vap->va_gid);
273 hammer_ref(&ip->lock);
274 if (RB_INSERT(hammer_ino_rb_tree, &hmp->rb_inos_root, ip)) {
275 hammer_unref(&ip->lock);
276 panic("hammer_create_inode: duplicate obj_id");
283 hammer_rel_inode(struct hammer_inode *ip)
285 /* XXX check last ref */
286 hammer_unref(&ip->lock);
290 * Unload and destroy the specified inode.
292 * (called via RB_SCAN)
295 hammer_unload_inode(struct hammer_inode *ip, void *data __unused)
297 KKASSERT(ip->lock.refs == 0);
298 KKASSERT(ip->vp == NULL);
299 hammer_ref(&ip->lock);
300 RB_REMOVE(hammer_ino_rb_tree, &ip->hmp->rb_inos_root, ip);
302 hammer_uncache_node(&ip->cache);
310 * A transaction has modified an inode, requiring a new record and possibly
311 * also data to be written out.
314 hammer_modify_inode(struct hammer_transaction *trans,
315 struct hammer_inode *ip, int flags)
318 ip->last_tid = trans->tid;
321 /************************************************************************
322 * HAMMER INODE MERGED-RECORD FUNCTIONS *
323 ************************************************************************
325 * These functions augment the B-Tree scanning functions in hammer_btree.c
326 * by merging in-memory records with on-disk records.
329 hammer_record_ondisk_t
330 hammer_ip_first(hammer_cursor_t cursor, struct hammer_inode *ip)
336 hammer_record_ondisk_t
337 hammer_ip_next(hammer_cursor_t cursor)
344 hammer_ip_resolve_data(hammer_cursor_t cursor)
351 * Access the filesystem buffer containing the cluster-relative byte
352 * offset, validate the buffer type, load *bufferp and return a
353 * pointer to the requested data. The buffer is reference and locked on
356 * If buf_type is 0 the buffer is assumed to be a pure-data buffer and
357 * no type or crc check is performed.
359 * If *bufferp is not NULL on entry it is assumed to contain a locked
360 * and referenced buffer which will then be replaced.
362 * If the caller is holding another unrelated buffer locked it must be
363 * passed in reorderbuf so we can properly order buffer locks.
365 * XXX add a flag for the buffer type and check the CRC here XXX
368 hammer_bread(hammer_cluster_t cluster, int32_t cloff,
369 u_int64_t buf_type, int *errorp,
370 struct hammer_buffer **bufferp)
372 hammer_buffer_t buffer;
377 * Load the correct filesystem buffer, replacing *bufferp.
379 buf_no = cloff / HAMMER_BUFSIZE;
381 if (buffer == NULL || buffer->cluster != cluster ||
382 buffer->buf_no != buf_no) {
384 hammer_unlock(&buffer->io.lock);
385 hammer_rel_buffer(buffer, 0);
387 buffer = hammer_get_buffer(cluster, buf_no, 0, errorp);
391 hammer_lock_ex(&buffer->io.lock);
395 * Validate the buffer type
397 buf_off = cloff & HAMMER_BUFMASK;
399 if (buf_type != buffer->ondisk->head.buf_type) {
400 kprintf("BUFFER HEAD TYPE MISMATCH %llx %llx\n",
401 buf_type, buffer->ondisk->head.buf_type);
405 if (buf_off < sizeof(buffer->ondisk->head)) {
406 kprintf("BUFFER OFFSET TOO LOW %d\n", buf_off);
413 * Return a pointer to the buffer data.
416 return((char *)buffer->ondisk + buf_off);