LAYOUTGET(五)

    上篇文章讲到了MDS中LAYOUTGET的处理过程,当MDS处理完毕后就会将数据封装到应答消息中返回给客户端。这篇文章中我们讲讲客户端接收到应到报文后的处理。以前的文章讲到过客户端用struct pnfs_layout_segment表示一个layout,LAYOUTGET的作用就是获取文件的layout,因此当客户端接收到LAYOUTGET应答报文后会创建一个pnfs_layout_segment结构,然后解析应答报文,用应答报文中的数据填充这个结构。这个处理过程是在函数pnfs_layout_process()中完成的,客户端执行完LAYOUTGET请求后会马上调用这个函数。

struct pnfs_layout_segment *
pnfs_layout_process(struct nfs4_layoutget *lgp)
{
	// 先取出文件索引节点中的pnfs_layout_hdr结构.
        struct pnfs_layout_hdr *lo = NFS_I(lgp->args.inode)->layout;
        struct nfs4_layoutget_res *res = &lgp->res;     // 这是LAYOUTGET返回的信息
        struct pnfs_layout_segment *lseg;
        struct inode *ino = lo->plh_inode;
        int status = 0;

        /* Inject layout blob into I/O device driver */
        // 调用alloc_lseg()创建一个pnfs_layout_segment结构,这个函数可能发起了GETDEVICEINFO请求.
        lseg = NFS_SERVER(ino)->pnfs_curr_ld->alloc_lseg(lo, res, lgp->gfp_flags);
        // 现在已经分配完,并且设置好了.
        if (!lseg || IS_ERR(lseg)) {
                if (!lseg)
                        status = -ENOMEM;
                else
                        status = PTR_ERR(lseg);
                dprintk("%s: Could not allocate layout: error %d\n",
                       __func__, status);
                goto out;
        }

        spin_lock(&ino->i_lock);
        if (test_bit(NFS_LAYOUT_BULK_RECALL, &lo->plh_flags)) {
                dprintk("%s forget reply due to recall\n", __func__);
                goto out_forget_reply;
        }

        if (pnfs_layoutgets_blocked(lo, 1) ||
            pnfs_layout_stateid_blocked(lo, &res->stateid)) {
                dprintk("%s forget reply due to state\n", __func__);
                goto out_forget_reply;
        }

        /* Done processing layoutget. Set the layout stateid */
        // 根据LAYOUTGET的返回值设置layout的stateid.
        pnfs_set_layout_stateid(lo, &res->stateid, false);

        init_lseg(lo, lseg);    // lseg指向了lo
        lseg->pls_range = res->range;   // 这是layout中数据的范围.
        pnfs_get_lseg(lseg);    // 增加lseg引用计数.
        pnfs_layout_insert_lseg(lo, lseg);      // 将lseg添加到lo中.

        // 设置return on close标志位
        if (res->return_on_close) {
                set_bit(NFS_LSEG_ROC, &lseg->pls_flags);
                set_bit(NFS_LAYOUT_ROC, &lo->plh_flags);
        }

        spin_unlock(&ino->i_lock);
        return lseg;
out:
        return ERR_PTR(status);

out_forget_reply:
        spin_unlock(&ino->i_lock);
        lseg->pls_layout = lo;
        NFS_SERVER(ino)->pnfs_curr_ld->free_lseg(lseg); // 出错了,需要释放这个lseg.
        goto out;
}
这个函数涉及到了file layout中的两个函数alloc_lseg()和free_lseg()。alloc_lseg()的作用是创建一个 pnfs_layout_segment结构,并根据LAYOUTGET应答报文中的数据填充这个结构中的字段,这是主要的处理函数。free_lseg()是当pnfs_layout_process()出错后删除刚创建的pnfs_layout_segment的函数。file layout中这两个函数定义如下:

