天天看點

建立nfs4_state_owner結構和nfs4_state結構

        nfs4_state_owner代表了用戶端的一個使用者,包含了這個使用者打開的所有檔案的資訊。每個檔案用資料結構nfs4_state表示,每打開一個檔案就向nfs4_state_owner中添加一個nfs4_state結構。是以,OPEN操作中首先要檢查用戶端是否為目前使用者建立了nfs4_state_owner結構,如果沒有就建立一個。這個流程是由函數nfs4_get_state_owner()實作的。

參數server:表示一個NFS檔案系統

參數cred:這是使用者資訊的資料結構,表示用戶端的一個使用者

參數gfp_flags:這是配置設定記憶體時使用的标志位

struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
                                              struct rpc_cred *cred,
                                              gfp_t gfp_flags)
{
        struct nfs_client *clp = server->nfs_client;    // 找到NFS用戶端的資料結構
        struct nfs4_state_owner *sp, *new;

        spin_lock(&clp->cl_lock);
        // 在server->state_owners中查找符合條件的nfs4_state_owner結構,查詢條件是cred,每個cred代表一個使用者.
        // 傳回符合條件的nfs4_state_owner結構,并增加該結構的引用計數。
        // 如果nfs4_state_owner已經挂載到lru連結清單中了,就從lru連結清單中删除.
        // 如果不存在符合條件的nfs4_state_owner結構,就傳回NULL.
        sp = nfs4_find_state_owner_locked(server, cred);
        spin_unlock(&clp->cl_lock);
        if (sp != NULL)         // 找到了,直接退出就可以了.
                goto out;

	// 目前使用者還沒有建立nfs4_state_owner結構.為目前使用者建立一個新的nfs4_state_owner結構.
	// 初始化引用計數為1.
        new = nfs4_alloc_state_owner(server, cred, gfp_flags);
        if (new == NULL)
                goto out;       // 建立過程失敗,配置設定記憶體過程出錯了.
        do {
		// 為新建立的nfs4_state_owner結構配置設定編号
                if (ida_pre_get(&server->openowner_id, gfp_flags) == 0)
                        break;		// 配置設定編号過程出錯了
                spin_lock(&clp->cl_lock);
		// 将新建立的nfs4_state_owner結構添加到紅黑樹中,并且設定了編号.
		// 由于nfs4_alloc_state_owner()配置設定記憶體時可能休眠,這個過程中其他程序可能
		// 建立了新的nfs4_state_ower結構,是以nfs4_insert_state_owner_locked()
		// 将new添加到紅黑樹之前又查找了一次,如果找到了符合條件的nfs4_state_owner結構
		// 就不添加了,這種情況下sp和new就不相同了.
                sp = nfs4_insert_state_owner_locked(new);
                spin_unlock(&clp->cl_lock);
        } while (sp == ERR_PTR(-EAGAIN));
        if (sp != new)  // 查找到了符合條件的nfs4_state_owner結構,就可以釋放剛剛建立的結構了.
                nfs4_free_state_owner(new);
out:
        // 檢查LRU連結清單中過期的nfs4_state_owner結構,删除這些結構.
        // 當nfs4_state_owner在lru連結清單中的時間超過了90秒就說明過期了,就需要删除了.
        // 90秒内的nfs4_state_owner不允許删除,因為這是剛釋放的,有可能還被其他程序使用.
        nfs4_gc_state_owners(server);
        return sp;
}
           

        NFS檔案系統中所有的nfs4_state_owner結構儲存在一棵紅黑樹中,紅黑樹的根節點是nfs_server結構中的state_owners。暫時不清楚為什麼要使用一棵紅黑樹,可能是為大系統準備的。查找nfs4_state_owner結構的函數是nfs4_find-state_owner_locked(),這個函數将目前使用者的資訊和紅黑樹中每個節點中的使用者資訊進行比較,使用者資訊相同就說明是同一個使用者的結構。

參數server:表示一個NFS檔案系統

參數cred:這是使用者資訊

