Browse Source

iommu/arm-smmu: Correct group reference count

The basic flow for add a device:
 arm_smmu_add_device
        |->iommu_group_get_for_dev
            |->iommu_group_get
                     return group;  (1)
            |->ops->device_group : Init/increase reference count to/by 1.
            |->iommu_group_add_device : Increase reference count by 1.
		     return group   (2)
        |->return 0;

Since we are adding one device, the flow is (2) and the group reference
count will be increased by 2. So, we need to add iommu_group_put at the
end of arm_smmu_add_device to decrease the count by 1.

Also take the failure path into consideration when fail to add a device.

Signed-off-by: Peng Fan <van.freenix@gmail.com>
Cc: Will Deacon <will.deacon@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Peng Fan 10 years ago
parent
commit
9a4a9d8c34
2 changed files with 12 additions and 7 deletions
  1. 11 7
      drivers/iommu/arm-smmu-v3.c
  2. 1 0
      drivers/iommu/arm-smmu.c

+ 11 - 7
drivers/iommu/arm-smmu-v3.c

@@ -1809,13 +1809,13 @@ static int arm_smmu_add_device(struct device *dev)
 		smmu = arm_smmu_get_for_pci_dev(pdev);
 		smmu = arm_smmu_get_for_pci_dev(pdev);
 		if (!smmu) {
 		if (!smmu) {
 			ret = -ENOENT;
 			ret = -ENOENT;
-			goto out_put_group;
+			goto out_remove_dev;
 		}
 		}
 
 
 		smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL);
 		smmu_group = kzalloc(sizeof(*smmu_group), GFP_KERNEL);
 		if (!smmu_group) {
 		if (!smmu_group) {
 			ret = -ENOMEM;
 			ret = -ENOMEM;
-			goto out_put_group;
+			goto out_remove_dev;
 		}
 		}
 
 
 		smmu_group->ste.valid	= true;
 		smmu_group->ste.valid	= true;
@@ -1831,20 +1831,20 @@ static int arm_smmu_add_device(struct device *dev)
 	for (i = 0; i < smmu_group->num_sids; ++i) {
 	for (i = 0; i < smmu_group->num_sids; ++i) {
 		/* If we already know about this SID, then we're done */
 		/* If we already know about this SID, then we're done */
 		if (smmu_group->sids[i] == sid)
 		if (smmu_group->sids[i] == sid)
-			return 0;
+			goto out_put_group;
 	}
 	}
 
 
 	/* Check the SID is in range of the SMMU and our stream table */
 	/* Check the SID is in range of the SMMU and our stream table */
 	if (!arm_smmu_sid_in_range(smmu, sid)) {
 	if (!arm_smmu_sid_in_range(smmu, sid)) {
 		ret = -ERANGE;
 		ret = -ERANGE;
-		goto out_put_group;
+		goto out_remove_dev;
 	}
 	}
 
 
 	/* Ensure l2 strtab is initialised */
 	/* Ensure l2 strtab is initialised */
 	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
 	if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {
 		ret = arm_smmu_init_l2_strtab(smmu, sid);
 		ret = arm_smmu_init_l2_strtab(smmu, sid);
 		if (ret)
 		if (ret)
-			goto out_put_group;
+			goto out_remove_dev;
 	}
 	}
 
 
 	/* Resize the SID array for the group */
 	/* Resize the SID array for the group */
@@ -1854,15 +1854,19 @@ static int arm_smmu_add_device(struct device *dev)
 	if (!sids) {
 	if (!sids) {
 		smmu_group->num_sids--;
 		smmu_group->num_sids--;
 		ret = -ENOMEM;
 		ret = -ENOMEM;
-		goto out_put_group;
+		goto out_remove_dev;
 	}
 	}
 
 
 	/* Add the new SID */
 	/* Add the new SID */
 	sids[smmu_group->num_sids - 1] = sid;
 	sids[smmu_group->num_sids - 1] = sid;
 	smmu_group->sids = sids;
 	smmu_group->sids = sids;
-	return 0;
 
 
 out_put_group:
 out_put_group:
+	iommu_group_put(group);
+	return 0;
+
+out_remove_dev:
+	iommu_group_remove_device(dev);
 	iommu_group_put(group);
 	iommu_group_put(group);
 	return ret;
 	return ret;
 }
 }

+ 1 - 0
drivers/iommu/arm-smmu.c

@@ -1355,6 +1355,7 @@ static int arm_smmu_add_device(struct device *dev)
 	if (IS_ERR(group))
 	if (IS_ERR(group))
 		return PTR_ERR(group);
 		return PTR_ERR(group);
 
 
+	iommu_group_put(group);
 	return 0;
 	return 0;
 }
 }