天天看点

【ceph】cephfs caps简介

目录

​​CAPS基础​​

​​基本概念​​

​​CAPS种类​​

​​CAPS PERMISSION种类​​

​​CAPS COMBINATION​​

​​PIN​​

​​AUTH、LINK、XATTR​​

​​FILE​​

​​CAPS管理​​

​​LOCK​​

​​CAPS如何变更​​

​​CAPS相关告警​​

​​总结​​

​​CAPS代码相关​​

​​CAPS数据表示和规则​​

​​FUSE WRITE实例​​

​​参考链接​​

CAPS基础

基本概念

caps是mds授予client对文件进行操作的许可证,当一个client想要对文件元数据进行变更时,比如读、写、修改权限等等操作,它都必须先获取到相应的caps才可以进行这些操作。

ceph对caps的划分粒度很细,且允许多个client在同一个inode上持有不同的caps。

CAPS种类

根据元数据内容的不同,ceph将caps也分为了多个类别,每种类别只负责作用于某些特定的元数据:

类别 功能
PIN mds是否将inode pin在cache中
AUTH 鉴权相关的元数据,主要是owner、group、mode;但是如果是完整的鉴权是需要查看ACL的,acl信息保存在xattr中,这就需要XATTR相关的cap
XATTR xattr
FILE 最重要也是最复杂的一个,用于文件数据,以及和文件数据相关的ize、atime、ctime、mtime等

CAPS PERMISSION种类

#define CEPH_CAP_GSHARED     1  /* client can reads (s) */
#define CEPH_CAP_GEXCL       2  /* client can read and update (x) */
#define CEPH_CAP_GCACHE      4  /* (file) client can cache reads (c) */
#define CEPH_CAP_GRD         8  /* (file) client can read (r) */
#define CEPH_CAP_GWR        16  /* (file) client can write (w) */
#define CEPH_CAP_GBUFFER    32  /* (file) client can buffer writes (b) */
#define CEPH_CAP_GWREXTEND  64  /* (file) client can extend EOF (a) */
#define CEPH_CAP_GLAZYIO   128  /* (file) client can perform lazy io (l) */      

CAPS COMBINATION

一个完整cap通过【类别+permission种类】组成,client可以同时申请多个类别的caps。但是并不是每种caps都可以使用每种permission,有些caps只能搭配部分permission。有关caps种类和permission的结合使用,有以下几个规则:

PIN

二值型,有pin就代表client知道这个inode存在,这样mds就一定会在其cache中保存这个inode

AUTH、LINK、XATTR

只能为shared或者exclusive

  • shared:client可以将对应元数据保存在本地并缓存和使用
  • exclusive:client不仅可以在本地缓存使用,还可以修改

下面是两个例子:

  • [A]s:某client对inode 0x11有As的cap,此时收到一个查看0x11状态的系统调用,那么client不需要再向mds请求,直接通过查询自身缓存并进行处理和回复
  • [A]x:某client对inode 0x11有Ax的cap,此时收到一个修改mode的系统调用,client可以直接在本地进行修改并回复,并且在之后才将修改变更通知mds

FILE

如前所述,file是最复杂的一种,下面是File cap的各个类别:

file cap种类 client权限
Fs client可以将mtime和size在本地cache并读取使用
Fx client可以将mtime和size在本地cache并进行修改和读取
Fr client可以同步地从osd读取数据,但不能cache
Fc client可以将文件数据cache在本地内存,并直接从cache中读
Fw client可以同步地写数据到osd中,但是不能buffer write
Fb client可以buffer write,先将写的数据维护在自己内存中,再统一flush到后端落盘

CAPS管理

LOCK

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

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

CAPS如何变更

  • mds可以针对每个client进行授予和移除caps,通常是由其他client的行为触发
  • 例:比如client1已经拥有了inode 0x111的cache read的cap,此时client2要对这个文件进行写,那显然除了授予client2响应的写caps的同时,还要剥夺client1的cache read的cap
  • 当client被移除caps时,其必须停止使用该cap,并给mds回应确认消息。mds需要等待收到client的确认消息后才会revoke。(如果client挂掉或者出于某种原因没有回复ack怎么办?)
  • client停止使用并不简单,在不同场景下需要完全不同的处理:
  • 例1:client被移除cache read cap,直接把该file的cache删掉,并变更状态就行了,这样下次的read请求过来时,还是到osd去读
  • 例2:client被移除buffer write cap,已经缓存了大量的数据还没有flush,那就需要先flush到osd,再变更状态和确认,这可能就需要较长时间