// 查找符合條件的nfs4_state_owner結構,在nfs_server->state_owners構成的紅黑樹中進行查找.
// (1) 在紅黑樹中找到了,增加引用計數,然後傳回.
// (2) 在紅黑樹中找到了,但是也連結到lru連結清單中了,先從lru連結清單中删除,然後增加引用計數,傳回
// (3) 很可惜,沒有找到,直接傳回NULL.
static struct nfs4_state_owner *
nfs4_find_state_owner_locked(struct nfs_server *server, struct rpc_cred *cred)
{
        // 取出nfs_server結構中儲存nfs4_state_owner結構的紅黑樹
        struct rb_node **p = &server->state_owners.rb_node,
                       *parent = NULL;
        struct nfs4_state_owner *sp;

        while (*p != NULL) {
                parent = *p;
                // 取出紅黑樹節點中關聯的nfs4_state_owner結構
                sp = rb_entry(parent, struct nfs4_state_owner, so_server_node); 

                // 檢查是否是我們查找的節點,檢查條件就是使用者資訊.
                if (cred < sp->so_cred)
                        p = &parent->rb_left;
                else if (cred > sp->so_cred)
                        p = &parent->rb_right;
                else {          // 相等了,找到目前使用者了.
                        if (!list_empty(&sp->so_lru))   // 這個結構已經連結到lru連結清單中了
                                list_del_init(&sp->so_lru);     // 先從lru連結清單中删除,我們要開始使用這個結構了,不需要回收了.
                        atomic_inc(&sp->so_count);      // 增加這個結構的引用計數
                        return sp;      // 這就是我們要使用的nfs4_state_owner結構.
                }
        }
        return NULL;    // 可惜了,沒有找到.
}
           

        如果在紅黑樹中沒有找到符合條件的nfs4_state_ower結構,就需要建立一個新的nfs4_state_owner結構,建立nfs4_state_owner結構的函數是nfs4_alloc_state_owner()。

參數server:表示一個NFS檔案系統

參數cred:這是使用者資訊的資料結構,表示用戶端的一個使用者

參數gfp_flags:這是配置設定記憶體時使用的标志位

static struct nfs4_state_owner *
nfs4_alloc_state_owner(struct nfs_server *server,
                struct rpc_cred *cred,
                gfp_t gfp_flags)
{
        struct nfs4_state_owner *sp;

        sp = kzalloc(sizeof(*sp), gfp_flags);           // 配置設定記憶體
        if (!sp)
                return NULL;            // 配置設定記憶體過程失敗,直接傳回.
        sp->so_server = server;                 // 所屬的nfs_server結構
        sp->so_cred = get_rpccred(cred);        // 這是使用者資訊
        spin_lock_init(&sp->so_lock);           // 保護這個結構的自旋鎖
        INIT_LIST_HEAD(&sp->so_states);         // 初始化nfs4_state結構所在的連結清單
        nfs4_init_seqid_counter(&sp->so_seqid); // 初始化nfs_seqid_counter結構
        atomic_set(&sp->so_count, 1);           // 初始化引用計數為1
        INIT_LIST_HEAD(&sp->so_lru);            // 當nfs4_state_owner不再使用時可以挂載到lru連結清單中.
        return sp;
}
           

        nfs4_alloc_state_owner()的流程很簡單,就是配置設定一塊記憶體,然後初始化nfs4_state_owner結構中的各個字段,so_seqid字段的初始化是在函數nfs4_init_seqid_counter()中完成的。

static void
nfs4_init_seqid_counter(struct nfs_seqid_counter *sc)
{
        sc->create_time = ktime_get();          // 建立時間
        sc->flags = 0;
        sc->counter = 0;        // 初始化計數為0,這是RPC等待隊列中任務(nfs_seqid結構)的數量.
        spin_lock_init(&sc->lock);      // 初始化自旋鎖
        INIT_LIST_HEAD(&sc->list);      // 初始化連結清單頭為空
        // 這個隊列中放的是RPC任務.
        rpc_init_wait_queue(&sc->wait, "Seqid_waitqueue");      // 初始化RPC等待隊列
}
           

        但是nfs4_init_seqid_counter()并沒有初始化nfs_seqid_counter結構中的owner_id字段,owner_id是nfs4_state_owner結構的編号,這個編号是依靠idr機制實作的。簡單來說,idr就是一種編号配置設定機制,idr負責給要管理的對象配置設定一個編号,可以根據這個編号找到被管理的對象。owner_id是在函數nfs4_insert_state_owner_locked()中設定的。

static struct nfs4_state_owner *
nfs4_insert_state_owner_locked(struct nfs4_state_owner *new)
{
        struct nfs_server *server = new->so_server;     // 找到nfs_server結構.
        struct rb_node **p = &server->state_owners.rb_node,     // 這是紅黑樹的根節點.
                       *parent = NULL;
        struct nfs4_state_owner *sp;
        int err;

        while (*p != NULL) {
                parent = *p;
                sp = rb_entry(parent, struct nfs4_state_owner, so_server_node); // 取出nfs4_state_owner結構.

                if (new->so_cred < sp->so_cred)
                        p = &parent->rb_left;   // 向左查找
                else if (new->so_cred > sp->so_cred)
                        p = &parent->rb_right;  // 向右查找
                else {          // 找到符合條件的nfs4_state_owner結構了
                        if (!list_empty(&sp->so_lru))
                                list_del_init(&sp->so_lru);     // 先從lru連結清單中删除.
                        atomic_inc(&sp->so_count);      // 增加引用計數
                        return sp;      // 然後傳回.
                }
        }
        // 好吧,不存在符合條件的nfs4_state_owner結構,可以使用new了.
        // 這裡在配置設定owner_id,每個owner_id代表了一個nfs4_state_owner結構.
        err = ida_get_new(&server->openowner_id, &new->so_seqid.owner_id);
        if (err)
                return ERR_PTR(err);
        // 将新配置設定的nfs4_state_owner結構插入到紅黑樹中
        rb_link_node(&new->so_server_node, parent, p);
        rb_insert_color(&new->so_server_node, &server->state_owners);
        return new;
}
           

        一般情況下nfs4_state_owner儲存在一棵紅黑樹中,當nfs4_state_owner廢棄不用時(比如使用者關閉了檔案)會連結到一個LRU連結清單中。nfs4_get_state_owner()會調用nfs4_gc_state_owners()清理LRU連結清單。如果nfs4_state_owner結構加入到LRU連結清單中的時間超過了90秒就會被釋放。

