返还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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章