下面来看看一个修改权限的例子实际感受下:

【ceph】cephfs caps简介

CAPS相关告警

下面是一些caps相关的主要告警信息,可参考对照排查问题:

告警信息 问题
Client failing to respond to capability release mds发出了revoke cap消息但是client没有回复
Client failing to cache pressure mds发送消息请求client去除一些pinned inode以减少内存使用,但client没有drop的足够或者没有回复的足够快

总结

  1. mds需要记住所有client pin的inode,
  2. mds的cache需要比client的cache更多
  3. caps是由mds和client端共同协作维护的,所以client需要正常运行,否则可能会block其他client(也就是说前面提出的问题,会被block?)

CAPS代码相关

CAPS数据表示和规则

一个client可以拥有多种类型(A,L,X,F)的caps,每种类型的caps也有多种permission类型(s,x,c,r,w,b,a,l)。那么如何表示这么多类型的caps呢?

  1. ceph首先规定了每种类型cap的bit范围,保证不同类型的cap的bit范围没有重叠。
/* generic cap bits */
#define CEPH_CAP_GSHARED     1  /* client can reads(s) */
#define CEPH_CAP_GEXCL       2  /* client can read and update(x) */
#define CEPH_CAP_GCACHE      4  /* (file) client can cache reads(c) */
#define CEPH_CAP_GRD         8  /* (file) client can read(r) */
#define CEPH_CAP_GWR        16  /* (file) client can write(w) */
#define CEPH_CAP_GBUFFER    32  /* (file) client can buffer writes(b) */
#define CEPH_CAP_GWREXTEND  64  /* (file) client can extend EOF(a) */
#define CEPH_CAP_GLAZYIO   128  /* (file) client can perform lazy io(l) */

/* per-lock shift */
#define CEPH_CAP_SAUTH      2 // A
#define CEPH_CAP_SLINK      4 // L
#define CEPH_CAP_SXATTR     6 // X
#define CEPH_CAP_SFILE      8 // F      
  1. 通过定义每种类型permission类型的bit位和每种cap类型的偏移量,通过移位将两者组合起来形成单一cap。
#define CEPH_CAP_AUTH_SHARED  (CEPH_CAP_GSHARED  << CEPH_CAP_SAUTH) // As
#define CEPH_CAP_AUTH_EXCL     (CEPH_CAP_GEXCL     << CEPH_CAP_SAUTH) // Ax
#define CEPH_CAP_LINK_SHARED  (CEPH_CAP_GSHARED  << CEPH_CAP_SLINK) // Ls
#define CEPH_CAP_LINK_EXCL     (CEPH_CAP_GEXCL     << CEPH_CAP_SLINK) // Lx
#define CEPH_CAP_XATTR_SHARED (CEPH_CAP_GSHARED  << CEPH_CAP_SXATTR) // Xs
#define CEPH_CAP_XATTR_EXCL    (CEPH_CAP_GEXCL     << CEPH_CAP_SXATTR) // Xx 
#define CEPH_CAP_FILE(x)    (x << CEPH_CAP_SFILE)
#define CEPH_CAP_FILE_SHARED   (CEPH_CAP_GSHARED   << CEPH_CAP_SFILE) // Fs
#define CEPH_CAP_FILE_EXCL     (CEPH_CAP_GEXCL     << CEPH_CAP_SFILE) // Fx 
#define CEPH_CAP_FILE_CACHE    (CEPH_CAP_GCACHE    << CEPH_CAP_SFILE) // Fc 
#define CEPH_CAP_FILE_RD       (CEPH_CAP_GRD       << CEPH_CAP_SFILE) // Fr 
#define CEPH_CAP_FILE_WR       (CEPH_CAP_GWR       << CEPH_CAP_SFILE) // Fw 
#define CEPH_CAP_FILE_BUFFER   (CEPH_CAP_GBUFFER   << CEPH_CAP_SFILE) // Fb 
#define CEPH_CAP_FILE_WREXTEND (CEPH_CAP_GWREXTEND << CEPH_CAP_SFILE) // Fa 
#define CEPH_CAP_FILE_LAZYIO   (CEPH_CAP_GLAZYIO   << CEPH_CAP_SFILE) // Fl      
  1. 再通过或运算符将不同的cap组合起来形成多个caps

