struct hammer_inode_data ino_data; /* in-memory cache */
struct hammer_rec_rb_tree rec_tree; /* in-memory cache */
int rec_generation;
- struct hammer_node_cache cache[2]; /* search initiate cache */
+ struct hammer_node_cache cache[4]; /* search initiate cache */
/*
* When a demark is created to synchronize an inode to
extern int64_t hammer_stats_btree_elements;
extern int64_t hammer_stats_btree_splits;
extern int64_t hammer_stats_btree_iterations;
+extern int64_t hammer_stats_btree_root_iterations;
extern int64_t hammer_stats_record_iterations;
extern int64_t hammer_stats_file_read;
extern int64_t hammer_stats_file_write;
hammer_inode_t dip, int64_t obj_id,
hammer_tid_t asof, u_int32_t localization,
int flags, int *errorp);
+struct hammer_inode *hammer_find_inode(hammer_transaction_t trans,
+ int64_t obj_id, hammer_tid_t asof,
+ u_int32_t localization);
void hammer_scan_inode_snapshots(hammer_mount_t hmp,
hammer_inode_info_t iinfo,
int (*callback)(hammer_inode_t ip, void *data),
node = NULL;
}
}
+ if (node == NULL)
+ ++hammer_stats_btree_root_iterations;
} else {
node = NULL;
+ ++hammer_stats_btree_root_iterations;
}
/*
int flags, int *errorp)
{
hammer_mount_t hmp = trans->hmp;
+ struct hammer_node_cache *cachep;
struct hammer_inode_info iinfo;
struct hammer_cursor cursor;
struct hammer_inode *ip;
ip->flags = flags & HAMMER_INODE_RO;
ip->cache[0].ip = ip;
ip->cache[1].ip = ip;
+ ip->cache[2].ip = ip;
+ ip->cache[3].ip = ip;
if (hmp->ronly)
ip->flags |= HAMMER_INODE_RO;
ip->sync_trunc_off = ip->trunc_off = ip->save_trunc_off =
* access the current version of the root inode and (if it is not
* a master) always access information under it with a snapshot
* TID.
+ *
+ * We cache recent inode lookups in this directory in dip->cache[2].
+ * If we can't find it we assume the inode we are looking for is
+ * close to the directory inode.
*/
retry:
- hammer_init_cursor(trans, &cursor, (dip ? &dip->cache[0] : NULL), NULL);
+ cachep = NULL;
+ if (dip) {
+ if (dip->cache[2].node)
+ cachep = &dip->cache[2];
+ else
+ cachep = &dip->cache[0];
+ }
+ hammer_init_cursor(trans, &cursor, cachep, NULL);
cursor.key_beg.localization = localization + HAMMER_LOCALIZE_INODE;
cursor.key_beg.obj_id = ip->obj_id;
cursor.key_beg.key = 0;
* The assumption is that it is near the directory inode.
*
* cache[1] tries to cache the location of the object data.
- * The assumption is that it is near the directory data.
+ * We might have something in the governing directory from
+ * scan optimizations (see the strategy code in
+ * hammer_vnops.c).
+ *
+ * We update dip->cache[2], if possible, with the location
+ * of the object inode for future directory shortcuts.
*/
hammer_cache_node(&ip->cache[0], cursor.node);
- if (dip && dip->cache[1].node)
- hammer_cache_node(&ip->cache[1], dip->cache[1].node);
+ if (dip) {
+ if (dip->cache[3].node) {
+ hammer_cache_node(&ip->cache[1],
+ dip->cache[3].node);
+ }
+ hammer_cache_node(&dip->cache[2], cursor.node);
+ }
/*
* The file should not contain any data past the file size
ip->flags = flags | HAMMER_INODE_RO | HAMMER_INODE_DUMMY;
ip->cache[0].ip = ip;
ip->cache[1].ip = ip;
+ ip->cache[2].ip = ip;
+ ip->cache[3].ip = ip;
ip->sync_trunc_off = ip->trunc_off = ip->save_trunc_off =
0x7FFFFFFFFFFFFFFFLL;
RB_INIT(&ip->rec_tree);
}
/*
+ * Return a referenced inode only if it is in our inode cache.
+ *
+ * Dummy inodes do not count.
+ */
+struct hammer_inode *
+hammer_find_inode(hammer_transaction_t trans, int64_t obj_id,
+ hammer_tid_t asof, u_int32_t localization)
+{
+ hammer_mount_t hmp = trans->hmp;
+ struct hammer_inode_info iinfo;
+ struct hammer_inode *ip;
+
+ iinfo.obj_id = obj_id;
+ iinfo.obj_asof = asof;
+ iinfo.obj_localization = localization;
+loop:
+ ip = hammer_ino_rb_tree_RB_LOOKUP_INFO(&hmp->rb_inos_root, &iinfo);
+ if (ip) {
+ if (ip->flags & HAMMER_INODE_DUMMY)
+ ip = NULL;
+ else
+ hammer_ref(&ip->lock);
+ }
+ return(ip);
+}
+
+/*
* Create a new filesystem object, returning the inode in *ipp. The
* returned inode will be referenced. The inode is created in-memory.
*
HAMMER_INODE_ATIME | HAMMER_INODE_MTIME;
ip->cache[0].ip = ip;
ip->cache[1].ip = ip;
+ ip->cache[2].ip = ip;
+ ip->cache[3].ip = ip;
ip->trunc_off = 0x7FFFFFFFFFFFFFFFLL;
/* ip->save_trunc_off = 0; (already zero) */
KKASSERT(ip->lock.refs == 1);
hammer_uncache_node(&ip->cache[0]);
hammer_uncache_node(&ip->cache[1]);
+ hammer_uncache_node(&ip->cache[2]);
+ hammer_uncache_node(&ip->cache[3]);
hammer_inode_wakereclaims(ip, 1);
if (ip->objid_cache)
hammer_clear_objid(ip);
int64_t hammer_stats_btree_elements;
int64_t hammer_stats_btree_splits;
int64_t hammer_stats_btree_iterations;
+int64_t hammer_stats_btree_root_iterations;
int64_t hammer_stats_record_iterations;
int64_t hammer_stats_file_read;
&hammer_stats_btree_splits, 0, "");
SYSCTL_QUAD(_vfs_hammer, OID_AUTO, stats_btree_iterations, CTLFLAG_RD,
&hammer_stats_btree_iterations, 0, "");
+SYSCTL_QUAD(_vfs_hammer, OID_AUTO, stats_btree_root_iterations, CTLFLAG_RD,
+ &hammer_stats_btree_root_iterations, 0, "");
SYSCTL_QUAD(_vfs_hammer, OID_AUTO, stats_record_iterations, CTLFLAG_RD,
&hammer_stats_record_iterations, 0, "");
{
struct hammer_transaction trans;
struct hammer_inode *ip;
+ struct hammer_inode *dip;
struct hammer_cursor cursor;
hammer_base_elm_t base;
hammer_off_t disk_offset;
biodone(ap->a_bio);
done:
+ /*
+ * Cache the b-tree node for the last data read in cache[1].
+ *
+ * If we hit the file EOF then also cache the node in the
+ * governing director's cache[3], it will be used to initialize
+ * the inode's cache[1] for any inodes looked up via the directory.
+ *
+ * This doesn't reduce disk accesses since the B-Tree chain is
+ * likely cached, but it does reduce cpu overhead when looking
+ * up file offsets for cpdup/tar/cpio style iterations.
+ */
if (cursor.node)
hammer_cache_node(&ip->cache[1], cursor.node);
+ if (ran_end >= ip->ino_data.size) {
+ dip = hammer_find_inode(&trans, ip->ino_data.parent_obj_id,
+ ip->obj_asof, ip->obj_localization);
+ if (dip) {
+ hammer_cache_node(&dip->cache[3], cursor.node);
+ hammer_rel_inode(dip, 0);
+ }
+ }
hammer_done_cursor(&cursor);
hammer_done_transaction(&trans);
return(error);