virtio /rpmsg 发送data/msg流程

int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,
void *data, int len, bool wait)
{

struct virtproc_info *vrp = rpdev->vrp;
struct device *dev = &rpdev->dev;
struct scatterlist sg;
struct rpmsg_hdr *msg;
int err;
/* bcasting isn't allowed */
if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) {
    dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst);
    return -EINVAL;
}

/*
 * We currently use fixed-sized buffers, and therefore the payload
 * length is limited.
 *
 * One of the possible improvements here is either to support
 * user-provided buffers (and then we can also support zero-copy
 * messaging), or to improve the buffer allocator, to support
 * variable-length buffer sizes.
 */
if (len > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) {
    dev_err(dev, "message is too big (%d)\n", len);
    return -EMSGSIZE;
}

/* grab a buffer */
msg = get_a_tx_buf(vrp); **// 如果是前num_sbufs次的话,获取到的是vrp->sbufs + RPMSG_BUF_SIZE * vrp->last_sbuf++(其实就是按vrp的首地址按照RPMSG_BUF_SIZE划分了几个小块); 后面再get的话,需要调用virtqueue_get_buf(后面再写这个函数)**
if (!msg && !wait)
    return -ENOMEM;

/* no free buffer ? wait for one (but bail after 15 seconds) */
while (!msg) {
    /* enable "tx-complete" interrupts, if not already enabled */
    rpmsg_upref_sleepers(vrp);

    /*
     * sleep until a free buffer is available or 15 secs elapse.
     * the timeout period is not configurable because there's
     * little point in asking drivers to specify that.
     * if later this happens to be required, it'd be easy to add.
     */
    err = wait_event_interruptible_timeout(vrp->sendq,
                (msg = get_a_tx_buf(vrp)),
                msecs_to_jiffies(15000));

    /* disable "tx-complete" interrupts if we're the last sleeper */
    rpmsg_downref_sleepers(vrp);

    /* timeout ? */
    if (!err) {
        dev_err(dev, "timeout waiting for a tx buffer\n");
        return -ERESTARTSYS;
    }
}
    /*对之前申请的msg地址对应的内存赋值,将要发送的data copy到msg -〉data*/
msg->len = len;
msg->flags = 0;
msg->src = src;
msg->dst = dst;
msg->reserved = 0;
memcpy(msg->data, data, len);

dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n",
                msg->src, msg->dst, msg->len,
                msg->flags, msg->reserved);
sg_init_one(&sg, msg, sizeof(*msg) + len);
//初始化一个sg list ,将msg加到这个sg list中
mutex_lock(&vrp->tx_lock);

/* add message to the remote processor's virtqueue */
err = virtqueue_add_outbuf(vrp->svq, &sg, 1, msg, GFP_KERNEL);
**//将msg加到outbuf中,下面写这个函数**
if (err) {
    /*
     * need to reclaim the buffer here, otherwise it's l重点内容ost
     * (memory won't leak, but rpmsg won't use it again for TX).
     * this will wait for a buffer management overhaul.
     */
    dev_err(dev, "virtqueue_add_outbuf failed: %d\n", err);
    goto out;
}

/* tell the remote processor it has a pending message to read */
virtqueue_kick(vrp->svq);
out:
mutex_unlock(&vrp->tx_lock);
return err;
}

void *virtqueue_get_buf(struct virtqueue *_vq, unsigned int *len)
{

struct vring_virtqueue *vq = to_vvq(_vq);
              void *ret;
    unsigned int i;
    u16 last_used;
    START_USE(vq);
    if (unlikely(vq->broken)) {
        END_USE(vq);
        return NULL;
    }

if (!more_used(vq)) {
    pr_debug("No more buffers in queue\n");
    printk("No more buffers in queue\n");
    END_USE(vq);
    return NULL;
}

/* Only get used array entries after they have been exposed by host. */
virtio_rmb(vq->weak_barriers);

last_used = (vq->last_used_idx & (vq->vring.num - 1));
i = vq->vring.used->ring[last_used].id; //其实这个i和上面的last_used是一样的。
*len = vq->vring.used->ring[last_used].len;

if (unlikely(i >= vq->vring.num)) {
    BAD_RING(vq, "id %u out of range\n", i);

    printk("id %u out of range\n", i);
    return NULL;
}
if (unlikely(!vq->data[i])) {
    BAD_RING(vq, "id %u is not a head!\n", i);

    printk("id %u is not a head!\n", i);
    return NULL;
}

/* detach_buf clears data, so grab it now. */
ret = vq->data[i];
detach_buf(vq, i);//将vq-〉data[i]置零
vq->last_used_idx++;
/* If we expect an interrupt for the next entry, tell host
 * by writing event index and flush out the write before
 * the read in the next get_buf call. */
if (!(vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT)) {
    vring_used_event(&vq->vring) = vq->last_used_idx;
    virtio_mb(vq->weak_barriers);
}
END_USE(vq);
return ret;//返回vq->data[i]
}

