【ceph】cephfs的锁 笔记

目录

CEPH MDS锁实现介绍

锁的作用

锁的获取

加锁类型

锁的种类和状态机

锁的状态转换

锁的释放

了解cephfs锁必须要知道的事情


CEPH caps简介

http://t.csdn.cn/7tY3w

CAPS 和锁的关系

caps由mds进行管理,其将元数据划分为多个部分,每个部分都有专门的锁(SimpleLock、ScatterLock、FileLock)来保护,mds通过这些锁的状态来确定caps可以怎么样分配。

mds内部维护了每个锁的状态机,其内容非常复杂,也是mds保证caps分配准确性和数据一致性的关键。

CEPH MDS锁实现介绍

锁的作用

MDS中的锁是为了保护log的正常写入。每次对目录树进行操作前,需要先将目标path中涉及的节点加锁,在内存中修改完目录树(修改方式类似于RCU,即生成一个新节点,push_back到 队列 中)后,将新的目录树信息(只是此条path,不是整个目录树)记录到MDS的journal对象中,journal对象落盘后再将 队列 中的节点pop_front出来,至此,内存中的目录树已经能反映出之前的修改,加的锁也在此时开始释放,最后当前目录树的信息更新到meta pool的dir对象中。

锁的获取

加锁类型

加锁的类型分三类:rdlock(读)、wrlock(写)、xlock(互斥)。每次对目录树进行操作前都要将path上的节点进行适当地加锁。可从src/mds/Server.cc中观察这一操作:

    handle_client_xxx
      |-- rdlock_path_xlock_dentry或rdlock_path_pin_ref
      |-- mds->locker->acquire_locks(mdr, rdlocks, wrlocks, xlocks)

对于一个路径进行操作时,最后一个dentry之前的dentry都要加rdlock,避免别人进行修改。xlock用于创建或者修改节点时,比如mkdir时需要对新的dentry加xlock,创建新文件时需要对CInode::linklock(负责inode的nlink属性)加xlock。rdlock和xlock符合通常认知:共享读,互斥写。

wrlock比较特殊,主要用在CInode::filelockCInode::nestlock上,前者负责保护当前目录的统计信息inode_t::dirstat,后者负责保护当前目录的递归统计信息inode_t::rstat

由于一个目录可以分成多个分片,甚至同一个分片也可以有多个副本分散于各个mds,为了允许对这些分片的统计信息同时进行修改,引入了wrlock,这些分散的被修改的信息将在后续的一个时间点上进行综合,最终传播到目录树的inode信息中(见CInode::preditry_journal_parents)。对于CInode::versionlockCDentry::versionlock也会加wrlock锁,但由于是locallock sm,意义和simplelock的xlock一样,只是为了互斥写。

锁的种类和状态机

一个inode中的信息有很多种,每种由不同的锁来保护,每个锁的状态变化由遵循特定的规则——状态机。状态机的定义在src/mds/locks.c中,共有四种:

  • simaplelock state machine
  • scatter_lock state machine
  • file_lock state machine
  • local_lock state machine
     

CInodeCDentry中每种锁使用的状态机如下:

struct LockType {
  int type;
  const sm_t *sm;
  explicit LockType(int t) : type(t) {
    switch (type) {
    case CEPH_LOCK_DN:
    case CEPH_LOCK_IAUTH:
    case CEPH_LOCK_ILINK:
    case CEPH_LOCK_IXATTR:
    case CEPH_LOCK_ISNAP:
    case CEPH_LOCK_IFLOCK:
    case CEPH_LOCK_IPOLICY:
      sm = &sm_simplelock;
      break;
    case CEPH_LOCK_IDFT:
    case CEPH_LOCK_INEST:
      sm = &sm_scatterlock;
      break;
    case CEPH_LOCK_IFILE:
      sm = &sm_filelock;
      break;
    case CEPH_LOCK_DVERSION:
    case CEPH_LOCK_IVERSION:
      sm = &sm_locallock;
      break;
    default:
      sm = 0;
    }
  }
};
 

其中locallock sm最简单,不做解释;

绝大多数锁使用simplelock sm,这些锁只需要“共享读、互斥写”功能;

目录分片信息和递归统计信息则使用scatterlock sm,这种状态机能提供“共享读、共享写”功能;

最复杂的是CInode::filelock使用的filelock sm,因为filelock既负责目录统计信息这种需要“共享读、共享写”的数据,也负责保护inode中的atime、mtime等需要“共享读、互斥写”的属性。

