Linux nlmclnt_lock NFS锁管理与rpc_call_syncnlmclnt_lock是NFS客户端网络锁管理器NLM协议实现的锁请求入口函数位于fs/lockd/clntproc.c。它负责将用户态的flock/posix lock操作转化为NLM协议的RPC调用。当VFS层调用flock或fcntl系统调用经过do_lock_fcntl/do_flock等路径后最终进入nlmclnt_lock。函数核心定义如下cint nlmclnt_lock(struct nlm_host *host, struct file_lock *fl,struct nlm_lockowner *owner, struct nlm_wait *block){struct nlm_rqst reqst, *call reqst;struct nlm_res *resp;struct nlm_wait nlm_block;int status -ENOLCK;if (!host-h_server)return -ENOLCK;resp nlm_alloc_resp(call, GFP_KERNEL);if (!resp)return -ENOMEM;nlmclnt_setlockargs(call, fl);call-a_host host;call-a_owner owner;if (fl-fl_flags FL_SLEEP) {/* 初始化阻塞等待结构 */nlm_block.b_status 0;nlm_block.b_wait __WAIT_BIT_KEY_INITIALIZER(nlm_block.b_status);block nlm_block;}status nlmclnt_lock_async(call, fl, block, nlm_lock_ops);...}nlmclnt_setlockargs负责填充NLM请求参数将内核file_lock转换为NLM协议参数cstatic void nlmclnt_setlockargs(struct nlm_rqst *req, struct file_lock *fl){struct nlm_args *argp req-a_args;struct nlm_lock *lock argp-lock;struct inode *inode file_inode(fl-fl_file);lock-fh NFS_FH(inode);lock-oh.data req-a_owner-pid;lock-oh.len sizeof(req-a_owner-pid);lock-svid fl-fl_pid;lock-fl.fl_start fl-fl_start;lock-fl.fl_end fl-fl_end;lock-fl.fl_type fl-fl_type;argp-block fl-fl_type ! F_UNLCK;argp-exclusive (fl-fl_flags FL_EXCLUSIVE) ! 0;argp-reclaim 0;argp-state nsm_local_state;}nlmclnt_lock_async构建RPC消息并通过rpc_call_sync发起同步调用cstatic int nlmclnt_lock_async(struct nlm_rqst *req, struct file_lock *fl,struct nlm_wait *block,const struct rpc_call_ops *ops){struct rpc_message msg {.rpc_proc nlm_procedures[4], /* NLMPROC_LOCK */.rpc_argp req-a_args,.rpc_resp req-a_res,};struct rpc_task_setup task_setup {.rpc_client req-a_host-h_rpcclnt,.rpc_message msg,.callback_ops ops,.flags RPC_TASK_ASYNC,};struct rpc_task *task;int status;task rpc_run_task(task_setup);if (IS_ERR(task))return PTR_ERR(task);if (fl-fl_flags FL_SLEEP) {/* 在锁被阻塞时通过nlm_block等待队列处理grants */status nlm_wait_task(block, task);} else {status rpc_wait_for_completion_task(task);}if (status 0)status req-a_res.status;switch (status) {case NLM_LCK_DENIED_GRACE_PERIOD:/* 在宽限期被拒绝自动重试 */schedule_timeout_killable(NLM_CLNT_DELAY);status -EAGAIN;break;case NLM_LCK_DENIED_NOLOCKS:status -ENOLCK;break;}rpc_put_task(task);return nlm_stat_to_errno(status);}rpc_call_sync是RPC层的同步调用封装它创建一个RPC任务并直接等待完成cint rpc_call_sync(struct rpc_clnt *clnt, struct rpc_message *msg, int flags){struct rpc_task *task;struct rpc_task_setup task_setup {.rpc_client clnt,.rpc_message msg,.callback_ops rpc_default_ops,.flags flags,};int ret;task rpc_run_task(task_setup);if (IS_ERR(task))return PTR_ERR(task);ret rpc_wait_for_completion_task(task);if (ret 0)ret task-tk_status;rpc_put_task(task);return ret;}在NLM锁场景中rpc_call_sync的关键在于处理锁的重试。当NLM服务器返回NLM_LCK_DENIED_GRACE_PERIOD时客户端必须在宽限期结束前不断重试。nlmclnt_lock通过循环调用rpc_call_sync完成这个重试逻辑cint nlmclnt_lock(struct nlm_host *host, struct file_lock *fl,struct nlm_lockowner *owner, struct nlm_wait *block){int status;for (;;) {status nlmclnt_lock_async(call, fl, block, nlm_lock_ops);if (status ! -EAGAIN)break;/* EAGAIN表示在宽限期等待后重试 */if (signalled())break;if (!(fl-fl_flags FL_SLEEP))break;}if (status 0)nfs_file_set_open(fl, NFS_LOCK_OPEN);return status;}RPC任务在rpc_execute中启动经历了bind、connect、send、call_allocate、call_reserve、call_bind、call_connect、call_transmit、call_status等状态机阶段。每个阶段由task-tk_action函数指针驱动。对于NLM协议RPC传输使用UDP或TCP协议在rpc_create_client时确定传输类型。nlmproc.c中的nlm_svc_lock_handler是服务端对应的锁处理函数接收NLM_LOCK请求后在服务端进行POSIX文件锁操作。客户端通过Cookie和Status字段匹配请求和响应Cookie由nlmclnt_setlockargs中的随机数生成保证RPC幂等性检查。