Linux Progamming

NFS Client in Linux Kernel - Loc

2019-01-31  本文已影响0人  帆子_8c3a

1. System Call

2. 内核中的Lock

分为两种lock,但在内核中统一成file_lock数据结构

2.1 NFS的特殊处理

以下两种函数,最终都统一调用nfs4_proc_lock

对于posix lock入口函数是nfs4_proc_lock(),分别调用:

3. NFS协议中的Lock Owner

对于普通的文件系统来说,flock的owner是内核下的open fd,posix lock的owner是current->files。如果Owner一样,说明lock请求来自同一个实体,判断lock是否冲突时候直接跳过。对于NFS来说,协议规定由这个数据结构表示owner(其实open的owner也同样由这个数据结构表示):
摘自nfs4.1协议

   struct state_owner4 {
           clientid4       clientid;
           opaque          owner<NFS4_OPAQUE_LIMIT>;
   };
  1. clientid,Server分配的,唯一标识一个和server建立session的client
  2. 字符串+字符串的长度,唯一标识client中的一个进程
  3. client标识+进程标识,唯一确定一个lock的owner。

3.1 Linux Kernel中的对Lock Owner的设置

Lock owner大小20字节:

  1. 8字节的固定字符,"lock id:"
  2. 4字节的s_dev,表示是本地哪个文件系统。
  3. 8字节的id。由ida_simple_get函数创建,在本地文件系统中唯一。对于flock,且open fd相同,会复用同一个。对于posix lock,且进程相同,也会复用同一个。
  4. 以上三点,保证了在一个client内Lock owner是唯一的。
  5. 再加上clientid,可以保证所有和Server建立session连接的client来自的lock owner是唯一确定。
static void encode_lockowner(struct xdr_stream *xdr, const struct nfs_lowner *lowner)
{
    p = reserve_space(xdr, 32);
    p = xdr_encode_hyper(p, lowner->clientid);
    *p++ = cpu_to_be32(20); //lock owner大小,20
    p = xdr_encode_opaque_fixed(p, "lock id:", 8); //8字节
    *p++ = cpu_to_be32(lowner->s_dev);//4字节
    xdr_encode_hyper(p, lowner->id);//8字节,不同进程会会分配到不同id
}

4. Linux Kernel对NFS Lock实体的描述

Linux Kernel对lock实体的表示,用的是struct nfs4_lock_state

struct nfs4_lock_state {
    struct list_head    ls_locks;   //将同一个open file的lock插入到state->lock_states
    struct nfs4_state * ls_state;   //指向open state
    unsigned long       ls_flags;
    struct nfs_seqid_counter    ls_seqid;//里面重要的owner_id,由ida_simple_get函数创建
    nfs4_stateid        ls_stateid; //server端返回的信息,Server端对这个实体的key
    atomic_t        ls_count;
    fl_owner_t      ls_owner; //对于同一个文件的lock,如果此项相同会复用以前
};

4.1 nfs4_lock_state的构造

struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner);
  1. 这个打开的文件里,可以获得一个list,包含所有对这个文件lock过的owner。如果这个owner已经在列表里,则直接返回。
  2. 如果没找到,从内存里获取一个。最重要是为这个结构,分配一个lsp->ls_seqid.owner_id,它是通过ida_simple_get分配的。
  3. nfs4_proc_getlk(),nfs4_proc_unlck(),nfs4_proc_setlk()都会调用nfs4_set_lock_state(),也就是为它们设置了owner。

nfs4_get_lock_state()就是根据open file state + owner唯一产生一个nfs4_lock_state,它其实存在open state的一个list里,保存所有的lock state。
nfs4_set_lock_state()将nfs4_lock_state存在了fl->fl_u.nfs4_fl.owner,其中fl是file_lock
nfs4_proc_getlk(),nfs4_proc_unlck(),nfs4_proc_setlk()都会调用nfs4_set_lock_state(),其中nfs4_proc_setlk()会将server返回的lock stateid存在nfs4_lock_state。而nfs4_proc_getlk()和nfs4_proc_unlck()会取出nfs4_lock_state,从而得到lock stateid,发送给server。

4.2 对 nfs4_lock_state的修改

NFS lock请求结束后,进入nfs4_lock_done,会把返回的stateid更新到nfs4_lock_state里。

5. NFS协议中Lock的使用

对于一个实体第一次使用Lock,client需要提供: (代码参看encode_lock)

  1. open_seqid
  2. open_stateid
  3. lock_seqid
  4. lock_owner
    Server端会返回lock stateid,以后再操作时候,就直接提供lock stateid就可以了
  5. lock_stateid
  6. lock_seqid

6. Blocking lock vs Non-blocking lock

fcntl系统调用会调到nfs4_proc_lock这里

  1. 调用nfs4_proc_setlk()发送LOCK RPC给server
  2. 如果返回值是EAGAIN或者是non-blocking的lock(用IS_SETLK判断),则直接返回
  3. 调用nfs4_set_lock_task_retry等待一个timeout时间。第一次timeout是1s,第二次是2s,最大值是30s
  4. 跳到步骤1

6. 为什么两次flock不会引起第二次NFS请求

例如下面的命令,执行两次。第一次会导致发生NFS请求,但第二次不会导致发生NFS请求。为什么?

flock -nx /mnt/vfs/1.txt /root/test.sh
  1. 第一次调用时,发送完NFS请求,会调用nfs4_lock_done,经过一些列调用会走到flock_lock_inode。注意此时equest->fl_flags是带着FL_ACCESS标记的,因此会将file_lock复制一份,并插入队列。
  2. 第二次调用时,在发送NFS请求前,会再次调用flock_lock_inode。此时equest->fl_flags是不带FL_ACCESS,如果发现冲突,则直接返回-EAGAIN,而不会调用NFS请求。

7. call stack

fcntl64
  fcntl_setlk64
    flock64_to_posix_lock //fl->fl_owner = current->files
    do_lock_file_wait
      vfs_lock_file
        nfs_lock // f_op->lock
          do_setlk
            nfs4_proc_lock // NFS_PROTO(inode)->lock
flock
  flock_make_lock //fl->fl_owner = filp
  nfs_flock // f_op->flock
    do_setlk
      nfs4_proc_lock // NFS_PROTO(inode)->lock

fcntl64和flock最终都调用nfs4_proc_lock

nfs4_proc_lock
  nfs4_proc_setlk
    _nfs4_proc_setlk
      nfs4_set_lock_state
        nfs4_get_lock_state
          __nfs4_find_lock_state 
          nfs4_alloc_lock_state //set lsp->ls_seqid.owner_id
      do_vfs_lock
        locks_lock_inode_wait
          flock_lock_inode_wait
            flock_lock_inode
      _nfs4_do_setlk
        => nfs4_lock_done
          do_vfs_lock
        nfs4_alloc_lockdata //p->arg.lock_owner.id = lsp->ls_seqid.owner_id;

8. 容易误解的地方

  1. Client打开一个文件,就会产生一个Open Owner,这个Open Owner里会有一个Open State。
  2. Client Lock一个文件,就会产生有一个Lock Owner(它一定是被打开的,所以一定有一个Open Owner和一个Open state), 对这个文件不停的修改range lock的范围,总对应一个lock state,而不是多个!! 所以一般情况下一个Lock Owner下,只有一个Lock State。对于delegation和layout,也被认为是lock state。这些所有的lock state存在lock owner的一个list里。对于一般情况,这个list只有一个元素(就是那个lock state)。
上一篇下一篇

猜你喜欢

热点阅读