CInode种每种锁负责保护的数据可由CInode::encode_lock_state推断出来:

void CInode::encode_lock_state(int type, bufferlist& bl)
{
  ...
  switch (type) {
  case CEPH_LOCK_IAUTH:
    encode(inode.version, bl);
    encode(inode.ctime, bl);
    encode(inode.mode, bl);
    encode(inode.uid, bl);
    encode(inode.gid, bl);  
    break;
  case CEPH_LOCK_ILINK:
    encode(inode.version, bl);
    encode(inode.ctime, bl);
    encode(inode.nlink, bl);
    break;
  case CEPH_LOCK_IDFT:
    ...
    encode(dirfragtree, bl);
    ...
  case CEPH_LOCK_IFILE:
    if (is_auth()) {
      encode(inode.version, bl);
      encode(inode.ctime, bl);
      encode(inode.mtime, bl);
      encode(inode.atime, bl);
      encode(inode.time_warp_seq, bl);
      if (!is_dir()) {
    encode(inode.layout, bl, mdcache->mds->mdsmap->get_up_features());
    encode(inode.size, bl);
    encode(inode.truncate_seq, bl);
    encode(inode.truncate_size, bl);
    encode(inode.client_ranges, bl);
    encode(inode.inline_data, bl);
      }
    ...
  case CEPH_LOCK_INEST:
    ...

锁的状态转换

有了状态机后就可根据预先定义的转换规则判断此次加锁是否可行,不可行的情况下要对锁的状态进行适当转换。锁状态的转换有两种驱动方式:

  • accquire_locks中根据当前sate判读是否能加锁,可以则直接变更锁的当前状态。
  • 按照状态机中的sm_state_t::next指示逐步变换,这种方式一般有tick或者log flush回调等函数触发。

只有auth才有机会直接变更锁的当前状态,副本只能向auth发消息请求加锁

下图展示了加xlock时的状态变换,根据状态机描述,如果当前无法加xlock,则对锁进行一些转换,如调用Locker::simple_xlock()Locker::simple_lock(),如果转换过程无法顺利进行(gather==true)则加锁失败。

加xlock时的状态转换

锁的释放

请求失败或完成后,Locker::drop_locks()负责锁的释放,其间会处理锁的等待队列,对锁的状态进行kick.

正常情况下要等到日志落盘后才会触发释放锁的动作,如果设置了mds_early_reply = true则提交完log就会释放rdlocks,但wrlock和xlock依然要等到log落盘后才释放。



作者:宋新颖
链接:https://www.jianshu.com/p/dc16309519d1
 

了解cephfs锁必须要知道的事情

1、本文讨论的锁不是flock。flock是posix标准中对文件某部分偏移加的锁。

而这里的锁指的是MDS集中管理多客户端对于文件并发访问时,Dentry和Inode等数据结构控制的锁。每个锁内有parent变量记录的就是该锁相关的Dentry或者Inode。Cephfs采用和GPFS类似的并发更新元文件的方式。同一份元数据分散多个MDS节点,但是有一个Auth节点,该节点负责

- 串行更新(serializing updates)

- 将元数据持久化到磁盘上(committing changes to disks)

- 保证缓存一致性(consistency)

- 维护节点之间的一致性(cache coherence)

多路并发的MDS节点会更新每个副本,但是会周期性地向AUTH发送当前节点最新的数据。

2、锁的类型分为LocalLock、SimpleLock、ScatterLock、FileLock

无论何种类型的锁,都有一个state变量记录当前锁的状态。每个锁都有相关的状态机控制状态转移。

​​​​​​​

 

根据状态机可以确定:

- 下一步锁的状态(next_state)

- 其他副本(Object比如Inode在多mds的其他mds也有副本叫replica)的状态

- 允许谁可读(can_read)、谁可加读锁(can_rdlock)、谁可加写(can_wrlock)、谁可以加排他锁(can_xlock)等。这里的谁被抽象成为ANY、AUTH、XCL等。

ANY指的任何拥有Object副本的MDS,

AUTH(authority)指的是被授权Object的MDS,

XCL指的是被授权Object的MDS或者排他执行的客户端。

- 对应的caps是什么,包括拥有Object副本的MDS的此时应该使用的caps(针对不同的角色也分为4种)

复杂程度而言,可以根据sm_state_t结构体里面定义的条数判断,LocalLock最简单,SimpleLock/ScatterLock相对复杂,FileLock最为复杂。

3、锁的状态有很多,都是以LOCK_开头的(除了LOCK_AC_*之外,它代表锁的某种行为)。

一般而言,

LOCK_<状态>                     都是稳定状态(标准是在状态机其next_state为0,也有例外),比如LOCK_SYNC/LOCK_LOCK/LOCK_MIX等。

LOCK_<状态1>_< 状态2>    是中间状态,比如LOCK_SYNC_LOCK就是从LOCK_SYNC状态向LOCK_LOCK状态转移的中间状态,日志中可以看到“sync->lock”就是这种状态dump出来信息。

4、每个Inode里面有多个Lock,每个Lock对应2中介绍的4种类型的锁。每种锁关联的是不同的文件系统资源。

LocalLock  -  versionlock

SimpleLock用在nlink、atime、mtime等属性的处理上。其特点是"共享读、互斥写"。

SimpleLock  - authlock  linklock  xattrlock(扩展属性相关)  snaplock(快照相关)    flocklockpolicylock(layout相关)  

ScatterLock用在需要处理迭代的数据结构,比如目录树或者目录下面的统计信息,其特点是“共享读、共享写”

ScatterLock -  dirfragtreelock nestlock 

FileLock既用在处理atime、ctime等需要互斥操作的属性上,也有需要共享写的统计信息上。

FileLock - filelock

5、Cap当中的s代表share,意味着客户端拥有读相关信息的能力,比如删除inodes时mds会设置CEPH_CAP_LINK_SHARED,即Cap为Ls,客户端读到该标志位,会判断inode的nlink信息是否为0,0则代表删除客户端执行针对删除的操作。x代表允许客户端更新相关信息的能力(独占执行的能力)。

6、Cap和锁的关系

客户端是根据MDS赋予的cap确定相关行为。MDS则根据客户端发来的请求和当前锁的状态,确定授予(grant)或者剥夺(revoke)客户端的caps。

7、打算对一个inode或者dentry加锁,需要先确定给其中的哪些锁加什么类型的操作。一般有rdlock、wrlock、remote_wrlock、xlock等。

rdlock  读锁操作,当一个资源加了读锁之后,不能再加独占锁对其修改。

wrlock  写锁操作

remote_wrlock  远端写锁操作

xlock  独占锁操作,比如修改某项资源,一定要让锁执行该操作。

使用场景比如,

在创建快照的时候需要给执行目录的inode的snap锁加上独占操作。

xlocks.insert(&diri->snaplock));

ls的时候会对父目录至根目录的dentry都加rdlock防止被修改。

rdlock.insert(&dn->lock)

日志里面“isnap sync r=<NUM>”之类的输出代表当前snap锁处在sync状态,锁上有NUM个读锁操作(见SimpleLock.h SimpleLock::_print())。

8、Locker中*_start 表示对某个锁执行某种类型的锁操作。*_finish表示结束对某个锁加某种类型的锁操作。*_try代表尝试去执行某种类型的锁操作是否能够成功。这里的*代表rdlock、wrlock、remote_wrlock、xlock等。

9、Locker中有操作状态的转变函数,将任何中间状态变为*_sync同步状态,*_excl 独占执行状态、*_lock锁状态、*_xlock独占锁状态。*可以是simple/scatter/file代表不同的锁类型。比如Locker::file_xsyn函数会让filelock进入xsyn状态,XSYN状态出现在如下场景中:

如果一个客户端在执行ls –al的时候想要rdlock。此时如果一个EXCL的客户端正在缓存写,那么MDS会让锁进入XSYN状态,进入这个状态写会被停止,但是缓存不会刷到磁盘(提升效率)。  

10、MDS和其他MDS处理锁相互发的是MLock请求。执行发送的函数是Locker::send_lock_message。内容是锁和相关的转换操作(LOCK_AC_*定义的行为)。

由auth的MDS发给replica的MDS有如下所示。replica的MDS收到之后会将其本地保留的锁状态,尽可能转换为消息中定义的锁状态。

LOCK_AC_SYNC      //向SYNC状态转换

LOCK_AC_MIX          //向MIX状态转换

LOCK_AC_LOCK      //向LOCK状态转换

LOCK_AC_LOCKFLUSHED      //向LOCKFLUSHED状态转换



作者:要厉害的
链接:https://www.jianshu.com/p/69919b806bf1
 

cephfs的cap

cephfs的cap - 简书

posted on 2022-10-04 01:21  bdy  阅读(283)  评论(0编辑  收藏  举报

导航