ssize_t bcm_vk_write()

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;
}