NFSv4.1支持三種layout類型:file layout、object layout、block layout,這三種layout對數據讀寫過程的處理方式不同。因此pNFS系統中,客戶端首先需要獲取layout類型,這是在掛載文件系統時實現的。客戶端掛載文件系統時需要發起GETATTR請求,獲取文件系統的基本信息,這個處理函數是nfs4_proc_fsinfo。
static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{
int error;
nfs_fattr_init(fsinfo->fattr);
error = nfs4_do_fsinfo(server, fhandle, fsinfo);
if (error == 0) {
/* block layout checks this! */
server->pnfs_blksize = fsinfo->blksize;
set_pnfs_layoutdriver(server, fhandle, fsinfo->layouttype);
}
return error;
}
RFC5661定義了NFS文件系統相關的很多屬性,其中一個屬性是fs_layout_type,這個屬性表示這套pNFS環境中支持的layout類型。當MDS接收到GETATTR請求報文後,如果請求報文中設置了這個屬性位,就會檢查支持的pNFS類型,然後封裝到應答報文中返回給客戶端。客戶端調用set_pnfs_layoutdriver()設置在nfs_server結構中。下一篇文章中再講解這個函數的流程,這篇文章中主要講講MDS查找layout類型的過程。首先介紹一個與layout相關的數據結構struct pnfs_export_operations,這個數據結構定義了MDS中layout相關的函數。NFS是一種網絡文件系統,它必須依附於一種實際的文件系統之上(比如EXT3、JFS等)。如果底層的文件系統支持pNFS,則這種文件系統必須實現struct pnfs_export_operations中定義的方法。這個數據結構定義如下:
struct pnfs_export_operations {
int (*layout_type) (struct super_block *);
int (*get_device_info) (struct super_block *,
struct exp_xdr_stream *,
u32 layout_type,
const struct nfsd4_pnfs_deviceid *);
int (*get_device_iter) (struct super_block *,
u32 layout_type,
struct nfsd4_pnfs_dev_iter_res *);
int (*set_device_notify) (struct super_block *,
struct pnfs_devnotify_arg *);
enum nfsstat4 (*layout_get) (struct inode *,
struct exp_xdr_stream *xdr,
const struct nfsd4_pnfs_layoutget_arg *,
struct nfsd4_pnfs_layoutget_res *);
int (*layout_commit) (struct inode *,
const struct nfsd4_pnfs_layoutcommit_arg *,
struct nfsd4_pnfs_layoutcommit_res *);
int (*layout_return) (struct inode *,
const struct nfsd4_pnfs_layoutreturn_arg *);
int (*can_merge_layouts) (u32 layout_type);
void (*get_verifier) (struct super_block *, u32 *p);
int (*get_state) (struct inode *, struct knfsd_fh *,
struct pnfs_get_state *);
};
這個數據結構中函數很多,看着也很複雜,但是我們通過函數名稱能大概明白每個函數的意思。比如get_device_info()應該是GETDEVICEINFO中調用的函數,laytou_get()應該是LAYOUTGET中調用的函數,layout_return()應該是LAYOUTRETURN中調用的函數,layout_type()應該是獲取layout類型的函數。而且底層文件系統不必實現這個數據結構中所有的函數,根據實際情況實現部分函數就可以了。當然,"實際情況"是什麼,只有這種文件系統的開發者清楚了。在後面的文章中我們將以GFS爲例,GFS文件系統只實現了下列幾個函數:const struct pnfs_export_operations pnfs_dlm_export_ops = {
.layout_type = nfsd4_pnfs_dlm_layouttype,
.get_device_info = nfsd4_pnfs_dlm_getdevinfo,
.get_device_iter = nfsd4_pnfs_dlm_getdeviter,
.layout_get = nfsd4_pnfs_dlm_layoutget,
};
爲了支持pNFS,文件系統超級塊中增加了一個指針 s_pnfs_op,這個指針指向了文件系統layout相關的操作函數集合。當然,這個指針目前只存在於Benny Halevy的代碼樹中,標準內核代碼中還沒有這個指針。
struct super_block {
......
const struct pnfs_export_operations *s_pnfs_op;
......
}
現在可以接着講MDS查找layout類型的過程了。GETATTR請求的處理函數是nfsd4_encode_fattr(),這個函數處理了MDS支持的所有屬性,其中就包含fs_layout_type,處理這個屬性的代碼片段如下:
__be32
nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
struct dentry *dentry, __be32 *buffer, int *countp, u32 *bmval,
struct svc_rqst *rqstp, int ignore_crossmnt)
{
......
if ((bmval1 & FATTR4_WORD1_FS_LAYOUT_TYPES) ||
(bmval2 & FATTR4_WORD2_LAYOUT_TYPES)) {
// 取出底層文件系統超級塊的數據結構
struct super_block *sb = dentry->d_inode->i_sb;
int type = 0;
/* Query the filesystem for supported pNFS layout types.
* Currently, we only support one layout type per file system.
* The export_ops->layout_type() returns the pnfs_layouttype4.
*/
buflen -= 4;
if (buflen < 0) /* length */
goto out_resource;
// 檢查文件系統是否定義了layout_type()函數
if (sb && sb->s_pnfs_op && sb->s_pnfs_op->layout_type)
// 調用layout_tyoe()取出文件系統的layout類型.
type = sb->s_pnfs_op->layout_type(sb);
dprintk("%s: sb=%p s_pnfs_op=%p layout_type()=%p layout_type=%u\n",
__func__, sb, sb->s_pnfs_op, sb->s_pnfs_op ? sb->s_pnfs_op->layout_type : NULL, type);
if (type) {
if ((buflen -= 4) < 0) /* type */
goto out_resource;
WRITE32(1); /* length */
// 組裝進GETATTR應答報文中.
WRITE32(type); /* type */
} else
WRITE32(0); /* length */
}
if (bmval2 & FATTR4_WORD2_LAYOUT_BLKSIZE) {
if ((buflen -= 4) < 0)
goto out_resource;
dprintk("%s: layout_blksize=%lu\n", __func__, stat.blksize);
WRITE32(stat.blksize);
}
......
}
可以看出,nfsd4_encode_fattr()直接調用底層文件系統的layout_type()獲取layout類型了,而GFS文件系統中這個函數很簡單:
static int
nfsd4_pnfs_dlm_layouttype(struct super_block *sb)
{
return LAYOUT_NFSV4_1_FILES;
}
講完了。