[PATCH v6 02/24] soc: qcom: Add APR bus driver

Srinivas Kandagatla srinivas.kandagatla at linaro.org
Sat Apr 28 04:12:51 PDT 2018


Thanks Bjorn for the review comments.

On 28/04/18 05:51, Bjorn Andersson wrote:
> On Thu 26 Apr 02:45 PDT 2018, Srinivas Kandagatla wrote:
>> diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c
> [..]
>> +int apr_send_pkt(struct apr_device *adev, void *buf)
> 
> Sorry, but I think we have discussed this before?
> 
Yes, I did mention that I would give it a try and see, This change was 
pretty intrusive when I last looked at this.

I agree with you on asymmetry! I will change this and add struc apr_pkt 
which would apr_hdr followed by payload. This should also work for 
callback as well.

> "buf" isn't some random buffer to be sent, it is a apr_hdr followed by
> some data. As such I think you should make this type struct apr_hdr *,
> or if you think that doesn't imply there's payload make a type apr_pkt
> that has a payload[] at the end.
> 
> It will make it obvious for both future readers and the compiler what
> kind of data we're passing here.
> 
> 
> This comment also applies to functions calling functions that calls
> apr_send_pkt() as they too lug around a void *.
> 
>> +{
>> +	struct apr *apr = dev_get_drvdata(adev->dev.parent);
>> +	struct apr_hdr *hdr;
>> +	unsigned long flags;
>> +	int ret;
>> +
>> +	spin_lock_irqsave(&adev->lock, flags);
>> +
>> +	hdr = (struct apr_hdr *)buf;
>> +	hdr->src_domain = APR_DOMAIN_APPS;
>> +	hdr->src_svc = adev->svc_id;
>> +	hdr->dest_domain = adev->domain_id;
>> +	hdr->dest_svc = adev->svc_id;
>> +
>> +	ret = rpmsg_trysend(apr->ch, buf, hdr->pkt_size);
>> +	if (ret) {
>> +		dev_err(&adev->dev, "Unable to send APR pkt %d\n",
>> +			hdr->pkt_size);
> 
> Afaict all callers of this function will print an error message,
> sometimes on more than one level in the stack. And if some code path
> does retry sending you will get a printout for each attempt, even though
> the caller is fine with it.
> 
> I would recommend unlocking the spinlock and then do:

I can do that !!

> 
> 	return ret ? : hdr->pkt_size;
> 
>> +	} else {
>> +		ret = hdr->pkt_size;
>> +	}
>> +
>> +	spin_unlock_irqrestore(&adev->lock, flags);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(apr_send_pkt);
>> +

>> +
>> +static int apr_callback(struct rpmsg_device *rpdev, void *buf,
>> +				  int len, void *priv, u32 addr)
>> +{
>> +	struct apr *apr = dev_get_drvdata(&rpdev->dev);
>> +	struct apr_client_message data;
>> +	struct apr_device *p, *c_svc = NULL;
>> +	struct apr_driver *adrv = NULL;
>> +	struct apr_hdr *hdr;
>> +	unsigned long flags;
>> +	uint16_t hdr_size;
>> +	uint16_t msg_type;
>> +	uint16_t ver;
>> +	uint16_t svc;
>> +
>> +	if (len <= APR_HDR_SIZE) {
>> +		dev_err(apr->dev, "APR: Improper apr pkt received:%p %d\n",
>> +			buf, len);
>> +		return -EINVAL;
>> +	}
>> +
>> +	hdr = buf;
>> +	ver = APR_HDR_FIELD_VER(hdr->hdr_field);
>> +	if (ver > APR_PKT_VER + 1)
>> +		return -EINVAL;
>> +
>> +	hdr_size = APR_HDR_FIELD_SIZE_BYTES(hdr->hdr_field);
>> +	if (hdr_size < APR_HDR_SIZE) {
>> +		dev_err(apr->dev, "APR: Wrong hdr size:%d\n", hdr_size);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (hdr->pkt_size < APR_HDR_SIZE) {
> 
> I think it would be nice to make sure that hdr->pkt_size is < len as
> well, to reject messages that larger than the incoming buffer.
> 
> The pkt_size should be in the ballpark of len, could this check be
> changed to hdr->pkt_size != len?

Yep, It makes sense, I can add that check here.

> 
>> +		dev_err(apr->dev, "APR: Wrong paket size\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	msg_type = APR_HDR_FIELD_MT(hdr->hdr_field);
>> +	if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) {
>> +		dev_err(apr->dev, "APR: Wrong message type: %d\n", msg_type);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (hdr->src_domain >= APR_DOMAIN_MAX ||
>> +			hdr->dest_domain >= APR_DOMAIN_MAX ||
>> +			hdr->src_svc >= APR_SVC_MAX ||
>> +			hdr->dest_svc >= APR_SVC_MAX) {
>> +		dev_err(apr->dev, "APR: Wrong APR header\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	svc = hdr->dest_svc;
>> +	spin_lock_irqsave(&apr->svcs_lock, flags);
>> +	list_for_each_entry(p, &apr->svcs, node) {
> 
> Rather than doing a O(n) search for the svc with svc_id you could use a
> idr or a radix_tree to keep the id -> svc mapping.

Am not 100% sure idr is correct thing here, as the svc_ids are static 
and the list we are talking here in worst case would be max of 13 
entires, in audio case it is just 4 services.
I think using radix_tree would be over do.

> 
>> +		if (svc == p->svc_id) {
>> +			c_svc = p;
>> +			if (c_svc->dev.driver)
>> +				adrv = to_apr_driver(c_svc->dev.driver);
>> +			break;
>> +		}
>> +	}
>> +	spin_unlock_irqrestore(&apr->svcs_lock, flags);
>> +
>> +	if (!adrv) {
>> +		dev_err(apr->dev, "APR: service is not registered\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	data.payload_size = hdr->pkt_size - hdr_size;
>> +	data.opcode = hdr->opcode;
>> +	data.src_port = hdr->src_port;
>> +	data.dest_port = hdr->dest_port;
>> +	data.token = hdr->token;
>> +	data.msg_type = msg_type;
>> +
>> +	if (data.payload_size > 0)
>> +		data.payload = buf + hdr_size;
>> +
> 
> Making a verbatim copy of parts of the hdr and then passing that to the
> APR devices creates an asymmetry in the send/callback API, without a
> whole lot of benefits. I would prefer that you introduce the apr_pkt, as
> described above, and once you have validated the size et al and found
> the service you just pass it along.
Okay, this would be a big rework, I will do it in next version.

> 
>> +	adrv->callback(c_svc, &data);
>> +
>> +	return 0;
>> +}
> [..]
>> +static int apr_uevent(struct device *dev, struct kobj_uevent_env *env)
>> +{
>> +	struct apr_device *adev = to_apr_device(dev);
>> +	int ret;
>> +
>> +	ret = of_device_uevent_modalias(dev, env);
>> +	if (ret != -ENODEV)
>> +		return ret;
>> +
>> +	return add_uevent_var(env, "MODALIAS= apr:%s", adev->name);
> 
> No space between '=' and "apr".
>
Yep.

>> +}
>> +
>> +struct bus_type aprbus = {
>> +	.name		= "aprbus",
> 
> Most busses doesn't have "bus" in the name.
> 
Yep, just "apr" make sense.

>> +	.match		= apr_device_match,
>> +	.probe		= apr_device_probe,
>> +	.uevent		= apr_uevent,
>> +	.remove		= apr_device_remove,
>> +};
>> +EXPORT_SYMBOL_GPL(aprbus);
>> +
>> +static int apr_add_device(struct device *dev, struct device_node *np,
>> +			  const struct apr_device_id *id)
>> +{
>> +	struct apr *apr = dev_get_drvdata(dev);
>> +	struct apr_device *adev = NULL;
>> +
>> +	adev = kzalloc(sizeof(*adev), GFP_KERNEL);
>> +	if (!adev)
>> +		return -ENOMEM;
>> +
>> +	spin_lock_init(&adev->lock);
>> +
>> +	adev->svc_id = id->svc_id;
>> +	adev->domain_id = id->domain_id;
>> +	adev->version = id->svc_version;
>> +	if (np)
>> +		strncpy(adev->name, np->name, APR_NAME_SIZE);
>> +	else
>> +		strncpy(adev->name, id->name, APR_NAME_SIZE);
>> +
>> +	dev_set_name(&adev->dev, "aprsvc:%s:%x:%x", adev->name,
>> +		     id->domain_id, id->svc_id);
>> +
>> +	adev->dev.bus = &aprbus;
>> +	adev->dev.parent = dev;
>> +	adev->dev.of_node = np;
>> +	adev->dev.release = apr_dev_release;
>> +	adev->dev.driver = NULL;
>> +
>> +	spin_lock(&apr->svcs_lock);
>> +	list_add_tail(&adev->node, &apr->svcs);
>> +	spin_unlock(&apr->svcs_lock);
>> +
>> +	dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev));
>> +
>> +	return device_register(&adev->dev);
> 
> If this fails you must call put_device(&adev->dev);
> 
Agree!!
>> +}
>> +
>> +static void of_register_apr_devices(struct device *dev)
>> +{
>> +	struct apr *apr = dev_get_drvdata(dev);
>> +	struct device_node *node;
>> +
>> +	for_each_child_of_node(dev->of_node, node) {
>> +		struct apr_device_id id = { {0} };
>> +
>> +		if (of_property_read_u32(node, "reg", &id.svc_id))
>> +			continue;
>> +
>> +		id.domain_id = apr->dest_domain_id;
>> +
>> +		if (apr_add_device(dev, node, &id))
>> +			dev_err(dev, "Failed to add arp %d svc\n", id.svc_id);
>> +	}
>> +}
> [..]
>> +
>> +static int __init apr_init(void)
>> +{
>> +	int ret;
>> +
>> +	ret = bus_register(&aprbus);
>> +	if (!ret)
>> +		ret = register_rpmsg_driver(&apr_driver);
> 
> bus_unregister() if ret here.
> 
Yep.

Thanks,
srini
>> +
>> +	return ret;
>> +}
>> +
> 
> Regards,
> Bjorn
> 



More information about the linux-arm-kernel mailing list