将buf加到outbuf或者inbuf中,都调用这个函数,根据参数区分

static inline int virtqueue_add(struct virtqueue *_vq,
struct scatterlist *sgs[],
struct scatterlist *(*next)
(struct scatterlist , unsigned int ),
unsigned int total_out,
unsigned int total_in,
unsigned int out_sgs,
unsigned int in_sgs,
void *data,
gfp_t gfp)
{

struct vring_virtqueue *vq = to_vvq(_vq);
struct scatterlist *sg;
unsigned int i, n, avail, uninitialized_var(prev), total_sg;
int head;
START_USE(vq);

BUG_ON(data == NULL);


total_sg = total_in + total_out;

/* If the host supports indirect descriptor tables, and we have multiple
 * buffers, then go indirect. FIXME: tune this threshold */
if (vq->indirect && total_sg > 1 && vq->vq.num_free) {
    head = vring_add_indirect(vq, sgs, next, total_sg, total_out,
                  total_in,
                  out_sgs, in_sgs, gfp);
    if (likely(head >= 0))
        goto add_head;
}

BUG_ON(total_sg > vq->vring.num);
BUG_ON(total_sg == 0);

if (vq->vq.num_free < total_sg) {
    pr_debug("Can't add buf len %i - avail = %i\n",
         total_sg, vq->vq.num_free);
    /* FIXME: for historical reasons, we force a notify here if
     * there are outgoing parts to the buffer.  Presumably the
     * host should service the ring ASAP. */
    if (out_sgs)
        vq->notify(&vq->vq);
    END_USE(vq);
    return -ENOSPC;
}

/* We're about to use some buffers from the free list. */
vq->vq.num_free -= total_sg;

head = i = vq->free_head;
for (n = 0; n < out_sgs; n++) {
    for (sg = sgs[n]; sg; sg = next(sg, &total_out)) {
        vq->vring.desc[i].flags = VRING_DESC_F_NEXT;
        vq->vring.desc[i].addr = sg_phys(sg);//实际物理地址,以便对端(host)可以读到
        vq->vring.desc[i].len = sg->length;
        prev = i;
        i = vq->vring.desc[i].next;
    }
}
for (; n < (out_sgs + in_sgs); n++) {
    for (sg = sgs[n]; sg; sg = next(sg, &total_in)) {
        vq->vring.desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE;
        vq->vring.desc[i].addr = sg_phys(sg);//实际物理地址,以便对端(host)可以读到
        vq->vring.desc[i].len = sg->length;
        prev = i;
        i = vq->vring.desc[i].next;
    }
}
/* Last one doesn't continue. */
vq->vring.desc[prev].flags &= ~VRING_DESC_F_NEXT;

/* Update free pointer */
vq->free_head = i;

add_head:
/* Set token. */
vq->data[head] = data; //vp-〉data记录data的虚拟地址。

/* Put entry in available array (but don't update avail->idx until they
 * do sync). */
avail = (vq->vring.avail->idx & (vq->vring.num-1)); //这个avail和vq->free_head其实是相等的。
vq->vring.avail->ring[avail] = head;

/* Descriptors and available array need to be set before we expose the
 * new available array entries. */
virtio_wmb(vq->weak_barriers);
vq->vring.avail->idx++;
vq->num_added++;

/* This is very unlikely, but theoretically possible.  Kick
 * just in case. */
if (unlikely(vq->num_added == (1 << 16) - 1))
    virtqueue_kick(_vq);

pr_debug("Added buffer head %i to %p\n", head, vq);
END_USE(vq);
return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值