in bcm-vk/bcm_vk_msg.c [1074:1250]
ssize_t bcm_vk_write(struct file *p_file,
const char __user *buf,
size_t count,
loff_t *f_pos)
{
ssize_t rc;
struct bcm_vk_ctx *ctx = p_file->private_data;
struct bcm_vk *vk = container_of(ctx->miscdev, struct bcm_vk,
miscdev);
struct bcm_vk_msgq __iomem *msgq;
struct device *dev = &vk->pdev->dev;
struct bcm_vk_wkent *entry;
u32 sgl_extra_blks;
u32 q_num;
u32 msg_size;
u32 msgq_size;
if (!bcm_vk_drv_access_ok(vk))
return -EPERM;
dev_dbg(dev, "Msg count %zu\n", count);
/* first, do sanity check where count should be multiple of basic blk */
if (count & (VK_MSGQ_BLK_SIZE - 1)) {
dev_err(dev, "Failure with size %zu not multiple of %zu\n",
count, VK_MSGQ_BLK_SIZE);
rc = -EINVAL;
goto write_err;
}
/* allocate the work entry + buffer for size count and inband sgl */
entry = kzalloc(sizeof(*entry) + count + vk->ib_sgl_size,
GFP_KERNEL);
if (!entry) {
rc = -ENOMEM;
goto write_err;
}
/* now copy msg from user space, and then formulate the work entry */
if (copy_from_user(&entry->to_v_msg[0], buf, count)) {
rc = -EFAULT;
goto write_free_ent;
}
entry->to_v_blks = count >> VK_MSGQ_BLK_SZ_SHIFT;
entry->ctx = ctx;
/* do a check on the blk size which could not exceed queue space */
q_num = get_q_num(&entry->to_v_msg[0]);
msgq = vk->to_v_msg_chan.msgq[q_num];
msgq_size = readl_relaxed(&msgq->size);
if (entry->to_v_blks + (vk->ib_sgl_size >> VK_MSGQ_BLK_SZ_SHIFT)
> (msgq_size - 1)) {
dev_err(dev, "Blk size %d exceed max queue size allowed %d\n",
entry->to_v_blks, msgq_size - 1);
rc = -EINVAL;
goto write_free_ent;
}
/* Use internal message id */
entry->usr_msg_id = get_msg_id(&entry->to_v_msg[0]);
rc = bcm_vk_get_msg_id(vk);
if (rc == VK_MSG_ID_OVERFLOW) {
dev_err(dev, "msg_id overflow\n");
rc = -EOVERFLOW;
goto write_free_ent;
}
set_msg_id(&entry->to_v_msg[0], rc);
ctx->q_num = q_num;
dev_dbg(dev,
"[Q-%d]Message ctx id %d, usr_msg_id 0x%x sent msg_id 0x%x\n",
ctx->q_num, ctx->idx, entry->usr_msg_id,
get_msg_id(&entry->to_v_msg[0]));
if (entry->to_v_msg[0].function_id == VK_FID_TRANS_BUF) {
/* Convert any pointers to sg list */
unsigned int num_planes;
int dir;
struct _vk_data *data;
/*
* check if we are in reset, if so, no buffer transfer is
* allowed and return error.
*/
if (vk->reset_pid) {
dev_dbg(dev, "No Transfer allowed during reset, pid %d.\n",
ctx->pid);
rc = -EACCES;
goto write_free_msgid;
}
num_planes = entry->to_v_msg[0].cmd & VK_CMD_PLANES_MASK;
if ((entry->to_v_msg[0].cmd & VK_CMD_MASK) == VK_CMD_DOWNLOAD)
dir = DMA_FROM_DEVICE;
else
dir = DMA_TO_DEVICE;
/* Calculate vk_data location */
/* Go to end of the message */
msg_size = entry->to_v_msg[0].size;
if (msg_size > entry->to_v_blks) {
rc = -EMSGSIZE;
goto write_free_msgid;
}
data = (struct _vk_data *)&entry->to_v_msg[msg_size + 1];
/* Now back up to the start of the pointers */
data -= num_planes;
/* Convert user addresses to DMA SG List */
rc = bcm_vk_sg_alloc(dev, entry->dma, dir, data, num_planes);
if (rc)
goto write_free_msgid;
atomic_inc(&ctx->dma_cnt);
/* try to embed inband sgl */
sgl_extra_blks = bcm_vk_append_ib_sgl(vk, entry, data,
num_planes);
entry->to_v_blks += sgl_extra_blks;
entry->to_v_msg[0].size += sgl_extra_blks;
} else if (entry->to_v_msg[0].function_id == VK_FID_INIT &&
entry->to_v_msg[0].context_id == VK_NEW_CTX) {
/*
* Init happens in 2 stages, only the first stage contains the
* pid that needs translating.
*/
pid_t org_pid, pid;
/*
* translate the pid into the unique host space as user
* may run sessions inside containers or process
* namespaces.
*/
#define VK_MSG_PID_MASK 0xffffff00
#define VK_MSG_PID_SH 8
org_pid = (entry->to_v_msg[0].arg & VK_MSG_PID_MASK)
>> VK_MSG_PID_SH;
pid = task_tgid_nr(current);
entry->to_v_msg[0].arg =
(entry->to_v_msg[0].arg & ~VK_MSG_PID_MASK) |
(pid << VK_MSG_PID_SH);
if (org_pid != pid)
dev_dbg(dev, "In PID 0x%x(%d), converted PID 0x%x(%d)\n",
org_pid, org_pid, pid, pid);
}
/*
* store work entry to pending queue until a response is received.
* This needs to be done before enqueuing the message
*/
bcm_vk_append_pendq(&vk->to_v_msg_chan, q_num, entry);
rc = bcm_to_v_msg_enqueue(vk, entry);
if (rc) {
dev_err(dev, "Fail to enqueue msg to to_v queue\n");
/* remove message from pending list */
entry = bcm_vk_dequeue_pending
(vk,
&vk->to_v_msg_chan,
q_num,
get_msg_id(&entry->to_v_msg[0]));
goto write_free_ent;
}
return count;
write_free_msgid:
bcm_vk_msgid_bitmap_clear(vk, get_msg_id(&entry->to_v_msg[0]), 1);
write_free_ent:
kfree(entry);
write_err:
return rc;
}