更形象的用图形来表示:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| p | _ |As   x |Ls   x |Xs   x |Fs   x   c   r   w   b   a   l |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| PIN   | AUTH  | LINK  | XATTR | FILE
0       2       4       6       8      

这里还需要注意两点:

  1. pin cap只需要bit位0,所以bit位1是空闲出来无用的
  2. 除了file cap,其他类型的都不会占用2bit以上,所以把file cap放到了高位

FUSE WRITE实例

一个实例

下面以fuse client write为例,简要分析下fuse write时caps的代码逻辑:

注:只截取了和caps相关的部分代码

int64_t Client::_write(Fh *f, int64_t offset, uint64_t size, const char *buf,
                    const struct iovec *iov, int iovcnt)
{

    want = CEPH_CAP_FILE_BUFFER;
  // 需要拥有file write(CEPH_CAP_FILE_WR)和auth shared(CEPH_CAP_AUTH_SHARED) caps才(即FwAs)能够写,get_caps中如果检查没有caps则会
  // 去向mds申请并等待返回
  int r = get_caps(in, CEPH_CAP_FILE_WR|CEPH_CAP_AUTH_SHARED, want, &have, endoff);
  if (r < 0)
    return r;

  /* clear the setuid/setgid bits, if any */
  if (unlikely(in->mode & (S_ISUID|S_ISGID)) && size > 0) {
    struct ceph_statx stx = { 0 };

    // 增加该inode对该caps的引用计数并检查该caps是否正在使用中
    put_cap_ref(in, CEPH_CAP_AUTH_SHARED);
    r = __setattrx(in, &stx, CEPH_SETATTR_KILL_SGUID, f->actor_perms);
    if (r < 0)
      return r;
  } else {
    put_cap_ref(in, CEPH_CAP_AUTH_SHARED);
  }


  // 如果有buffer 或者lazy io cap则直接在objectcacher cache中写
  if (cct->_conf->client_oc &&
      (have & (CEPH_CAP_FILE_BUFFER | CEPH_CAP_FILE_LAZYIO))) {
    // do buffered write
    if (!in->oset.dirty_or_tx)
      get_cap_ref(in, CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_BUFFER);

    get_cap_ref(in, CEPH_CAP_FILE_BUFFER);

    // async, caching, non-blocking.
    // 缓存写的调用,异步、cache、非阻塞
    r = objectcacher->file_write(&in->oset, &in->layout,
                 in->snaprealm->get_snap_context(),
                 offset, size, bl, ceph::real_clock::now(),
                 0);
    put_cap_ref(in, CEPH_CAP_FILE_BUFFER);

    if (r < 0)
      goto done;

    // flush cached write if O_SYNC is set on file fh
    // O_DSYNC == O_SYNC on linux < 2.6.33
    // O_SYNC = __O_SYNC | O_DSYNC on linux >= 2.6.33
    if ((f->flags & O_SYNC) || (f->flags & O_DSYNC)) {
      _flush_range(in, offset, size);
    }
  } else {// 如果没有buffer cap,则直接通过osd写
    if (f->flags & O_DIRECT)
      _flush_range(in, offset, size);

    // simple, non-atomic sync write
    C_SaferCond onfinish("Client::_write flock");
    unsafe_sync_write++;
    get_cap_ref(in, CEPH_CAP_FILE_BUFFER);  // released by onsafe callback

    // 同步写的调用
    filer->write_trunc(in->ino, &in->layout, in->snaprealm->get_snap_context(),
               offset, size, bl, ceph::real_clock::now(), 0,
               in->truncate_size, in->truncate_seq,
               &onfinish);
    client_lock.Unlock();
    // 写完之后通过条件变量在这里等待,当写完成之后被唤醒,执行一些清理工作并返回
    onfinish.wait();
    client_lock.Lock();
    _sync_write_commit(in);
  }

}      

参考链接

[1] ​​What are “caps”? (And Why Won’t my Client Drop Them?)​​​ [2] ​​cephfs capabilities​​