iommu.lists.linux-foundation.org archive mirror
 help / color / mirror / Atom feed
From: Lu Baolu <baolu.lu@linux.intel.com>
To: "Prakhya, Sai Praneeth" <sai.praneeth.prakhya@intel.com>,
	"iommu@lists.linux-foundation.org"
	<iommu@lists.linux-foundation.org>
Cc: "Raj, Ashok" <ashok.raj@intel.com>,
	Will Deacon <will.deacon@arm.com>,
	Robin Murphy <robin.murphy@arm.com>,
	Christoph Hellwig <hch@lst.de>
Subject: Re: [PATCH V2 3/5] iommu: Add support to change default domain of an iommu_group
Date: Mon, 24 Feb 2020 15:39:06 +0800	[thread overview]
Message-ID: <d5138046-b613-b070-2af8-4a9a519ca42a@linux.intel.com> (raw)
In-Reply-To: <FFF73D592F13FD46B8700F0A279B802F5721A118@ORSMSX114.amr.corp.intel.com>

On 2020/2/24 15:03, Prakhya, Sai Praneeth wrote:
>> On 2020/2/24 11:20, Prakhya, Sai Praneeth wrote:
>>>>> +	list_for_each_entry_safe(grp_dev, temp, &group->devices, list) {
>>>>> +		struct device *dev;
>>>>> +
>>>>> +		dev = grp_dev->dev;
>>>>> +		iommu_release_device(dev);
>>>>> +
>>>>> +		ret = iommu_group_add_device(group, dev);
>>>>> +		if (ret)
>>>>> +			dev_err(dev, "Failed to add to iommu group %d: %d\n",
>>>>> +				group->id, ret);
>>>> Need to handle this error case.
>>> I wasn't sure on how to handle the error ☹
>>
>> Just roll back to the state before calling this function and return an appropriate
>> error value.
>>
>> The likely behavior is detaching the new domains from all devices (if it has
>> already attached), attaching the old domains to all devices in the group,
> 
> And while handling this error case, there is a possibility that attaching to old domain could fail.. so, we might need to handle that error case as well. If we plan to handle error case, since we have removed devices from group above, adding them back could fail too.. that would lead into handling error case for an error case.

We can assume that the old domain should always be attached back.
Otherwise, there must be some bugs in the vendor iommu driver.

It must be able to role back, otherwise users have to reboot the
system in order to use the devices in the group. This is not
acceptable in the production kernel.

> So, given the probability of these functions failing here are very low, I think, why not bite the bullet and say, add code to handle these error cases if we see that these functions are failing frequently? Otherwise the error handling code is just a dead code.
> 
>> cleaning
>> up all new resources allocated in this function, putting a error message to tell
>> the user why it fails and returning an error code.
>>
>>> i.e. group's domain/default_domain are already updated to new domain and
>> assume there are 'n' devices in the group and this failed for 'k'th device, I wasn't
>> sure how I could roll back the changes made for k-1 devices.
>>
>> A successful attach could be checked by (group->domain ==
>> group->default_domain).
> 
> No.. because I have manually set group->domain == group->default_domain = new_domain (did this because iommu_group_add_device() and iommu_group_create_direct_mappings() needs them)

You could set group->domain to the default domain only after it has been
attached to the device successfully, right?

> So, probably we might need some other way to check successful attach.
> 
>>> So, I thought probably just alert the user that there was an error while
>> changing default domain type and try updating for other devices in the group
>> (hopefully other devices might succeed). Also,*generally*  we shouldn't see any
>> errors here because all these devices were already in the same group earlier (we
>> aren’t adding/removing new devices to the group). We are just changing default
>> domain type and we already made sure that device could be in the requested
>> default domain type.
>>>
>>>>> +
>>>>> +		ret = prv_dom->ops->add_device(dev);
>>>>> +		if (ret)
>>>>> +			dev_err(dev, "Error adding to iommu: %d\n", ret);
>>>> Ditto.
>>>>
>>>>> +	}
>>>>> +
>>>>> +	iommu_group_put(group);
>>>>> +	iommu_domain_free(prv_dom);
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int is_driver_bound(struct device *dev, void *not_used) {
>>>>> +	int ret = 0;
>>>>> +
>>>>> +	device_lock(dev);
>>>>> +	if (device_is_bound(dev))
>>>>> +		ret = 1;
>>>>> +	device_unlock(dev);
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static ssize_t iommu_group_store_type(struct iommu_group *group,
>>>>> +				      const char *buf, size_t count) {
>>>>> +	int ret = 0, req_type = 0, req_auto = 0;
>>>>> +	struct iommu_domain *prv_dom;
>>>>> +	struct group_device *grp_dev;
>>>>> +	const struct iommu_ops *ops;
>>>>> +	struct device *dev;
>>>>> +
>>>>> +	if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
>>>>> +		return -EACCES;
>>>>> +
>>>>> +	if (WARN_ON(!group))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if (sysfs_streq(buf, "identity"))
>>>>> +		req_type = IOMMU_DOMAIN_IDENTITY;
>>>>> +	else if (sysfs_streq(buf, "DMA"))
>>>>> +		req_type = IOMMU_DOMAIN_DMA;
>>>>> +	else if (sysfs_streq(buf, "auto"))
>>>>> +		req_auto = 1;
>>>>> +	else
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	/*
>>>>> +	 * Check if any device in the group still has a driver binded to it.
>>>>> +	 * This might race with device driver probing code and unfortunately
>>>>> +	 * there is no clean way out of that either, locking all devices in the
>>>>> +	 * group and then do the re-attach will introduce a lock-inversion with
>>>>> +	 * group->mutex - Joerg.
>>>> Do you mean that we can't do below?
>>>>
>>>> mutex_lock(&group->mutex);
>>>> for_each_group_device()
>>>> 	device_lock(dev);
>>>>
>>>> /* Default domain switch */
>>>> for_each_group_device()
>>>> 	device_unlock()
>>>> mutex_unlock(&group->mutex)
>>> I think, Joerg talks about two issues here 1. is_driver_bound()
>>> takes/releases device_lock() which could race with device driver probing code
>> i.e. in an other terminal user could try to unload/load the module.
>>
>> So you need to device_lock() all devices during the whole process.
>> (I assume that load/unload device driver requiring this lock as well.
>> Didn't check it in code. Please correct me if I misunderstood it.)
> 
> Ok.. I will check this.
> 
>>> 2. We cannot do as you suggested above because the functions (one is
>> mentioned below) that switch default domain need to take
>> iommu_group_mutex lock. So, taking the mutex and then calling iommu
>> functions that switch default domain will dead lock.
>>
>> You probably need to move the mutex_lock() in that function out to the caller?
>> And only assert the lock has been held there.
> 
> I mean, there are many functions that are taking iommu_group_mutex_lock and they are being called as part of changing default domain.
> So, we would then need to change all those functions and let callers of those functions know that now it's their responsibility to take the lock and call these functions.
> Some of those functions are:
> 
> iommu_group_add_device
> iommu_group_remove_device
> iommu_group_for_each_dev
> 
> So, rather than that, I think it's better to delegate taking/releasing lock to individual functions than giving that responsibility to callers because then some callers might forget to take/release lock.

Okay. Fair enough.

> 
>>>
>>>>> +	 */
>>>>> +	if (iommu_group_for_each_dev(group, NULL, is_driver_bound)) {
>>>>> +		pr_err("Active drivers exist for devices in the group\n");
>>>>> +		return -EBUSY;
>>>>> +	}
>>>>> +
>>>>> +	mutex_lock(&group->mutex);
>>>>> +	prv_dom = group->default_domain;
>>>>> +	if (!prv_dom || !prv_dom->ops || !prv_dom->ops-
>>>>> dev_def_domain_type) {
>>>>> +		pr_err("'dev_def_domain_type' call back isn't registered\n");
>>>>> +		ret = -EPERM;
>>>>> +		goto out;
>>>>> +	}
>>>>> +
>>>>> +	/*
>>>>> +	 * Check if any user level driver (that doesn't use kernel driver like
>>>>> +	 * VFIO) is directly using the group. VFIO use case is detected by
>>>>> +	 * is_driver_bound() check above
>>>>> +	 */
>>>>> +	if (prv_dom != group->domain) {
>>>>> +		pr_err("Group assigned to user level for direct access\n");
>>>>> +		ret = -EBUSY;
>>>>> +		goto out;
>>>>> +	}
>>>>> +
>>>>> +	/*
>>>>> +	 * If user has requested "auto", kernel has to decide the default domain
>>>>> +	 * type of a group. Hence, find out individual preferences of a device.
>>>>> +	 */
>>>>> +	ops = prv_dom->ops;
>>>>> +	if (req_auto) {
>>>>> +		int dma_devs = 0, idt_devs = 0, dev_def_dom;
>>>>> +
>>>>> +		list_for_each_entry(grp_dev, &group->devices, list) {
>>>>> +			dev = grp_dev->dev;
>>>>> +			dev_def_dom = ops->dev_def_domain_type(dev);
>>>>> +			if (dev_def_dom == IOMMU_DOMAIN_IDENTITY)
>>>>> +				idt_devs++;
>>>>> +			if (dev_def_dom == IOMMU_DOMAIN_DMA)
>>>>> +				dma_devs++;
>>>>> +		}
>>>>> +
>>>>> +		/*
>>>>> +		 * Default domain type of a group is undefined if some devices
>>>>> +		 * in the group should be in dma domain while other devices
>>>>> +		 * should be in identity domain
>>>>> +		 */
>>>>> +		if (idt_devs && dma_devs) {
>>>>> +			pr_err("Some devices need identity domain while other
>>>> need dma domain\n");
>>>>> +			ret = -EPERM;
>>>>> +			goto out;
>>>>> +		}
>>>>> +
>>>>> +		/*
>>>>> +		 * Default domain type of a group is identity if
>>>>> +		 * 1. All the devices in the group need to be in identity domain
>>>>> +		 * 2. Some devices should be in identity domain while other
>>>>> +		 *    devices could be in either of dma or identity domain
>>>>> +		 */
>>>>> +		if (idt_devs && !dma_devs)
>>>>> +			req_type = IOMMU_DOMAIN_IDENTITY;
>>>>> +
>>>>> +		/*
>>>>> +		 * Default domain type of a group is dma if
>>>>> +		 * 1. All the devices in the group need to be in dma domain
>>>>> +		 * 2. Some devices should be in dma domain while other devices
>>>>> +		 *    could be in either of dma or identity domain
>>>>> +		 * 3. All the devices could be in either of the domains (namely
>>>>> +		 *    dma and identity)
>>>>> +		 */
>>>>> +		if (!idt_devs)
>>>>> +			req_type = IOMMU_DOMAIN_DMA;
>>>> Actually, There are four combinations:
>>>>
>>>> 			idt_devs==0		idt_devs!=0
>>>> dma_devs == 0		iommu_def_domain_type	identity
>>>> dma_devs != 0		DMA			unsupported
>>> Yes.. you are right. All these four combinations are presently handled in the
>> code.
>>
>> At least I didn't see the iommu_def_domain_type is used if both idt_devs and
>> dma_devs are both equal to 0. :-)
> 
> Sorry! I didn't get what you meant by "iommu_def_domain_type", so, could you please explain that?
> 
> When idt_devs == 0 and dma_devs == 0, it means that all the devices in the group could be in either of the domain. Hence, I default to DMA domain which is covered by this code.
> 		if (!idt_devs)
> 			req_type = IOMMU_DOMAIN_DMA;

iommu_def_domain_type is the system level default domain type defined in
iommu.c. If the vendor iommu driver has no special requirement, we
should choose the default value.

Best regards,
baolu
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

  reply	other threads:[~2020-02-24  7:39 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-16 21:57 [PATCH V2 0/5] iommu: Add support to change default domain of a group Sai Praneeth Prakhya
2020-02-16 21:57 ` [PATCH V2 1/5] iommu: Add dev_def_domain_type() call back function to iommu_ops Sai Praneeth Prakhya
2020-02-22 23:37   ` Lu Baolu
2020-02-22 23:39     ` Prakhya, Sai Praneeth
2020-02-16 21:57 ` [PATCH V2 2/5] iommu/vt-d: Rename device_def_domain_type() to intel_iommu_dev_def_domain_type() Sai Praneeth Prakhya
2020-02-22 23:42   ` Lu Baolu
2020-02-22 23:59     ` Prakhya, Sai Praneeth
2020-02-23  1:50       ` Lu Baolu
2020-02-24  3:23         ` Prakhya, Sai Praneeth
2020-02-16 21:57 ` [PATCH V2 3/5] iommu: Add support to change default domain of an iommu_group Sai Praneeth Prakhya
2020-02-23  1:20   ` Lu Baolu
2020-02-24  3:20     ` Prakhya, Sai Praneeth
2020-02-24  5:46       ` Lu Baolu
2020-02-24  7:03         ` Prakhya, Sai Praneeth
2020-02-24  7:39           ` Lu Baolu [this message]
2020-02-24  7:57             ` Prakhya, Sai Praneeth
2020-02-24  8:12               ` Lu Baolu
2020-02-24  8:39                 ` Lu Baolu
2020-02-24  8:44                   ` Prakhya, Sai Praneeth
2020-03-02 15:08   ` Joerg Roedel
2020-03-03  6:47     ` Lu Baolu
2020-03-03 13:13       ` Joerg Roedel
2020-03-04 12:17         ` Lu Baolu
2020-02-16 21:57 ` [PATCH V2 4/5] iommu: Take lock before reading iommu_group default domain type Sai Praneeth Prakhya
2020-02-16 21:57 ` [PATCH V2 5/5] iommu: Document usage of "/sys/kernel/iommu_groups/<grp_id>/type" file Sai Praneeth Prakhya
2020-02-23  1:38   ` Lu Baolu
2020-02-24  2:18     ` Prakhya, Sai Praneeth
2020-02-22 23:40 ` [PATCH V2 0/5] iommu: Add support to change default domain of a group Prakhya, Sai Praneeth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d5138046-b613-b070-2af8-4a9a519ca42a@linux.intel.com \
    --to=baolu.lu@linux.intel.com \
    --cc=ashok.raj@intel.com \
    --cc=hch@lst.de \
    --cc=iommu@lists.linux-foundation.org \
    --cc=robin.murphy@arm.com \
    --cc=sai.praneeth.prakhya@intel.com \
    --cc=will.deacon@arm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).