nfs-ganesha cache代码分析,转mdcache readdir

一,nfs-ganesha版本2.3.3 ,2.4.5cache分析

https://github.com/zanglinjie/nfs-ganesha点击打开链接

mdcache在2.4.0之后放在了FSAL层,对应的目录为src\FSAL\Stackable_FSALs\FSAL_MDCACHE

2.4版本ganesha缓存配置块

struct config_block mdcache_param_blk = {
	.dbus_interface_name = "org.ganesha.nfsd.config.cache_inode",
	.blk_desc.name = "CacheInode",
	.blk_desc.type = CONFIG_BLOCK,
	.blk_desc.u.blk.init = mdcache_param_init,
	.blk_desc.u.blk.params = mdcache_params,
	.blk_desc.u.blk.commit = noop_conf_commit
};

ganesha框架图采用模块化的设计不易实现,但易于维护


二,cache_inode_lookup 与mdcache_lookup 

2.1cache_inode_lookup

2.2mdcahce_lookup

函数定义:

fsal_status_t mdc_lookup(mdcache_entry_t *mdc_parent, const char *name,
			 bool uncached, mdcache_entry_t **new_entry,
			 struct attrlist *attrs_out)

流程图:
mdcache_entry_t结构体!!!如下:

typedef struct mdcache_fsal_obj_handle mdcache_entry_t;

mdcache_entry_t结构体是一个对象(文件/目录等)cache的实体,mdc_parent是父目录存在于cache中的cache实例。
mdcache_entry->fsobj.fsdir.parent保存了父目录的信息,如果是查找父目录的话,直接调用mdcache_locate_host将mdcache_entry->fsobj.fsdir.parent转化为父目录的mdcache_entry_t条目返回即可。
接下来调用mdc_try_get_cached,通过mdc_parent和name查找cache,如果不存在的话,我们调用mdc_lookup_uncached。
struct mdcache_fsal_obj_handle {
	/** Reader-writer lock for attributes */
	pthread_rwlock_t attr_lock;
	/** MDCache FSAL Handle */
	struct fsal_obj_handle obj_handle;
	/** Sub-FSAL handle */
	struct fsal_obj_handle *sub_handle;
	/** Cached attributes */
	struct attrlist attrs;
	/** FH hash linkage */
	struct {
		struct avltree_node node_k;	/*< AVL node in tree */
		mdcache_key_t key;	/*< Key of this entry */
		bool inavl;
	} fh_hk;
	/** Flags for this entry */
	uint32_t mde_flags;
	/** Time at which we last refreshed attributes. */
	time_t attr_time;
	/** Time at which we last refreshed acl. */
	time_t acl_time;
	/** New style LRU link */
	mdcache_lru_t lru;
	/** Exports per entry (protected by attr_lock) */
	struct glist_head export_list;
	/** ID of the first mapped export for fast path
	 *  This is an int32_t because we need it to be -1 to indicate
	 *  no mapped export.
	 */
	int32_t first_export_id;
	/** Lock on type-specific cached content.  See locking
	    discipline for details. */
	pthread_rwlock_t content_lock;
	/** Filetype specific data, discriminated by the type field.
	    Note that data for special files is in
	    attributes.rawdev */
	union mdcache_fsobj {
		struct state_hdl hdl;
		struct {
			/** List of chunks in this directory, not ordered */
			struct glist_head chunks;
			/** List of detached directory entries. */
			struct glist_head detached;
			/** Spin lock to protect the detached list. */
			pthread_spinlock_t spin;
			/** Count of detached directory entries. */
			int detached_count;
			/** @todo FSF
			 *
			 * This is somewhat fragile, however, a reorganization
			 * is possible. If state_lock was to be moved into
			 * state_file and state_dir, and the state code was
			 * made clear which it was working with, dhdl could
			 * be replaced with a state_dir which would be
			 * smaller than state_file, and then the additional
			 * members of fsdir would basically overlay
			 * the larger state_file that hdl is.
			 *
			 * Such a reorg could save memory AND make for a
			 * crisper interface.
			 */
			struct state_hdl dhdl; /**< Storage for dir state */
			/** The parent host-handle of this directory ('..') */
			struct gsh_buffdesc parent;
			/** The first dirent cookie in this directory.
			 *  0 if not known.
			 */
			fsal_cookie_t first_ck;
			struct {
				/** Children by name hash */
				struct avltree t;
				/** Table of dirents by FSAL cookie */
				struct avltree ck;
				/** Table of dirents in sorted order. */
				struct avltree sorted;
				/** Heuristic. Expect 0. */
				uint32_t collisions;
			} avl;
		} fsdir;		/**< DIRECTORY data */
	} fsobj;
}

2.2.1 mdc_lookup_uncached函数,了解元数据怎么加入cache中之后,就会理解怎么查找。


在调用了FSAL文件系统(CEPH)的lookup之后,进行cache的创建操作,这里主要看mdcache_alloc_and_check_handle流程:



看流程比较清晰,做三个事情:
1、新建条目。
2、增加到父目录的avl树中。
3、针对目录保存父目录的key。

下面分别将这三件事情做了什么详细说明:

2.2.2新建条目


这个是最复杂的事情,首先还是照常先看流程图:



这里着重讨论新建部分,主要是针对目录的avl树的初始化,以及加入到全局的avl树中。
对于目录来说,会初始化它子目录的avl树,以缓存所有的子目录和文件。下面是 mdcache_entry->fsobj.fsdir.avl的结构的定义。

struct {
      /*目录下条目的avl树Children by name hash */
      struct avltree t;
       /*删除条目的avl树 删除*/
      struct avltree c;
      /** FSAL的cookie的构成的avl树 */
      struct avltree ck;
      /**排序的avl树 需要支持fso_compute_readdir_cookie, 暂时不分析*/
      struct avltree sorted;
      /** 冲突标记0,暂时不知道搞什么飞机的东西. */
      uint32_t collisions;
} avl;

主要是两棵树,t 和 ck ,t保存以文件名的hash值作为比较值的avl树,可以用来查找某个文件,而ck则构建的以子文件或目录在文件夹中的offset为比较值的avl树,主要用来列举目录的所有或部分条目。
主要对这几个树进行初始化。

然后加入到全局的avl树(key的hash值作为avl树的比较值)中。

struct cih_lookup_table结构体保存的全局的cache,默认有7个分区(配置文件中Nparts设置),每个分区存在一个avl树,而且每个分区有cache字段,直接cache了32633个cache条目。全局cache查找策略是,通过key的hash直接查找,如果在cache没有查到,才到avl树中找,找到了之后替换掉cache。

struct cih_lookup_table {
	GSH_CACHE_PAD(0);
	cih_partition_t *partition;
	uint32_t npart;
	uint32_t cache_sz;
};

/* Support inline lookups */
extern struct cih_lookup_table cih_fhcache;

增加到父目录的avl树中

主要做到是事情是新建一个mdcache_dir_entry_t条目,将其加入到的avl树(t, ck)中。

2.2.3 针对目录保存父目录的key


这里在上面提过,如果查找到的是目录,需要将父目录的key赋值给目录cache条目的 fsobj.fsdir.parent字段,方便lookup..的查找。

mdc_try_get_cached存在cache的流程

主要是在parent的avl树t中查找,如果查到了,在全局的avl树中确认存在,即返回。

cache每个avl树的作用的简单总结

全局avl树的作用
通过key快速查询mdcache_entry_t条目信息。

目录avl树中的t
查找子文件或者目录时使用。

目录avl树中的ck
主要是readdir使用


三,cache_inode_readdir与mdcache_readdir

mdcache_readdir
       在2.5.0之后的版本中,加入了readdir chunk,可以不完全将目录存入cache中,默认每个chunk存128个条目(可以通过Dir_Chunk设置),整个系统chunk的水线为10000(可以通过Chunks_HWMark设置)。chunk也有自己的lru列表,如果超过chunk的数目,就会被踢掉。

    if (test_mde_flags(directory, MDCACHE_BYPASS_DIRCACHE)) {
        /* Not caching dirents; pass through directly to FSAL */
        return mdcache_readdir_uncached(directory, whence, dir_state,
                        cb, attrmask, eod_met);
    }
    if (mdcache_param.dir.avl_chunk > 0) {
        /* Dirent chunking is enabled. */
        LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE,
                "Calling mdcache_readdir_chunked whence=%"PRIx64,
                whence ? *whence : (uint64_t) 0);
        return mdcache_readdir_chunked(directory,
                           whence ? *whence : (uint64_t) 0,
                           dir_state, cb, attrmask,
                           eod_met);
    }

    MDCACHE_BYPASS_DIRCACHE标记目前只有在没有开启chunk的情况下,如果目录过大,会打上此标记,不会被缓存,开启了chunk,此标记永远失效。除了这两行之外,后面的操作是在没有开启chunk的情况下的流程,暂时不做分析。所以mdcache_readdir其实是执行了mdcache_readdir_chunked函数。

mdcache_readdir_chunked的流程如下:



还是和lookup一样,我们先看没有找到的流程。

3.1  mdcache_populate_dir_chunk

流程如下:



做了3件事情:
1、mdcache_get_chunk。
2、调用本地的readdir操作。
3、本地的readdir调用回调函数mdc_readdir_chunk_object。

3.1.1 mdcache_get_chunk
这个函数是获取一个可用的chunk,如果全局的chunk数达到了阈值,就从lru中淘汰出一个。

3.1.2 调用本地的readdir操作
本地读取数据

3.1.3 本地每读取一个数据,调用一次mdc_readdir_chunk_object回调函数,我们照常看看mdc_readdir_chunk_object的流程


其实可以发现,很多事情和lookup类似的。与lookup不同的是,lookup如果没有找到,会将chunk置无效(compute_readdir_cookie没有实现的情况下,如果实现了,会加入chunk中),而在这里,chunk的结构体中,存在一个dirents参数,这里保存这个chunk的所有文件的链表。

3.2 mdcache_avl_lookup_ck
现在我们回头看看直接在cache中找的函数,其实就是遍历ck的过程。在mdcache_entry_t结构体中,存在一个 first_ck的字段,作为一个目录ck的初始值,每个chunk中存这next_ck的字段,就可以进行ck的遍历操作。

3.3 chunk->dirents的loop操作
针对每个ck查找到的chunk,对chunk->dirents 的loop操作,对每个cache条目,调用上层的回调函数,已完成readdir的操作。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值