Ceph源码笔记- CephFS-Client-Mds
(1)Client
(1-a) create
fuse_ll_create Client->ll_create Client->_create
(1-b) open
fuse_ll_open fuse_ll_req_prepare CephFuse->Handle->client->ll_open Client->ll_open Client->_open CEPH_MDS_OP_OPEN Client->make_request Client->choose_target_mds Inode->hash_dentry_name Client->send_request
(1-c) read
fuse_ll_read Client->ll_read Client->_read bjectCacher->_readx ObjectCacher->bh_read Client->_read_async ObjectCacher->file_read ObjectCacher->prepare_read ObjectCacher->readx
(1-d) write
fuse_ll_write
Client->ll_write
Client->_write
Client->get_caps
// 分支A:缓存 :Client->objectcacher
Client->objectcacher
ObjectCacher->file_write
ObjectCacher->writex
ObjectCacher->_wait_for_write
// 分支B:直接写透
Client->_flush_range
Filer->write_trunc
(2)MDS
(2-a) 多活mds中,分发给某一个mds处理
Server->dispatch_client_request
CEPH_MDS_OP_LOOKUPINO
CEPH_MDS_OP_LOOKUPNAME
CEPH_MDS_OP_LOOKUPPARENT
Server->handle_client_lookup_ino
MDCache->open_ino
MDCache->do_open_ino
MDCache->do_open_ino_peer
MDCache->mds->send_message_mds
C_MDS_LookupIno2
(2-b) mds的处理:输入路径,返回 Inode
CEPH_MDS_OP_OPEN: Server->handle_client_openc // 遍历mdcache获得inode和dentry MDCache->path_traverse // 分支A:本节点Cache中不存在,去其他mds节点找找 MDCache->find_ino_peers MDSRank->get_nodeid // 分支B:如果Cache中存在该文件的iNode就直接打开 Server->handle_client_open
(2-c) 分发给某一个mds处理Openc选择合适的poolid
void Server::handle_client_openc(MDRequestRef& mdr)
{
MClientRequest *req = mdr->client_request;
client_t client = mdr->get_client();
// 将flag转为cmode,由于是CREATE操作,此处的cmode为CEPH_FILE_MODE_WR(写)、或者CEPH_FILE_MODE_RDWR(读+写)
int cmode = ceph_flags_to_mode(req->head.args.open.flags);
...
bool excl = req->head.args.open.flags & CEPH_O_EXCL;
// 客户端未指定O_EXCL标记,代表着:若文件存在则直接打开,若不存在则创建后打开
// 此处还做了额外的处理:若文件存在但状态STALE,则查找最新的inode信息再重试(C_MDS_TryFindInode);
// 异常情况下,除非为不存在(-ENOENT),否则直接返回
if (!excl) {
// path_traverse 为逐级向上查找过程,加载路径上所有节点的inode和dentry
// 在MDS源码分析-3一文中有详细阐述
int r = mdcache->path_traverse(mdr, NULL, NULL, req->get_filepath(),
&mdr->dn[0], NULL, MDS_TRAVERSE_FORWARD);
if (r > 0) return;
if (r == 0) {
// 文件存在,则直接打开,与上述的OPEN处理流程一致
handle_client_open(mdr);
return;
}
if (r < 0 && r != -ENOENT) {
if (r == -ESTALE) {
// STALE状态,则重新加载最新inode
MDSInternalContextBase *c = new C_MDS_TryFindInode(this, mdr);
mdcache->find_ino_peers(req->get_filepath().get_ino(), c);
} else {
dout(10) << "FAIL on error " << r << dendl;
respond_to_request(mdr, r);
}
return;
}
}
set<SimpleLock*> rdlocks, wrlocks, xlocks;
file_layout_t *dir_layout = NULL;
// 准备读锁和互斥锁
// 从父目录开始,路径上的目录全部需要读锁
// 由于此流程为新创建文件,inode还未生成,所以创建一个null dentry,并将null dentry加入互斥锁列表xlocks
// 此处返回的是一个null dentry,即还未关联inode的dentry,注意并不是一个nullptr
CDentry *dn = rdlock_path_xlock_dentry(mdr, 0, rdlocks, wrlocks, xlocks,
!excl, false, false, &dir_layout);
if (!dn) return;
if (mdr->snapid != CEPH_NOSNAP) {
// 不允许在snap目录下做创建操作,此检查按理应该前置,可以避免一些无谓的开销
respond_to_request(mdr, -EROFS);
return;
}
// 设置layout,若父目录有layout则使用,否则使用默认的layout
file_layout_t layout;
...
const auto default_layout = layout;
// 读取请求中可能携带的layout参数,进行转换,更新layout参数
...
// 若layout发生改变,说明客户端在创建的同时还要设置定制化的layout