static void nfs4_gc_state_owners(struct nfs_server *server)
{
        struct nfs_client *clp = server->nfs_client;    // 找到NFS用戶端結構
        struct nfs4_state_owner *sp, *tmp;
        unsigned long time_min, time_max;
        LIST_HEAD(doomed);              // 初始化一個臨時連結清單

        spin_lock(&clp->cl_lock);
        time_max = jiffies;             // 現在時刻
        time_min = (long)time_max - (long)clp->cl_lease_time;   // cl_lease_time = 90秒
        // 周遊LRU連結清單中每一個nfs4_state_owner結構
        list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
                /* NB: LRU is sorted so that oldest is at the head */
                // so_expires表示sp添加到LRU連結清單中的時間
                if (time_in_range(sp->so_expires, time_min, time_max))
                        break;          // 如果添加到LRU連結清單中的時間不超過90秒,就不處理了.
                // 添加到LRU連結清單中的時間超過了90秒,清理LRU連結清單.
                list_move(&sp->so_lru, &doomed);        // 移動到臨時連結清單doomed中.
                nfs4_remove_state_owner_locked(sp);     // 這個函數從紅黑樹中删除sp,釋放id
        }
        spin_unlock(&clp->cl_lock);

        // 釋放臨時連結清單中所有的nfs4_state_owner結構.
        list_for_each_entry_safe(sp, tmp, &doomed, so_lru) {
                list_del(&sp->so_lru);
                nfs4_free_state_owner(sp);      // 釋放nfs4_state_owner結構
        }
}
           

        nfs4_state表示一個檔案的打開狀态,建立完nfs4_state_owner後還需要建立一個nfs4_state結構,這是由函數nfs4_get_open_state實作的,這個函數的流程如下:

參數inode:這是檔案索引節點,表示我們要打開的檔案

參數owner:這是前面建立的nfs4_state_owner結構,表示用戶端一個使用者

struct nfs4_state *
nfs4_get_open_state(struct inode *inode, struct nfs4_state_owner *owner)
{
	struct nfs4_state *state, *new;
	struct nfs_inode *nfsi = NFS_I(inode);		// 取出nfs_inode結構

	spin_lock(&inode->i_lock);		// 加鎖
	// 查找符合條件的nfs4_state結構。如果不存在,這個函數會傳回NULL.
	// 如果存在就增加state的引用計數,因為我們要使用這個資料結構了.
	state = __nfs4_find_state_byowner(inode, owner);
	spin_unlock(&inode->i_lock);		// 解鎖
	if (state)
		goto out;		// 找到了
	// 沒有找到,那麼就建立一個新的nfs4_state結構,初始化引用計數為1.
	new = nfs4_alloc_open_state();
	spin_lock(&owner->so_lock);
	spin_lock(&inode->i_lock);
	// 由于nfs4_alloc_open_state()配置設定記憶體過程中可能休眠,再次查找是否存在符合條件的nfs4_state結構.
	state = __nfs4_find_state_byowner(inode, owner);
	// 使用新建立的nfs4_state結構.
	if (state == NULL && new != NULL) {
		state = new;		// OK
		state->owner = owner;		// 所屬于的nfs4_state_owner結構
		// 因為添加了一個nfs4_state結構,是以需要增加nfs4_state_owner的引用計數.
		atomic_inc(&owner->so_count);
		list_add(&state->inode_states, &nfsi->open_states);	// 挂載到nfs_inode中
		ihold(inode);
		state->inode = inode;		// 設定檔案索引節點
		spin_unlock(&inode->i_lock);
		/* Note: The reclaim code dictates that we add stateless
		 * and read-only stateids to the end of the list */
		// 挂載到nfs4_state_owner結構的連結清單中
		list_add_tail(&state->open_states, &owner->so_states);
		spin_unlock(&owner->so_lock);
	} else {	// state==NULL&&new==NULL   state!=NULL&new==NULL  state!=NULL&new!=NULL
		spin_unlock(&inode->i_lock);
		spin_unlock(&owner->so_lock);
		if (new)
			nfs4_free_open_state(new);	// 直接釋放記憶體
	}
out:
	return state;
}
           

繼續閱讀