[PATCH v6 5/5] firmware: imx: adds miscdev
Sascha Hauer
s.hauer at pengutronix.de
Tue Jul 23 07:20:32 PDT 2024
Hi Pankaj,
On Mon, Jul 22, 2024 at 10:21:40AM +0530, Pankaj Gupta wrote:
> +static int se_ioctl_cmd_snd_rcv_rsp_handler(struct se_if_device_ctx *dev_ctx,
> + u64 arg)
> +{
> + struct se_if_priv *priv = dev_get_drvdata(dev_ctx->dev);
> + struct se_ioctl_cmd_snd_rcv_rsp_info cmd_snd_rcv_rsp_info;
> + struct se_api_msg *tx_msg __free(kfree) = NULL;
> + struct se_api_msg *rx_msg __free(kfree) = NULL;
> + int err = 0;
> +
> + if (copy_from_user(&cmd_snd_rcv_rsp_info, (u8 *)arg,
> + sizeof(cmd_snd_rcv_rsp_info))) {
> + dev_err(dev_ctx->priv->dev,
> + "%s: Failed to copy cmd_snd_rcv_rsp_info from user\n",
> + dev_ctx->miscdev.name);
> + err = -EFAULT;
> + goto exit;
> + }
> +
> + if (cmd_snd_rcv_rsp_info.tx_buf_sz < SE_MU_HDR_SZ) {
> + dev_err(dev_ctx->priv->dev,
> + "%s: User buffer too small(%d < %d)\n",
> + dev_ctx->miscdev.name,
> + cmd_snd_rcv_rsp_info.tx_buf_sz,
> + SE_MU_HDR_SZ);
> + err = -ENOSPC;
> + goto exit;
> + }
> +
> + rx_msg = kzalloc(cmd_snd_rcv_rsp_info.rx_buf_sz, GFP_KERNEL);
> + if (!rx_msg) {
> + err = -ENOMEM;
> + goto exit;
> + }
> +
> + tx_msg = memdup_user(cmd_snd_rcv_rsp_info.tx_buf,
> + cmd_snd_rcv_rsp_info.tx_buf_sz);
> + if (IS_ERR(tx_msg)) {
> + err = PTR_ERR(tx_msg);
> + goto exit;
> + }
> +
> + if (tx_msg->header.tag != priv->cmd_tag) {
> + err = -EINVAL;
> + goto exit;
> + }
> +
> + guard(mutex)(&priv->se_if_cmd_lock);
> + priv->waiting_rsp_dev = dev_ctx;
> + dev_ctx->temp_resp_size = cmd_snd_rcv_rsp_info.rx_buf_sz;
> +
> + /* Device Context that is assigned to be a
> + * FW's command receiver, has pre-allocated buffer.
> + */
> + if (dev_ctx != priv->cmd_receiver_dev)
> + dev_ctx->temp_resp = rx_msg;
> +
> + err = ele_miscdev_msg_send(dev_ctx,
> + tx_msg,
> + cmd_snd_rcv_rsp_info.tx_buf_sz);
> + if (err < 0)
> + goto exit;
> +
> + cmd_snd_rcv_rsp_info.tx_buf_sz = err;
> +
> + err = ele_miscdev_msg_rcv(dev_ctx,
> + cmd_snd_rcv_rsp_info.rx_buf,
> + cmd_snd_rcv_rsp_info.rx_buf_sz);
Ok, here you now have serialized sending and receiving messages,
With this you no longer need priv->waiting_rsp_dev, dev_ctx->temp_resp
and dev_ctx->temp_resp_size. Drop these for further cleanup.
> +}
> +
> +static int se_ioctl_get_mu_info(struct se_if_device_ctx *dev_ctx,
> + u64 arg)
> +{
> + struct se_if_priv *priv = dev_get_drvdata(dev_ctx->dev);
> + struct se_if_node_info *if_node_info;
> + struct se_ioctl_get_if_info info;
> + int err = 0;
> +
> + if_node_info = (struct se_if_node_info *)priv->info;
> +
> + info.se_if_id = if_node_info->se_if_id;
> + info.interrupt_idx = 0;
> + info.tz = 0;
> + info.did = if_node_info->se_if_did;
> + info.cmd_tag = if_node_info->cmd_tag;
> + info.rsp_tag = if_node_info->rsp_tag;
> + info.success_tag = if_node_info->success_tag;
> + info.base_api_ver = if_node_info->base_api_ver;
> + info.fw_api_ver = if_node_info->fw_api_ver;
This really shouldn't be here. You pass cmd_tag and rsp_tag to userspace
just to guide userspace how to construct a message.
This shows that the messages should be constructed in the Kernel rather
than in userspace. Just pass the message content from userspace to the
kernel and let the kernel build the message on the sender side.
> +/* IOCTL entry point of a character device */
> +static long se_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
> +{
> + struct se_if_device_ctx *dev_ctx = container_of(fp->private_data,
> + struct se_if_device_ctx,
> + miscdev);
> + struct se_if_priv *se_if_priv = dev_ctx->priv;
> + int err = -EINVAL;
> +
> + /* Prevent race during change of device context */
> + if (down_interruptible(&dev_ctx->fops_lock))
> + return -EBUSY;
> +
> + switch (cmd) {
> + case SE_IOCTL_ENABLE_CMD_RCV:
> + if (!se_if_priv->cmd_receiver_dev) {
> + err = 0;
> + se_if_priv->cmd_receiver_dev = dev_ctx;
> + dev_ctx->temp_resp = kzalloc(MAX_NVM_MSG_LEN, GFP_KERNEL);
> + if (!dev_ctx->temp_resp)
> + err = -ENOMEM;
> + }
cmd_receiver_dev isn't locked by anything, still it can be accessed by
different userspace processes.
Besides, when already another instance is configured for receiving
commands I would expect an -EBUSY here instead of silently ignoring the
ioctl.
> + break;
> + case SE_IOCTL_GET_MU_INFO:
> + err = se_ioctl_get_mu_info(dev_ctx, arg);
> + break;
> + case SE_IOCTL_SETUP_IOBUF:
> + err = se_ioctl_setup_iobuf_handler(dev_ctx, arg);
> + break;
> + case SE_IOCTL_GET_SOC_INFO:
> + err = se_ioctl_get_se_soc_info_handler(dev_ctx, arg);
> + break;
> + case SE_IOCTL_CMD_SEND_RCV_RSP:
> + err = se_ioctl_cmd_snd_rcv_rsp_handler(dev_ctx, arg);
> + break;
> +
> + default:
> + err = -EINVAL;
> + dev_dbg(se_if_priv->dev,
> + "%s: IOCTL %.8x not supported\n",
> + dev_ctx->miscdev.name,
> + cmd);
> + }
> +
> + up(&dev_ctx->fops_lock);
> + return (long)err;
> +}
> +
...
> +static int init_device_context(struct se_if_priv *priv)
> +{
> + const struct se_if_node_info *info = priv->info;
> + struct se_if_device_ctx *dev_ctx;
> + u8 *devname;
> + int ret = 0;
> + int i;
> +
> + priv->ctxs = devm_kzalloc(priv->dev, sizeof(dev_ctx) * priv->max_dev_ctx,
> + GFP_KERNEL);
> +
> + if (!priv->ctxs) {
> + ret = -ENOMEM;
> + return ret;
> + }
> +
> + /* Create users */
> + for (i = 0; i < priv->max_dev_ctx; i++) {
> + dev_ctx = devm_kzalloc(priv->dev, sizeof(*dev_ctx), GFP_KERNEL);
> + if (!dev_ctx) {
> + ret = -ENOMEM;
> + return ret;
> + }
> +
> + dev_ctx->dev = priv->dev;
> + dev_ctx->status = SE_IF_CTX_FREE;
> + dev_ctx->priv = priv;
> +
> + priv->ctxs[i] = dev_ctx;
> +
> + /* Default value invalid for an header. */
> + init_waitqueue_head(&dev_ctx->wq);
> +
> + INIT_LIST_HEAD(&dev_ctx->pending_out);
> + INIT_LIST_HEAD(&dev_ctx->pending_in);
> + sema_init(&dev_ctx->fops_lock, 1);
> +
> + devname = devm_kasprintf(priv->dev, GFP_KERNEL, "%s_ch%d",
> + info->se_name, i);
> + if (!devname) {
> + ret = -ENOMEM;
> + return ret;
> + }
> +
> + dev_ctx->miscdev.name = devname;
> + dev_ctx->miscdev.minor = MISC_DYNAMIC_MINOR;
> + dev_ctx->miscdev.fops = &se_if_fops;
> + dev_ctx->miscdev.parent = priv->dev;
> + ret = misc_register(&dev_ctx->miscdev);
> + if (ret) {
> + dev_err(priv->dev, "failed to register misc device %d\n",
> + ret);
> + return ret;
> + }
Here you register four character devices which all allow a single open.
There's no need to artificially limit the number of users. Just register
a single character device, allow it to be opened multiple times and
allocate the instance specific context as necessary in se_if_fops_open().
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the linux-arm-kernel
mailing list