layout需要MDS、DS和客户端三方共同支持,当客户端通过GETATTR请求获取了layout类型后,还需要检查自己是否支持这种类型,客户端检查、设置layout的函数是set_pnfs_layoutdriver()。
RFC5661定义了三种layout,三种layout的编号依次为
enum pnfs_layouttype {
LAYOUT_NFSV4_1_FILES = 1,
LAYOUT_OSD2_OBJECTS = 2,
LAYOUT_BLOCK_VOLUME = 3,
};
layout编号从1开始,0是一个保留序号,不允许出现编号为0的layout类型。
客户端与pNFS相关的数据结构是struct pnfs_layoutdriver_type,这个数据结构很恐怖,包含了很多函数:
struct pnfs_layoutdriver_type {
// 这个数据结构链接到全局链表pnfs_modules_tbl中
struct list_head pnfs_tblid;
const u32 id; // 这种layout类型的编号.
const char *name; // 这种layout类型的名称
struct module *owner; // 模块
unsigned flags; // 一些标志位
int (*set_layoutdriver) (struct nfs_server *, const struct nfs_fh *);
int (*clear_layoutdriver) (struct nfs_server *);
struct pnfs_layout_hdr * (*alloc_layout_hdr) (struct inode *inode, gfp_t gfp_flags);
void (*free_layout_hdr) (struct pnfs_layout_hdr *);
struct pnfs_layout_segment * (*alloc_lseg) (struct pnfs_layout_hdr *layoutid, struct nfs4_layoutget_res *lgr, gfp_t gfp_flags);
void (*free_lseg) (struct pnfs_layout_segment *lseg);
/* test for nfs page cache coalescing */
const struct nfs_pageio_ops *pg_read_ops;
const struct nfs_pageio_ops *pg_write_ops;
struct pnfs_ds_commit_info *(*get_ds_info) (struct inode *inode);
void (*mark_request_commit) (struct nfs_page *req,
struct pnfs_layout_segment *lseg,
struct nfs_commit_info *cinfo);
void (*clear_request_commit) (struct nfs_page *req,
struct nfs_commit_info *cinfo);
int (*scan_commit_lists) (struct nfs_commit_info *cinfo,
int max);
void (*recover_commit_reqs) (struct list_head *list,
struct nfs_commit_info *cinfo);
int (*commit_pagelist)(struct inode *inode,
struct list_head *mds_pages,
int how,
struct nfs_commit_info *cinfo);
/*
* Return PNFS_ATTEMPTED to indicate the layout code has attempted
* I/O, else return PNFS_NOT_ATTEMPTED to fall back to normal NFS
*/
enum pnfs_try_status (*read_pagelist) (struct nfs_read_data *nfs_data);
enum pnfs_try_status (*write_pagelist) (struct nfs_write_data *nfs_data, int how);
void (*free_deviceid_node) (struct nfs4_deviceid_node *);
void (*encode_layoutreturn) (struct pnfs_layout_hdr *layoutid,
struct xdr_stream *xdr,
const struct nfs4_layoutreturn_args *args);
void (*cleanup_layoutcommit) (struct nfs4_layoutcommit_data *data);
void (*encode_layoutcommit) (struct pnfs_layout_hdr *layoutid,
struct xdr_stream *xdr,
const struct nfs4_layoutcommit_args *args);
};
现在暂时不讲这些函数,后面用到的时候再讲。file layout中这个数据结构定义如下:
static struct pnfs_layoutdriver_type filelayout_type = {
.id = LAYOUT_NFSV4_1_FILES,
.name = "LAYOUT_NFSV4_1_FILES",
.owner = THIS_MODULE,
.alloc_layout_hdr = filelayout_alloc_layout_hdr,
.free_layout_hdr = filelayout_free_layout_hdr,
.alloc_lseg = filelayout_alloc_lseg,
.free_lseg = filelayout_free_lseg,
.pg_read_ops = &filelayout_pg_read_ops,
.pg_write_ops = &filelayout_pg_write_ops,
.get_ds_info = &filelayout_get_ds_info,
.mark_request_commit = filelayout_mark_request_commit,
.clear_request_commit = filelayout_clear_request_commit,
.scan_commit_lists = filelayout_scan_commit_lists,
.recover_commit_reqs = filelayout_recover_commit_reqs,
.commit_pagelist = filelayout_commit_pagelist,
.read_pagelist = filelayout_read_pagelist,
.write_pagelist = filelayout_write_pagelist,
.free_deviceid_node = filelayout_free_deveiceid_node,
};
这个数据结构保存在struct nfs_server中了,表示这个NFS文件系统使用的layout类型。
struct nfs_server {
......
struct pnfs_layoutdriver_type *pnfs_curr_ld;
......
}
另外,客户端还定义了一个全局链表pnfs_modules_tbl,这个链表中保存的也是struct pnfs_layoutdriver_type。因为layout最多有三种类型,因此这个链表中最多有三个元素。现在可以讲解了set_pnfs_layoutdriver()。
void
set_pnfs_layoutdriver(struct nfs_server *server, const struct nfs_fh *mntfh,
u32 id)
{
struct pnfs_layoutdriver_type *ld_type = NULL;
// 编号为0的layout类型已经预留了,表示不使用layout.
if (id == 0)
goto out_no_driver;
// EXCHGID4_FLAG_USE_PNFS_MDS、EXCHGID4_FLAG_USE_NON_PNFS表示服务器角色,
// 这些标志位是通过EXCHANGE_ID获取的.
if (!(server->nfs_client->cl_exchange_flags &
(EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS))) {
printk(KERN_ERR "NFS: %s: id %u cl_exchange_flags 0x%x\n",
__func__, id, server->nfs_client->cl_exchange_flags);
goto out_no_driver;
}
// 在全局链表pnfs_modules_tbl中查找是否已经加载这个layout驱动了.
ld_type = find_pnfs_driver(id);
if (!ld_type) { // 这个内核模块还没有加载
// nfs_layout_nfsv41_files.ko blocklayoutdriver.ko objlayoutdriver.ko
// 好吧,加载这个内核模块.
request_module("%s-%u", LAYOUT_NFSV4_1_MODULE_PREFIX, id);
ld_type = find_pnfs_driver(id); // 再次查找
if (!ld_type) { // 还是没有找到,只好出错了.
dprintk("%s: No pNFS module found for %u.\n",
__func__, id);
goto out_no_driver;
}
}
// 客户端已经加载这个layout驱动程序了
// 关联到这个nfs_server结构中.
server->pnfs_curr_ld = ld_type;
// 如果layoutdriver定义了set_layoutdriver,就需要先执行这个函数
// file layout没有定义这个函数,因此就不看了。
if (ld_type->set_layoutdriver
&& ld_type->set_layoutdriver(server, mntfh)) {
// 但是函数执行过程中出错了
printk(KERN_ERR "NFS: %s: Error initializing pNFS layout "
"driver %u.\n", __func__, id);
module_put(ld_type->owner);
goto out_no_driver;
}
/* Bump the MDS count */
// 增加NFS客户端源数据服务器的数量
atomic_inc(&server->nfs_client->cl_mds_count);
dprintk("%s: pNFS module for %u set\n", __func__, id);
return;
out_no_driver:
dprintk("%s: Using NFSv4 I/O\n", __func__);
server->pnfs_curr_ld = NULL;
}
这个函数逻辑很简单,首先根据从MDS获取的layout类型编号在全局链表pnfs_modules_tbl中查找对应的pnfs_layoutdriver_type结构。如果找到了,说明已经加载这种layout的驱动程序了。如果没有找到,则首先加载这种layout的驱动程序,然后添加到全局链表中。最后设置nfs_server结构中的指针pnfs_curr_ld就可以了。