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就可以了。