static struct pnfs_layoutdriver_type filelayout_type = {
        ......
        .alloc_lseg             = filelayout_alloc_lseg,
        .free_lseg              = filelayout_free_lseg,
        ......
};
我们只讲alloc_lseg(),这个函数代码如下:

static struct pnfs_layout_segment *
filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid,
                      struct nfs4_layoutget_res *lgr,
                      gfp_t gfp_flags)
{
        struct nfs4_filelayout_segment *fl;
        int rc;
        struct nfs4_deviceid id;        // 设备id.

        dprintk("--> %s\n", __func__);
        fl = kzalloc(sizeof(*fl), gfp_flags);   // 分配内存
        if (!fl)
                return NULL;    // 分配内存出错了.

        // 解码LAYOUTGET应答报文,根据应答报文中的数据填充fl.
        rc = filelayout_decode_layout(layoutid, fl, lgr, &id, gfp_flags);
        // filelayout_check_layout()是一个检查函数,这个函数检查了很多信息,
        if (rc != 0 || filelayout_check_layout(layoutid, fl, lgr, &id, gfp_flags)) {
                _filelayout_free_lseg(fl);
                return NULL;
        }
        return &fl->generic_hdr;
}
这个函数很短,逻辑很清晰,但是需要介绍一个数据结构struct nfs4_filelayout_segment。前面一直说pnfs_layout_segment结构表示一个layout,其实这个数据结构不足以表示一个完整的layout,pnfs_layout_segment中只包含了layout的基本信息,比如layout在文件中的起始位置、layout长度、layout访问方式等信息。但是各种类型的layout还有一些特殊数据需要保存。对于file layout来说,表示layout的数据结构是struct nfs4_filelayout_segment,pnfs_layout_segment是nfs4_filelayout_segment中的一部分。

struct nfs4_filelayout_segment {
        struct pnfs_layout_segment generic_hdr; // 这是各种类型的layout通用的数据.
        u32 stripe_type;                // STRIPE_SPARSE 或者 STRIPE_DENSE
        u32 commit_through_mds;         // COMMIT请求是提交给MDS还是提交给DS.
        u32 stripe_unit;                // Stripe Unit 大小
        u32 first_stripe_index;         // 第一个I/O请求发送给哪个DS组.
        u64 pattern_offset;             // 这是pattern在文件中的起始位置.
        // 这个数据结构中保存了DS的地址,这个结构中的数据需要通过GETDEVICEINFO请求获取.
        struct nfs4_file_layout_dsaddr *dsaddr; /* Point to GETDEVINFO data */
        unsigned int num_fh;            // LAYOUTGET请求返回的文件句柄的数量
        struct nfs_fh **fh_array;       // 这是一组文件句柄,供DS使用.
};
这个数据结构中的字段应该很清晰了,基本上就是LAYOUTGET应答报文中的数据,但是dsaddr除外,这个字段存储的是layout的Stripe信息和DS的地址,LAYOUTGET请求中没有获取这些信息,这些信息是在GETDEVICEINFO中获取的,后面会讲解GETDEVICEINFO请求。现在filelayout_alloc_lseg()就很容易理解了,这个函数包含三个步骤:

步骤1:调用kzalloc()创建一个新的nfs4_filelayout_segment结构。

步骤2:调用filelayout_decode_layout()解析LAYOUTGET应答报文,填充nfs4_filelayout_segment中的各个字段。

步骤3:调用filelayout_check_layout()检查nfs4_filelayout_segment结构中的数据是否有效。因为MDS返回的数据不一定有效。如果数据无效客户端就不能使用这个layout。对于我们来说,这个函数最主要的作用是检查了nfs4_filelayout_segment中dsaddr中的信息是否有效。如果无效,这个函数会向MDS发起GETDEVICEINFO请求获取DS的地址。

