这篇文章中我们讨论这样一种情况:假设用户user1以只读权限打开了文件file1,并且服务器为这个文件分配了delegation。现在用户user2用样以只读权限打开文件file1,那么user2打开文件的过程是什么样的呢?是否还需要向服务器发送OPEN请求?
和前一篇文章相似,客户端仍然需要创建一个RPC任务,在发送OPEN请求报文前执行函数nfs4_open_prepare(),我们看看这种情况下程序怎么执行的。
static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
{
struct nfs4_opendata *data = calldata; // 取出nfs4_opendata结构
struct nfs4_state_owner *sp = data->owner; // nfs4_state_owner结构
....
if (data->state != NULL) { // 这是一个nfs4_state结构.
struct nfs_delegation *delegation;
....
delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
if (data->o_arg.claim != NFS4_OPEN_CLAIM_DELEGATE_CUR &&
can_open_delegated(delegation, data->o_arg.fmode))
goto unlock_no_action;
rcu_read_unlock();
}
....
unlock_no_action:
rcu_read_unlock();
out_no_action:
task->tk_action = NULL;
}
我们略去了无关的代码,可以看出nfs4_open_prepare()调用了函数can_open_delegated()。如果这个函数返回非0值就不需要发起OPEN请求了,如果这个函数返回0就仍然需要发起OPEN请求。我们马上看看这个函数的代码
static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
{
if (delegation == NULL)
return 0;
if ((delegation->type & fmode) != fmode) // 访问权限不同,不能打开.
return 0;
if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
return 0;
nfs_mark_delegation_referenced(delegation); // 可以使用
return 1;
}
这个函数很简单,就是比较了了文件中delegation的类型是OPEN操作中的类型是否一致。如果delegation的类型是只读,而fmode中请求的访问权限也是只读,则二者匹配,就不需要再次发起OPEN请求了。但是,这个函数还检查了标志位NFS_DELEGATION_NEED_RECLAIM,前面的文章中讲过,这个标志位标志delegation中的信息无效,需要更新了。因此如果设置了这个标志位函数也返回0。如果确定了可以使用这个delegation,那么就需要调用函数nfs_mark_delegation_referenced(),这个函数的作用是设置标志位NFS_DELEGATION_REFERENCED。前面的文章中讲过,NFS_DELEGATION_REFERENCED表示这是一个有效的delegation。客户端的delegation只在一段时间内有效,如果delegation在一段时间内没有使用,客户端就认为这个delegation已经没用了,就会清除这个标志位。再过一段时间就会删除这个delegation。这里设置标志位NFS_DELEGATION_REFERENCED的作用是告诉客户端,这是一个有用的delegation,你不要删除了。
同样,由于没有发起RPC请求,在设置nfs4_state时nfs4_opendata_to_nfs4_state()也会调用到nfs4_try_open_cached(),我们看看这种情况下nfs4_try_open_cached()的流程。
static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
{
struct nfs4_state *state = opendata->state; // 这是使用的nfs4_state结构.
struct nfs_inode *nfsi = NFS_I(state->inode); // 这是文件索引节点
struct nfs_delegation *delegation; // 查找delegation.
int open_mode = opendata->o_arg.open_flags & (O_EXCL|O_TRUNC);
fmode_t fmode = opendata->o_arg.fmode; // 这是文件访问模式
nfs4_stateid stateid;
int ret = -EAGAIN;
for (;;) {
rcu_read_lock();
delegation = rcu_dereference(nfsi->delegation); // 取出delegation.
// 检查是否可以引用这个delegation.
if (!can_open_delegated(delegation, fmode)) {
rcu_read_unlock();
break; // 如果不可以引用这个delegation,就直接退出.
}
/* Save the delegation */ // OK,现在可以引用这个delegation了.
nfs4_stateid_copy(&stateid, &delegation->stateid); // 先取出了delegation的stateid.
rcu_read_unlock();
// 检查用户是否有访问权限.
ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
if (ret != 0)
goto out;
ret = -EAGAIN;
/* Try to update the stateid using the delegation */
// 更新stateid
if (update_open_stateid(state, NULL, &stateid, fmode))
goto out_return_state;
}
out:
return ERR_PTR(ret);
out_return_state:
atomic_inc(&state->count);
return state;
}
这种情况下nfs4_try_open_cached()依次调用了四个函数:can_open_delegated()、nfs4_stateid_copy()、nfs_may_open()、update_open_stateid()。can_open_delegated()再次检查了user2是否可以使用这个delegation,由于前面已经检查过了,一般情况下这里不会出错。接下来我们看看另外三个函数的作用。
static inline void nfs4_stateid_copy(nfs4_stateid *dst, const nfs4_stateid *src)
{
memcpy(dst, src, sizeof(*dst));
}
这个函数很简单,就是取出了delegation中的stateid。
int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
{
return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
}
nfs_may_open()的作用是检查user2对文件file1的访问权限,是否可以以只读权限打开文件。因为客户端不会向服务器发起OPEN请求了。如果user2没有file1的访问权限,那么接下来的读操作就会出错。为了避免这种情况因此需要先检查user2对file1的访问权限。这里,nfs_do_access()会向服务器发起ACCESS请求,我们在前面的文章中讲解过这个函数了。
opendata->state是一个新创建的nfs4_state结构,update_open_stateid()的最用是更新这个结构中的信息。
static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
{
struct nfs_inode *nfsi = NFS_I(state->inode); // 找到文件的索引节点
struct nfs_delegation *deleg_cur;
int ret = 0;
fmode &= (FMODE_READ|FMODE_WRITE); // 文件访问权限 读写
rcu_read_lock();
deleg_cur = rcu_dereference(nfsi->delegation); // 取出文件中的delegation.
if (deleg_cur == NULL)
goto no_delegation;
spin_lock(&deleg_cur->lock);
if (nfsi->delegation != deleg_cur ||
(deleg_cur->type & fmode) != fmode) // 比较用户的访问权限和delegation中的访问权限是否一致
goto no_delegation_unlock;
if (delegation == NULL)
delegation = &deleg_cur->stateid;
else if (!nfs4_stateid_match(&deleg_cur->stateid, delegation))
goto no_delegation_unlock;
nfs_mark_delegation_referenced(deleg_cur); // 设置标志位NFS_DELEGATION_REFERENCED
// 这个函数在更新nfs4_state结构中的open_stateid和stateid.
__update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
ret = 1;
no_delegation_unlock:
spin_unlock(&deleg_cur->lock);
no_delegation:
rcu_read_unlock();
if (!ret && open_stateid != NULL) {
__update_open_stateid(state, open_stateid, NULL, fmode);
ret = 1;
}
return ret;
}
目前这种情况下会执行到__update_open_stateid(state, open_stateid, NULL, fmode);
static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
{
write_seqlock(&state->seqlock);
if (deleg_stateid != NULL) {
// 设置nfs4_state结构中的stateid.
nfs4_stateid_copy(&state->stateid, deleg_stateid);
set_bit(NFS_DELEGATED_STATE, &state->flags);
}
// 现在open_stateid==NULL,因此没有设置open_stateid.
if (open_stateid != NULL)
nfs_set_open_stateid_locked(state, open_stateid, fmode);
write_sequnlock(&state->seqlock);
spin_lock(&state->owner->so_lock);
// 这个函数在设置nfs4_state结构中各种访问权限的计数.
update_open_stateflags(state, fmode);
spin_unlock(&state->owner->so_lock);
}
可以看到,这种情况下没有设置open_stateid,只设置了stateid,而这个stateid就是从delegation中获取的,并且设置了标志位NFS_DELEGATED_STATE,说明这是一个包含delegation的nfs4_state结构,以后user2就使用delegation的stateid从服务器中读取数据。