返還delegation

    現在可以講解delegation的返還過程了。像上一篇文章中講到的那樣,當文件的訪問過程發生衝突時,服務器會向客戶端發送CB_RECALL請求。客戶端接收到CB_RECALL請求後,首先會更新關聯在這個文件上的open stateid,然後就向服務器發起DELEGRETURN請求,將delegation返回給服務器。這篇文章中主要講講delegation的返還過程。

    服務器發起CB_RECALL請求時向客戶端傳遞了三個參數:

callback_ident:這是客戶端文件系統的編號,用來區分客戶端掛載的多個NFS文件系統,這是客戶端發起SETCLIENTID時告訴服務器的。

stateid:這是要返還的delegation的stateid

filehandle:這是要返還的delegation的文件句柄。

    客戶端接收到CB_RECALL請求後,會根據callback_ident選擇NFS文件系統,然後遍歷這個文件系統中所有已經打開的文件,將文件系統跟filehandle進行比較,如果二者匹配,就說明找到了對應的文件,就是要返還這個文件關聯的delegation。客戶端處理delegation的函數如下:

參數inode:這是客戶端查找到的文件索引節點,就是要返還這個文件上關聯的delegation。

參數delegation:這是文件上關聯的delegation

參數issync:表示同步操作還是異步操作

static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
{
        struct nfs_inode *nfsi = NFS_I(inode);  // 找到nfs_inode結構
        int err;

        down_write(&nfsi->rwsem);
        // 步驟1   更新文件中關聯的open stateid
        err = nfs_delegation_claim_opens(inode, &delegation->stateid);
        up_write(&nfsi->rwsem);
        if (err)
                goto out;

        // 步驟2   返回delegation
        err = nfs_do_return_delegation(inode, delegation, issync);
out:
        return err;
}
這個函數包含兩個步驟:

步驟1:更新文件上關聯的open stateid,確保返還delegation後所有的用戶還可以繼續發起READ請求,上篇文章中已經講解過這個函數了。

步驟2:調用nfs_do_return_delegation(),向服務器發起DELEGRETURN請求,返還delegation,這是這篇文章中需要講解的函數。

static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
{
        int res = 0;

        // 將delegation返還給服務器端 這個函數發起了DELEGRETURN請求.
        res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
        nfs_free_delegation(delegation);        // 處理完畢,釋放delegation佔用的內存.
        return res;
}

nfs4_proc_delegreturn()的作用是創建一個RPC任務,構建DELEGRETURN請求報文,然後向服務器發送DELEGRETURN請求,這個構成沒什麼好講的,略過了。只需要說明一點:這個RPC任務會發起三個請求PUTFH、GETATTR、DELEGRETURN。PUTFH會將文件句柄傳遞給服務器,DELEGRETURN會將delegation的stateid傳遞給服務器,這就可以了。

    接着講服務器端的處理程序。DELEGRETURN請求的處理函數是nfsd4_delegreturn(),當服務器接收到請求報文後就會執行這個函數。

__be32
nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                  struct nfsd4_delegreturn *dr)
{
        struct nfs4_delegation *dp;             // 這是一個delegation的數據結構
        stateid_t *stateid = &dr->dr_stateid;   // 取出請求報文中的stateid
        struct nfs4_stid *s;
        struct inode *inode;
        __be32 status;

        // 解析文件句柄,檢查客戶端傳遞過來的是否是一個有效的文件句柄,
        // 查找文件句柄對應的文件.
        if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
                return status;
        inode = cstate->current_fh.fh_dentry->d_inode;  // 取出文件的索引節點結構

        nfs4_lock_state();
        // 根據stateid查找要刪除的delegation.
        status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s);
        if (status)
                goto out;
        dp = delegstateid(s);
        // 這是一個檢查函數.
        status = check_stateid_generation(stateid, &dp->dl_stid.sc_stateid, nfsd4_has_session(cstate));
        if (status)
                goto out;

        unhash_delegation(dp);  // 刪除這個delegation.
out:
        nfs4_unlock_state();

        return status;
}


    fh_verify()的作用是解析客戶端傳遞過來的文件句柄,查找對應的文件,然後檢查用戶的訪問權限。對於客戶端來說,文件句柄是透明的,客戶端無法解析也不必要解析文件句柄。但是服務器清晰文件句柄的結構,fh_verify()可以解析文件句柄,檢查客戶端傳遞過來的是否是一個有效句柄,然後找到這個文件句柄對應的文件,設置文件的索引節點結構。

    nfsd4_lookup_stateid()的作用是根據客戶端傳遞過來的stateid查找delegation。

nfsd4_delegreturn --> nfsd4_lookup_stateid

