文件解锁是加锁的反操作,当处理完毕后应该尽量赶快解锁,避免影响其他用户访问文件。NFS中与文件解锁相关的操作有两个:LOCKU和RELEASE_LOCKOWNER。LOCKU的作用是解锁,RELEASE_LOCKOWNER的作用是释放stateid。
LOCKU客户端程序
客户端处理LOCKU请求的函数是nfs4_proc_unlck(),这个函数最终调用了nfs4_do_unlck(),这个函数负责创建一个RPC任务,根据文件锁的信息初始化RPC请求报文,然后向服务器发送请求,代码如下:
static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
struct nfs_open_context *ctx,
struct nfs4_lock_state *lsp,
struct nfs_seqid *seqid)
{
struct nfs4_unlockdata *data;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCKU],
.rpc_cred = ctx->cred,
};
struct rpc_task_setup task_setup_data = {
.rpc_client = NFS_CLIENT(lsp->ls_state->inode),
.rpc_message = &msg,
.callback_ops = &nfs4_locku_ops,
.workqueue = nfsiod_workqueue,
.flags = RPC_TASK_ASYNC,
};
/* Ensure this is an unlock - when canceling a lock, the
* canceled lock is passed in, and it won't be an unlock.
*/
fl->fl_type = F_UNLCK; // 文件锁操作是F_UNLCK
// 创建一个新的nfs4_unlockdata结构,初始化LOCKU请求的参数信息
data = nfs4_alloc_unlockdata(fl, ctx, lsp, seqid);
if (data == NULL) {
nfs_free_seqid(seqid);
return ERR_PTR(-ENOMEM);
}
nfs41_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1);
msg.rpc_argp = &data->arg;
msg.rpc_resp = &data->res;
task_setup_data.callback_data = data;
return rpc_run_task(&task_setup_data); // 创建一个RPC任务,发送RPC请求.
}
LOCKU服务器端处理程序
服务器接收到LOCKU请求后,先调用nfsd4_decode_locku()解析报文,解析出的数据保存在数据结构struct nfsd4_locku,这个结构的定义如下:
struct nfsd4_locku {
u32 lu_type; // 文件锁类型
u32 lu_seqid; // 文件锁seqid
stateid_t lu_stateid; // 文件锁stateid
u64 lu_offset; // 文件锁在文件中的偏移
u64 lu_length; // 文件锁长度
};
然后服务器调用函数nfsd4_locku()处理请求,这个函数代码如下:
__be32
nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
struct nfsd4_locku *locku)
{
struct nfs4_ol_stateid *stp;
struct file *filp = NULL;
struct file_lock file_lock;
__be32 status;
int err;
dprintk("NFSD: nfsd4_locku: start=%Ld length=%Ld\n",
(long long) locku->lu_offset, // 这是文件锁的起始位置
(long long) locku->lu_length); // 这是文件锁的长度
// 检查文件锁的长度是否有效.
if (check_lock_length(locku->lu_offset, locku->lu_length))
return nfserr_inval;
nfs4_lock_state();
// 这个函数在检查stateid,根据stateid查找nfs4_ol_stateid结构.
status = nfs4_preprocess_seqid_op(cstate, locku->lu_seqid,
&locku->lu_stateid, NFS4_LOCK_STID, &stp);
if (status)
goto out; // 查找过程出错了.
// 取出一个文件对象结构.
filp = find_any_file(stp->st_file);
if (!filp) {
status = nfserr_lock_range; // 没有找到文件对象结构.
goto out;
}
BUG_ON(!filp); // 这条语句在后来的代码中已经去掉了,重复检查了.
// 初始化一个文件锁结构
locks_init_lock(&file_lock);
file_lock.fl_type = F_UNLCK; // 这是删除文件锁操作.
file_lock.fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
file_lock.fl_pid = current->tgid;
file_lock.fl_file = filp;
file_lock.fl_flags = FL_POSIX;
file_lock.fl_lmops = &nfsd_posix_mng_ops;
file_lock.fl_start = locku->lu_offset;
file_lock.fl_end = last_byte_offset(locku->lu_offset, locku->lu_length);
nfs4_transform_lock_offset(&file_lock);
/*
* Try to unlock the file in the VFS.
*/
// 调用VFS层的函数删除文件锁
err = vfs_lock_file(filp, F_SETLK, &file_lock, NULL);
if (err) {
dprintk("NFSD: nfs4_locku: vfs_lock_file failed!\n");
goto out_nfserr; // 删除文件锁过程出错了.
}
/*
* OK, unlock succeeded; the only thing left to do is update the stateid.
*/
// 更新stateid
update_stateid(&stp->st_stid.sc_stateid);
memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
out:
if (!cstate->replay_owner)
nfs4_unlock_state();
return status;
out_nfserr:
status = nfserrno(err);
goto out;
}
这个函数思路很清楚,首先检查stateid是否有效,查找对应的nfs4_ol_stateid结构,然后创建了一个文件锁结构,调用VFS层的函数vfs_lock_file()解锁就可以了。
当所有的锁全部解除后,客户端会向服务器发起RELEASE_LOCKOWNER请求,删除stateid。
RELEASE_LOCKOWNER客户端处理程序
客户端的处理函数是nfs4_release_lockowner(),这个函数也比较简单,就是创建了一个RPC任务,然后按照RFC的规定设置了请求报文中的数据。
int nfs4_release_lockowner(struct nfs4_lock_state *lsp)
{
struct nfs_server *server = lsp->ls_state->owner->so_server;
struct nfs_release_lockowner_data *data;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER],
};
if (server->nfs_client->cl_mvops->minor_version != 0)
return -EINVAL;
data = kmalloc(sizeof(*data), GFP_NOFS);
if (!data)
return -ENOMEM;
data->lsp = lsp;
data->server = server;
data->args.lock_owner.clientid = server->nfs_client->cl_clientid;
data->args.lock_owner.id = lsp->ls_seqid.owner_id;
data->args.lock_owner.s_dev = server->s_dev;
msg.rpc_argp = &data->args;
rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data);
return 0;
}
RELEASE_LOCKOWNER服务器端处理程序
服务器端的处理程序是nfsd4_release_lockowner(),这个函数根据owner查找对应的nfs4_lockowner结构,然后释放这个结构就可以了,代码如下:
__be32
nfsd4_release_lockowner(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
struct nfsd4_release_lockowner *rlockowner)
{
clientid_t *clid = &rlockowner->rl_clientid; // 取出clientid.
struct nfs4_stateowner *sop;
struct nfs4_lockowner *lo;
struct nfs4_ol_stateid *stp;
struct xdr_netobj *owner = &rlockowner->rl_owner;
struct list_head matches;
// 这是clientid的hash值
unsigned int hashval = ownerstr_hashval(clid->cl_id, owner);
__be32 status;
struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n",
clid->cl_boot, clid->cl_id);
/* XXX check for lease expiration */
status = nfserr_stale_clientid;
if (STALE_CLIENTID(clid, nn))
return status; // 客户端已经过期了,就不必处理了.
nfs4_lock_state();
status = nfserr_locks_held;
INIT_LIST_HEAD(&matches);
// 遍历链表中每个nfs4_stateowner结构,系统中所有的nfs4_stateowner保存在全局hash表ownerstr_hashtbl中了.
list_for_each_entry(sop, &ownerstr_hashtbl[hashval], so_strhash) {
if (sop->so_is_open_owner) // 这是一个nfs4_openowner结构,检查下一个.
continue;
if (!same_owner_str(sop, owner, clid))
continue; // 名称不相同,这不是我们查找的nfs4_lockowner结构.
// 遍历链表中每个nfs4_ol_stateid结构 目前情况下一个nfs4_lockowner结构中只包含一个nfs4_ol_stateid结构.
list_for_each_entry(stp, &sop->so_stateids,
st_perstateowner) {
lo = lockowner(sop); // 取出nfs4_lockowenr结构.
if (check_for_locks(stp->st_file, lo))
goto out; // 如果还有文件锁没有释放,就不能处理了.
list_add(&lo->lo_list, &matches);
}
}
/* Clients probably won't expect us to return with some (but not all)
* of the lockowner state released; so don't release any until all
* have been checked. */
status = nfs_ok;
while (!list_empty(&matches)) { // 处理每一个nfs4_lockowner结构.
lo = list_entry(matches.next, struct nfs4_lockowner,
lo_list);
/* unhash_stateowner deletes so_perclient only
* for openowners. */
list_del(&lo->lo_list);
release_lockowner(lo); // 删除这个nfs4_lockowner结构.
}
out:
nfs4_unlock_state();
return status;
}
但是感觉这个函数有些乱。目前实现中,nfs4_lockowner中只能包含一个nfs4_ol_stateid,但是数据结构中使用了一个链表,为以后扩充做准备吗?如果nfs4_lockowner中包含了多个nfs4_ol_stateid,这个函数就可能出问题。现在没办法进行验证,先这样放着吧。