1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29730ffcbSVarun Prakash /*
39730ffcbSVarun Prakash * Copyright (c) 2016 Chelsio Communications, Inc.
49730ffcbSVarun Prakash */
59730ffcbSVarun Prakash
69730ffcbSVarun Prakash #include "cxgbit.h"
79730ffcbSVarun Prakash
89730ffcbSVarun Prakash static void
cxgbit_set_one_ppod(struct cxgbi_pagepod * ppod,struct cxgbi_task_tag_info * ttinfo,struct scatterlist ** sg_pp,unsigned int * sg_off)99730ffcbSVarun Prakash cxgbit_set_one_ppod(struct cxgbi_pagepod *ppod,
109730ffcbSVarun Prakash struct cxgbi_task_tag_info *ttinfo,
119730ffcbSVarun Prakash struct scatterlist **sg_pp, unsigned int *sg_off)
129730ffcbSVarun Prakash {
139730ffcbSVarun Prakash struct scatterlist *sg = sg_pp ? *sg_pp : NULL;
149730ffcbSVarun Prakash unsigned int offset = sg_off ? *sg_off : 0;
159730ffcbSVarun Prakash dma_addr_t addr = 0UL;
169730ffcbSVarun Prakash unsigned int len = 0;
179730ffcbSVarun Prakash int i;
189730ffcbSVarun Prakash
199730ffcbSVarun Prakash memcpy(ppod, &ttinfo->hdr, sizeof(struct cxgbi_pagepod_hdr));
209730ffcbSVarun Prakash
219730ffcbSVarun Prakash if (sg) {
229730ffcbSVarun Prakash addr = sg_dma_address(sg);
239730ffcbSVarun Prakash len = sg_dma_len(sg);
249730ffcbSVarun Prakash }
259730ffcbSVarun Prakash
269730ffcbSVarun Prakash for (i = 0; i < PPOD_PAGES_MAX; i++) {
279730ffcbSVarun Prakash if (sg) {
289730ffcbSVarun Prakash ppod->addr[i] = cpu_to_be64(addr + offset);
299730ffcbSVarun Prakash offset += PAGE_SIZE;
309730ffcbSVarun Prakash if (offset == (len + sg->offset)) {
319730ffcbSVarun Prakash offset = 0;
329730ffcbSVarun Prakash sg = sg_next(sg);
339730ffcbSVarun Prakash if (sg) {
349730ffcbSVarun Prakash addr = sg_dma_address(sg);
359730ffcbSVarun Prakash len = sg_dma_len(sg);
369730ffcbSVarun Prakash }
379730ffcbSVarun Prakash }
389730ffcbSVarun Prakash } else {
399730ffcbSVarun Prakash ppod->addr[i] = 0ULL;
409730ffcbSVarun Prakash }
419730ffcbSVarun Prakash }
429730ffcbSVarun Prakash
439730ffcbSVarun Prakash /*
449730ffcbSVarun Prakash * the fifth address needs to be repeated in the next ppod, so do
459730ffcbSVarun Prakash * not move sg
469730ffcbSVarun Prakash */
479730ffcbSVarun Prakash if (sg_pp) {
489730ffcbSVarun Prakash *sg_pp = sg;
499730ffcbSVarun Prakash *sg_off = offset;
509730ffcbSVarun Prakash }
519730ffcbSVarun Prakash
529730ffcbSVarun Prakash if (offset == len) {
539730ffcbSVarun Prakash offset = 0;
549730ffcbSVarun Prakash if (sg) {
559730ffcbSVarun Prakash sg = sg_next(sg);
569730ffcbSVarun Prakash if (sg)
579730ffcbSVarun Prakash addr = sg_dma_address(sg);
589730ffcbSVarun Prakash }
599730ffcbSVarun Prakash }
609730ffcbSVarun Prakash ppod->addr[i] = sg ? cpu_to_be64(addr + offset) : 0ULL;
619730ffcbSVarun Prakash }
629730ffcbSVarun Prakash
639730ffcbSVarun Prakash static struct sk_buff *
cxgbit_ppod_init_idata(struct cxgbit_device * cdev,struct cxgbi_ppm * ppm,unsigned int idx,unsigned int npods,unsigned int tid)649730ffcbSVarun Prakash cxgbit_ppod_init_idata(struct cxgbit_device *cdev, struct cxgbi_ppm *ppm,
659730ffcbSVarun Prakash unsigned int idx, unsigned int npods, unsigned int tid)
669730ffcbSVarun Prakash {
679730ffcbSVarun Prakash struct ulp_mem_io *req;
689730ffcbSVarun Prakash struct ulptx_idata *idata;
699730ffcbSVarun Prakash unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ppm->llimit;
709730ffcbSVarun Prakash unsigned int dlen = npods << PPOD_SIZE_SHIFT;
719730ffcbSVarun Prakash unsigned int wr_len = roundup(sizeof(struct ulp_mem_io) +
729730ffcbSVarun Prakash sizeof(struct ulptx_idata) + dlen, 16);
739730ffcbSVarun Prakash struct sk_buff *skb;
749730ffcbSVarun Prakash
759730ffcbSVarun Prakash skb = alloc_skb(wr_len, GFP_KERNEL);
769730ffcbSVarun Prakash if (!skb)
779730ffcbSVarun Prakash return NULL;
789730ffcbSVarun Prakash
794df864c1SJohannes Berg req = __skb_put(skb, wr_len);
809730ffcbSVarun Prakash INIT_ULPTX_WR(req, wr_len, 0, tid);
819730ffcbSVarun Prakash req->wr.wr_hi = htonl(FW_WR_OP_V(FW_ULPTX_WR) |
829730ffcbSVarun Prakash FW_WR_ATOMIC_V(0));
839730ffcbSVarun Prakash req->cmd = htonl(ULPTX_CMD_V(ULP_TX_MEM_WRITE) |
849730ffcbSVarun Prakash ULP_MEMIO_ORDER_V(0) |
859730ffcbSVarun Prakash T5_ULP_MEMIO_IMM_V(1));
869730ffcbSVarun Prakash req->dlen = htonl(ULP_MEMIO_DATA_LEN_V(dlen >> 5));
879730ffcbSVarun Prakash req->lock_addr = htonl(ULP_MEMIO_ADDR_V(pm_addr >> 5));
889730ffcbSVarun Prakash req->len16 = htonl(DIV_ROUND_UP(wr_len - sizeof(req->wr), 16));
899730ffcbSVarun Prakash
909730ffcbSVarun Prakash idata = (struct ulptx_idata *)(req + 1);
919730ffcbSVarun Prakash idata->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_IMM));
929730ffcbSVarun Prakash idata->len = htonl(dlen);
939730ffcbSVarun Prakash
949730ffcbSVarun Prakash return skb;
959730ffcbSVarun Prakash }
969730ffcbSVarun Prakash
979730ffcbSVarun Prakash static int
cxgbit_ppod_write_idata(struct cxgbi_ppm * ppm,struct cxgbit_sock * csk,struct cxgbi_task_tag_info * ttinfo,unsigned int idx,unsigned int npods,struct scatterlist ** sg_pp,unsigned int * sg_off)989730ffcbSVarun Prakash cxgbit_ppod_write_idata(struct cxgbi_ppm *ppm, struct cxgbit_sock *csk,
999730ffcbSVarun Prakash struct cxgbi_task_tag_info *ttinfo, unsigned int idx,
1009730ffcbSVarun Prakash unsigned int npods, struct scatterlist **sg_pp,
1019730ffcbSVarun Prakash unsigned int *sg_off)
1029730ffcbSVarun Prakash {
1039730ffcbSVarun Prakash struct cxgbit_device *cdev = csk->com.cdev;
1049730ffcbSVarun Prakash struct sk_buff *skb;
1059730ffcbSVarun Prakash struct ulp_mem_io *req;
1069730ffcbSVarun Prakash struct ulptx_idata *idata;
1079730ffcbSVarun Prakash struct cxgbi_pagepod *ppod;
1089730ffcbSVarun Prakash unsigned int i;
1099730ffcbSVarun Prakash
1109730ffcbSVarun Prakash skb = cxgbit_ppod_init_idata(cdev, ppm, idx, npods, csk->tid);
1119730ffcbSVarun Prakash if (!skb)
1129730ffcbSVarun Prakash return -ENOMEM;
1139730ffcbSVarun Prakash
1149730ffcbSVarun Prakash req = (struct ulp_mem_io *)skb->data;
1159730ffcbSVarun Prakash idata = (struct ulptx_idata *)(req + 1);
1169730ffcbSVarun Prakash ppod = (struct cxgbi_pagepod *)(idata + 1);
1179730ffcbSVarun Prakash
1189730ffcbSVarun Prakash for (i = 0; i < npods; i++, ppod++)
1199730ffcbSVarun Prakash cxgbit_set_one_ppod(ppod, ttinfo, sg_pp, sg_off);
1209730ffcbSVarun Prakash
1219730ffcbSVarun Prakash __skb_queue_tail(&csk->ppodq, skb);
1229730ffcbSVarun Prakash
1239730ffcbSVarun Prakash return 0;
1249730ffcbSVarun Prakash }
1259730ffcbSVarun Prakash
1269730ffcbSVarun Prakash static int
cxgbit_ddp_set_map(struct cxgbi_ppm * ppm,struct cxgbit_sock * csk,struct cxgbi_task_tag_info * ttinfo)1279730ffcbSVarun Prakash cxgbit_ddp_set_map(struct cxgbi_ppm *ppm, struct cxgbit_sock *csk,
1289730ffcbSVarun Prakash struct cxgbi_task_tag_info *ttinfo)
1299730ffcbSVarun Prakash {
1309730ffcbSVarun Prakash unsigned int pidx = ttinfo->idx;
1319730ffcbSVarun Prakash unsigned int npods = ttinfo->npods;
1329730ffcbSVarun Prakash unsigned int i, cnt;
1339730ffcbSVarun Prakash struct scatterlist *sg = ttinfo->sgl;
1349730ffcbSVarun Prakash unsigned int offset = 0;
1359730ffcbSVarun Prakash int ret = 0;
1369730ffcbSVarun Prakash
1379730ffcbSVarun Prakash for (i = 0; i < npods; i += cnt, pidx += cnt) {
1389730ffcbSVarun Prakash cnt = npods - i;
1399730ffcbSVarun Prakash
1409730ffcbSVarun Prakash if (cnt > ULPMEM_IDATA_MAX_NPPODS)
1419730ffcbSVarun Prakash cnt = ULPMEM_IDATA_MAX_NPPODS;
1429730ffcbSVarun Prakash
1439730ffcbSVarun Prakash ret = cxgbit_ppod_write_idata(ppm, csk, ttinfo, pidx, cnt,
1449730ffcbSVarun Prakash &sg, &offset);
1459730ffcbSVarun Prakash if (ret < 0)
1469730ffcbSVarun Prakash break;
1479730ffcbSVarun Prakash }
1489730ffcbSVarun Prakash
1499730ffcbSVarun Prakash return ret;
1509730ffcbSVarun Prakash }
1519730ffcbSVarun Prakash
cxgbit_ddp_sgl_check(struct scatterlist * sg,unsigned int nents)1529730ffcbSVarun Prakash static int cxgbit_ddp_sgl_check(struct scatterlist *sg,
1539730ffcbSVarun Prakash unsigned int nents)
1549730ffcbSVarun Prakash {
1559730ffcbSVarun Prakash unsigned int last_sgidx = nents - 1;
1569730ffcbSVarun Prakash unsigned int i;
1579730ffcbSVarun Prakash
1589730ffcbSVarun Prakash for (i = 0; i < nents; i++, sg = sg_next(sg)) {
1599730ffcbSVarun Prakash unsigned int len = sg->length + sg->offset;
1609730ffcbSVarun Prakash
1619730ffcbSVarun Prakash if ((sg->offset & 0x3) || (i && sg->offset) ||
1629730ffcbSVarun Prakash ((i != last_sgidx) && (len != PAGE_SIZE))) {
1639730ffcbSVarun Prakash return -EINVAL;
1649730ffcbSVarun Prakash }
1659730ffcbSVarun Prakash }
1669730ffcbSVarun Prakash
1679730ffcbSVarun Prakash return 0;
1689730ffcbSVarun Prakash }
1699730ffcbSVarun Prakash
1709730ffcbSVarun Prakash static int
cxgbit_ddp_reserve(struct cxgbit_sock * csk,struct cxgbi_task_tag_info * ttinfo,unsigned int xferlen)1719730ffcbSVarun Prakash cxgbit_ddp_reserve(struct cxgbit_sock *csk, struct cxgbi_task_tag_info *ttinfo,
1729730ffcbSVarun Prakash unsigned int xferlen)
1739730ffcbSVarun Prakash {
1749730ffcbSVarun Prakash struct cxgbit_device *cdev = csk->com.cdev;
1759730ffcbSVarun Prakash struct cxgbi_ppm *ppm = cdev2ppm(cdev);
1769730ffcbSVarun Prakash struct scatterlist *sgl = ttinfo->sgl;
1779730ffcbSVarun Prakash unsigned int sgcnt = ttinfo->nents;
1789730ffcbSVarun Prakash unsigned int sg_offset = sgl->offset;
1799730ffcbSVarun Prakash int ret;
1809730ffcbSVarun Prakash
1819730ffcbSVarun Prakash if ((xferlen < DDP_THRESHOLD) || (!sgcnt)) {
1829730ffcbSVarun Prakash pr_debug("ppm 0x%p, pgidx %u, xfer %u, sgcnt %u, NO ddp.\n",
1839730ffcbSVarun Prakash ppm, ppm->tformat.pgsz_idx_dflt,
1849730ffcbSVarun Prakash xferlen, ttinfo->nents);
1859730ffcbSVarun Prakash return -EINVAL;
1869730ffcbSVarun Prakash }
1879730ffcbSVarun Prakash
1889730ffcbSVarun Prakash if (cxgbit_ddp_sgl_check(sgl, sgcnt) < 0)
1899730ffcbSVarun Prakash return -EINVAL;
1909730ffcbSVarun Prakash
1919730ffcbSVarun Prakash ttinfo->nr_pages = (xferlen + sgl->offset +
1929730ffcbSVarun Prakash (1 << PAGE_SHIFT) - 1) >> PAGE_SHIFT;
1939730ffcbSVarun Prakash
1949730ffcbSVarun Prakash /*
1959730ffcbSVarun Prakash * the ddp tag will be used for the ttt in the outgoing r2t pdu
1969730ffcbSVarun Prakash */
1979730ffcbSVarun Prakash ret = cxgbi_ppm_ppods_reserve(ppm, ttinfo->nr_pages, 0, &ttinfo->idx,
1989730ffcbSVarun Prakash &ttinfo->tag, 0);
1999730ffcbSVarun Prakash if (ret < 0)
2009730ffcbSVarun Prakash return ret;
2019730ffcbSVarun Prakash ttinfo->npods = ret;
2029730ffcbSVarun Prakash
2039730ffcbSVarun Prakash sgl->offset = 0;
2049730ffcbSVarun Prakash ret = dma_map_sg(&ppm->pdev->dev, sgl, sgcnt, DMA_FROM_DEVICE);
2059730ffcbSVarun Prakash sgl->offset = sg_offset;
2069730ffcbSVarun Prakash if (!ret) {
207cedefa85SVarun Prakash pr_debug("%s: 0x%x, xfer %u, sgl %u dma mapping err.\n",
2089730ffcbSVarun Prakash __func__, 0, xferlen, sgcnt);
2099730ffcbSVarun Prakash goto rel_ppods;
2109730ffcbSVarun Prakash }
2119730ffcbSVarun Prakash
2129730ffcbSVarun Prakash cxgbi_ppm_make_ppod_hdr(ppm, ttinfo->tag, csk->tid, sgl->offset,
2139730ffcbSVarun Prakash xferlen, &ttinfo->hdr);
2149730ffcbSVarun Prakash
2159730ffcbSVarun Prakash ret = cxgbit_ddp_set_map(ppm, csk, ttinfo);
2169730ffcbSVarun Prakash if (ret < 0) {
2179730ffcbSVarun Prakash __skb_queue_purge(&csk->ppodq);
2189730ffcbSVarun Prakash dma_unmap_sg(&ppm->pdev->dev, sgl, sgcnt, DMA_FROM_DEVICE);
2199730ffcbSVarun Prakash goto rel_ppods;
2209730ffcbSVarun Prakash }
2219730ffcbSVarun Prakash
2229730ffcbSVarun Prakash return 0;
2239730ffcbSVarun Prakash
2249730ffcbSVarun Prakash rel_ppods:
2259730ffcbSVarun Prakash cxgbi_ppm_ppod_release(ppm, ttinfo->idx);
2269730ffcbSVarun Prakash return -EINVAL;
2279730ffcbSVarun Prakash }
2289730ffcbSVarun Prakash
2299730ffcbSVarun Prakash void
cxgbit_get_r2t_ttt(struct iscsit_conn * conn,struct iscsit_cmd * cmd,struct iscsi_r2t * r2t)230*be36d683SMax Gurtovoy cxgbit_get_r2t_ttt(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
2319730ffcbSVarun Prakash struct iscsi_r2t *r2t)
2329730ffcbSVarun Prakash {
2339730ffcbSVarun Prakash struct cxgbit_sock *csk = conn->context;
2349730ffcbSVarun Prakash struct cxgbit_device *cdev = csk->com.cdev;
2359730ffcbSVarun Prakash struct cxgbit_cmd *ccmd = iscsit_priv_cmd(cmd);
2369730ffcbSVarun Prakash struct cxgbi_task_tag_info *ttinfo = &ccmd->ttinfo;
2378f13142aSColin Ian King int ret;
2389730ffcbSVarun Prakash
2399730ffcbSVarun Prakash if ((!ccmd->setup_ddp) ||
2409730ffcbSVarun Prakash (!test_bit(CSK_DDP_ENABLE, &csk->com.flags)))
2419730ffcbSVarun Prakash goto out;
2429730ffcbSVarun Prakash
2439730ffcbSVarun Prakash ccmd->setup_ddp = false;
2449730ffcbSVarun Prakash
2459730ffcbSVarun Prakash ttinfo->sgl = cmd->se_cmd.t_data_sg;
2469730ffcbSVarun Prakash ttinfo->nents = cmd->se_cmd.t_data_nents;
2479730ffcbSVarun Prakash
2489730ffcbSVarun Prakash ret = cxgbit_ddp_reserve(csk, ttinfo, cmd->se_cmd.data_length);
2499730ffcbSVarun Prakash if (ret < 0) {
250cedefa85SVarun Prakash pr_debug("csk 0x%p, cmd 0x%p, xfer len %u, sgcnt %u no ddp.\n",
2519730ffcbSVarun Prakash csk, cmd, cmd->se_cmd.data_length, ttinfo->nents);
2529730ffcbSVarun Prakash
2539730ffcbSVarun Prakash ttinfo->sgl = NULL;
2549730ffcbSVarun Prakash ttinfo->nents = 0;
2559730ffcbSVarun Prakash } else {
2569730ffcbSVarun Prakash ccmd->release = true;
2579730ffcbSVarun Prakash }
2589730ffcbSVarun Prakash out:
2599730ffcbSVarun Prakash pr_debug("cdev 0x%p, cmd 0x%p, tag 0x%x\n", cdev, cmd, ttinfo->tag);
2609730ffcbSVarun Prakash r2t->targ_xfer_tag = ttinfo->tag;
2619730ffcbSVarun Prakash }
2629730ffcbSVarun Prakash
cxgbit_unmap_cmd(struct iscsit_conn * conn,struct iscsit_cmd * cmd)263*be36d683SMax Gurtovoy void cxgbit_unmap_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd)
2649730ffcbSVarun Prakash {
2659730ffcbSVarun Prakash struct cxgbit_cmd *ccmd = iscsit_priv_cmd(cmd);
2669730ffcbSVarun Prakash
2679730ffcbSVarun Prakash if (ccmd->release) {
2686ecdafaeSVarun Prakash if (cmd->se_cmd.se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) {
2696ecdafaeSVarun Prakash put_page(sg_page(&ccmd->sg));
2706ecdafaeSVarun Prakash } else {
2719730ffcbSVarun Prakash struct cxgbit_sock *csk = conn->context;
2729730ffcbSVarun Prakash struct cxgbit_device *cdev = csk->com.cdev;
2739730ffcbSVarun Prakash struct cxgbi_ppm *ppm = cdev2ppm(cdev);
2746ecdafaeSVarun Prakash struct cxgbi_task_tag_info *ttinfo = &ccmd->ttinfo;
2759730ffcbSVarun Prakash
2761ae01724SVarun Prakash /* Abort the TCP conn if DDP is not complete to
2771ae01724SVarun Prakash * avoid any possibility of DDP after freeing
2781ae01724SVarun Prakash * the cmd.
2791ae01724SVarun Prakash */
2801ae01724SVarun Prakash if (unlikely(cmd->write_data_done !=
2811ae01724SVarun Prakash cmd->se_cmd.data_length))
2821ae01724SVarun Prakash cxgbit_abort_conn(csk);
2831ae01724SVarun Prakash
2846ecdafaeSVarun Prakash if (unlikely(ttinfo->sgl)) {
2859730ffcbSVarun Prakash dma_unmap_sg(&ppm->pdev->dev, ttinfo->sgl,
2869730ffcbSVarun Prakash ttinfo->nents, DMA_FROM_DEVICE);
2876ecdafaeSVarun Prakash ttinfo->nents = 0;
2886ecdafaeSVarun Prakash ttinfo->sgl = NULL;
2899730ffcbSVarun Prakash }
2906ecdafaeSVarun Prakash cxgbi_ppm_ppod_release(ppm, ttinfo->idx);
2916ecdafaeSVarun Prakash }
2929730ffcbSVarun Prakash ccmd->release = false;
2939730ffcbSVarun Prakash }
2949730ffcbSVarun Prakash }
2959730ffcbSVarun Prakash
cxgbit_ddp_init(struct cxgbit_device * cdev)2969730ffcbSVarun Prakash int cxgbit_ddp_init(struct cxgbit_device *cdev)
2979730ffcbSVarun Prakash {
2989730ffcbSVarun Prakash struct cxgb4_lld_info *lldi = &cdev->lldi;
2999730ffcbSVarun Prakash struct net_device *ndev = cdev->lldi.ports[0];
3009730ffcbSVarun Prakash struct cxgbi_tag_format tformat;
3019730ffcbSVarun Prakash int ret, i;
3029730ffcbSVarun Prakash
3039730ffcbSVarun Prakash if (!lldi->vr->iscsi.size) {
3049730ffcbSVarun Prakash pr_warn("%s, iscsi NOT enabled, check config!\n", ndev->name);
3059730ffcbSVarun Prakash return -EACCES;
3069730ffcbSVarun Prakash }
3079730ffcbSVarun Prakash
3089730ffcbSVarun Prakash memset(&tformat, 0, sizeof(struct cxgbi_tag_format));
3099730ffcbSVarun Prakash for (i = 0; i < 4; i++)
3109730ffcbSVarun Prakash tformat.pgsz_order[i] = (lldi->iscsi_pgsz_order >> (i << 3))
3119730ffcbSVarun Prakash & 0xF;
3129730ffcbSVarun Prakash cxgbi_tagmask_check(lldi->iscsi_tagmask, &tformat);
3139730ffcbSVarun Prakash
3149730ffcbSVarun Prakash ret = cxgbi_ppm_init(lldi->iscsi_ppm, cdev->lldi.ports[0],
3159730ffcbSVarun Prakash cdev->lldi.pdev, &cdev->lldi, &tformat,
316a248384eSVarun Prakash lldi->vr->iscsi.size, lldi->iscsi_llimit,
317a248384eSVarun Prakash lldi->vr->iscsi.start, 2,
318a248384eSVarun Prakash lldi->vr->ppod_edram.start,
319a248384eSVarun Prakash lldi->vr->ppod_edram.size);
3209730ffcbSVarun Prakash if (ret >= 0) {
3219730ffcbSVarun Prakash struct cxgbi_ppm *ppm = (struct cxgbi_ppm *)(*lldi->iscsi_ppm);
3229730ffcbSVarun Prakash
3239730ffcbSVarun Prakash if ((ppm->tformat.pgsz_idx_dflt < DDP_PGIDX_MAX) &&
3249730ffcbSVarun Prakash (ppm->ppmax >= 1024))
3259730ffcbSVarun Prakash set_bit(CDEV_DDP_ENABLE, &cdev->flags);
3269730ffcbSVarun Prakash ret = 0;
3279730ffcbSVarun Prakash }
3289730ffcbSVarun Prakash
3299730ffcbSVarun Prakash return ret;
3309730ffcbSVarun Prakash }
331