static int
filelayout_check_layout(struct pnfs_layout_hdr *lo,
                        struct nfs4_filelayout_segment *fl,     // 这里面包含了解码后的数据
                        struct nfs4_layoutget_res *lgr,         // 这是LAYOUTGET的返回值
                        struct nfs4_deviceid *id,               // deviceid
                        gfp_t gfp_flags)
{
        struct nfs4_deviceid_node *d;
        struct nfs4_file_layout_dsaddr *dsaddr;
        int status = -EINVAL;
        struct nfs_server *nfss = NFS_SERVER(lo->plh_inode);    // 取出nfs_server结构.

        dprintk("--> %s\n", __func__);

        /* FIXME: remove this check when layout segment support is added */
	// 客户端目前只支持整个文件的layout.
        if (lgr->range.offset != 0 ||
            lgr->range.length != NFS4_MAX_UINT64) {
                dprintk("%s Only whole file layouts supported. Use MDS i/o\n",
                        __func__);
                goto out;
        }

	// pattern的起始位置也必须是0,必须从文件头开始.
        if (fl->pattern_offset > lgr->range.offset) {
                dprintk("%s pattern_offset %lld too large\n",
                                __func__, fl->pattern_offset);
                goto out;
        }

	// 目前Stripe Unit大小必须是缓存页长度整数倍.
        if (!fl->stripe_unit || fl->stripe_unit % PAGE_SIZE) {
                dprintk("%s Invalid stripe unit (%u)\n",
                        __func__, fl->stripe_unit);
                goto out;
        }
        /* find and reference the deviceid */
	// 这个函数在检查deviceid.
        d = nfs4_find_get_deviceid(NFS_SERVER(lo->plh_inode)->pnfs_curr_ld,
                                   NFS_SERVER(lo->plh_inode)->nfs_client, id);
        if (d == NULL) {
		// 没找到,发起GETDEVICEINFO请求.
                dsaddr = filelayout_get_device_info(lo->plh_inode, id, gfp_flags);
                if (dsaddr == NULL)
                        goto out;
        } else
                dsaddr = container_of(d, struct nfs4_file_layout_dsaddr, id_node);
        /* Found deviceid is unavailable */
        // 设备现在不可使用.
        if (filelayout_test_devid_unavailable(&dsaddr->id_node))
                        goto out_put;

        fl->dsaddr = dsaddr;    // 这里面是设备信息.

        // 这是第一个使用的stripe编号.  超出范围了.
        if (fl->first_stripe_index >= dsaddr->stripe_count) {
                dprintk("%s Bad first_stripe_index %u\n",
                                __func__, fl->first_stripe_index);
                goto out_put;
        }
        // SPARSE方式下允许所有的ns class使用同一个文件句柄。如果不使用同一个文件句柄,则必须每个DS组一个文件句柄.
        if ((fl->stripe_type == STRIPE_SPARSE && fl->num_fh > 1 && fl->num_fh != dsaddr->ds_num) ||
            // DENSE方式下,每个DS组必须有自己的文件句柄,不允许使用同一个文件句柄.
            (fl->stripe_type == STRIPE_DENSE && fl->num_fh != dsaddr->stripe_count)) {
                dprintk("%s num_fh %u not valid for given packing\n",
                        __func__, fl->num_fh);
                goto out_put;
        }

	// nfss->rsize是客户端设置的每次读操作过程中可以传输的数据量
	// nfss->wsize是客户端设置的每次写操作过程中可以传输的数据量
        if (fl->stripe_unit % nfss->rsize || fl->stripe_unit % nfss->wsize) {
                dprintk("%s Stripe unit (%u) not aligned with rsize %u "
                        "wsize %u\n", __func__, fl->stripe_unit, nfss->rsize,
                        nfss->wsize);
        }

        status = 0;
out:
        dprintk("--> %s returns %d\n", __func__, status);
        return status;
out_put:
        nfs4_fl_put_deviceid(dsaddr);
        goto out;
}

我们在下篇文章中介绍nfs4_find_get_deviceid()和filelayout_get_device_info()两个函数以及GETDEVICEINFO请求的处理过程。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值