static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s)
{
        struct nfs4_client *cl;
        struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);

        // 這是兩個預留的stateid,不允許使用.
        if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
                return nfserr_bad_stateid;
        // stateid中包含了clientid,因此可以檢查這個stateid是否已經過期了.
        if (STALE_STATEID(stateid, nn))         // stateid過期了
                return nfserr_stale_stateid;
        // 查找是否存在這個NFS客戶端結構.
        cl = find_confirmed_client(&stateid->si_opaque.so_clid);
        if (!cl)
                return nfserr_expired;
        // 查找符合條件的nfs4_stid結構,可能是一個nfs4_delegation結構,也可能是一個nfs4_ol_stateid結構.
        *s = find_stateid_by_type(cl, stateid, typemask);
        if (!*s)
                return nfserr_bad_stateid;
        return nfs_ok;
}
stateid由四部分構成:

si_generation:這是一個操作序列號,執行OPEN, OPEN_CONFIRM, OPEN_DOWNGRADE後這個序列號會遞增.  4字節
cl_boot:這是服務器端NFS服務的開啓時間  4字節
cl_id:這是服務器分配給NFS客戶端編號,可以保證客戶端編號不重疊  4字節
so_id:這是服務器創建stateid生成的一個編號,服務器可以保證這個編號不重疊  4字節

cl_boot和cl_id就是客戶端的clientid。STALE_STATEID()將stateid中NFS服務的啓動時間跟服務器中NFS服務時間的啓動時間進行比較,判斷stateid是否過期了。如果兩個時間不相同,就說明服務器已經重啓了,但是客戶端還不知道這個信息,這時就會返回錯誤嗎nfserr_stale_stateid。客戶端接收到這個錯誤碼後就會進行狀態恢復操作。find_confirmed_client()的作用是檢查是否存在這個客戶端,代碼如下:

nfsd4_delegreturn --> nfsd4_lookup_stateid --> find_confirmed_client

static struct nfs4_client *
find_confirmed_client(clientid_t *clid)
{
        struct nfs4_client *clp;
        unsigned int idhashval = clientid_hashval(clid->cl_id);         // 這是這個NFS客戶端的hash值

        // 系統中所有的nfs4_client結構保存在全局鏈表conf_id_hashtbl中了.
        list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) {
                if (same_clid(&clp->cl_clientid, clid)) {       // clientid相同,是同一個客戶端.
                        renew_client(clp);
                        return clp;
                }
        }
        return NULL;
}

find_stateid_by_type()的作用是根據stateid查找對應的nfs4_stid結構,nfs4_stid是什麼結構呢?nfs4_stid是nfs4_ol_stateid和nfs4_delegation中的一個字段,因此這個函數查找的是nfs4_ol_stateid或者nfs4_delegation結構。

參數cl表示一個NFS客戶端
參數t是要查找的stateid
參數typemask是stateid的類型,取值如下:NFS4_OPEN_STID、NFS4_LOCK_STID、NFS4_DELEG_STID

nfsd4_delegreturn --> nfsd4_lookup_stateid --> find_stateid_by_type

static struct nfs4_stid *find_stateid_by_type(struct nfs4_client *cl, stateid_t *t, char typemask)
{
        struct nfs4_stid *s;

        s = find_stateid(cl, t);
        if (!s)
                return NULL;
        if (typemask & s->sc_type)      // 比較類型
                return s;
        return NULL;
}

state中包含一個字段so_id,服務器可以保證客戶端中每個stateid中的so_id的值都不相同,這是通過idr機制實現的,find_stateid()就是通過idr機制查找的。查找到nfs4_stid結構後,還需要比較類型是否一致。

    查找到delegation後,最後做的一件事情就是刪除delegation,這是通過unhash_delegation()實現的。

nfsd4_delegreturn --> unhash_delegation

static void
unhash_delegation(struct nfs4_delegation *dp)
{
        unhash_stid(&dp->dl_stid);              // 先釋放so_id
        // 同一個客戶端中所有的delegation構成了一個鏈表,從這個鏈表中摘除.
        list_del_init(&dp->dl_perclnt);
        spin_lock(&recall_lock);
        // 同一個文件中所有的delegation構成了一個鏈表,從這個鏈表中摘除.
        list_del_init(&dp->dl_perfile);
        // 這個delegation還可能掛載在一個lru鏈表中了(del_recall_lru),從這個鏈表中摘除.
        list_del_init(&dp->dl_recall_lru);
        spin_unlock(&recall_lock);
        // 減少delegation所在文件的計數,因爲將delegation掛載到文件中時增加了文件的計數
        nfs4_put_deleg_lease(dp->dl_file);
        // 減少delegation自身計數,當計數減到0時釋放內存.
        nfs4_put_delegation(dp);
}

發佈了70 篇原創文章 · 獲贊 4 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章