Browse Source

iommu/amd: fix accounting of device_state

This patch fixes a bug in the accounting of the device_state.
In the current code, the device_state was put (decremented) too many times,
which sometimes lead to the driver getting stuck permanently in
put_device_state_wait(). That happen because the device_state->count would go
below zero, which is never supposed to happen.

The root cause is that the device_state was decremented in put_pasid_state()
and put_pasid_state_wait() but also in all the functions that call those
functions. Therefore, the device_state was decremented twice in each of these
code paths.

The fix is to decouple the device_state accounting from the pasid_state
accounting - remove the call to put_device_state() from the
put_pasid_state() and the put_pasid_state_wait())

Signed-off-by: Oded Gabbay <oded.gabbay@amd.com>
Oded Gabbay 10 năm trước cách đây
mục cha
commit
a015c1e926
1 tập tin đã thay đổi với 2 bổ sung6 xóa
  1. 2 6
      drivers/iommu/amd_iommu_v2.c

+ 2 - 6
drivers/iommu/amd_iommu_v2.c

@@ -272,10 +272,8 @@ static void free_pasid_state(struct pasid_state *pasid_state)
 
 
 static void put_pasid_state(struct pasid_state *pasid_state)
 static void put_pasid_state(struct pasid_state *pasid_state)
 {
 {
-	if (atomic_dec_and_test(&pasid_state->count)) {
-		put_device_state(pasid_state->device_state);
+	if (atomic_dec_and_test(&pasid_state->count))
 		wake_up(&pasid_state->wq);
 		wake_up(&pasid_state->wq);
-	}
 }
 }
 
 
 static void put_pasid_state_wait(struct pasid_state *pasid_state)
 static void put_pasid_state_wait(struct pasid_state *pasid_state)
@@ -284,9 +282,7 @@ static void put_pasid_state_wait(struct pasid_state *pasid_state)
 
 
 	prepare_to_wait(&pasid_state->wq, &wait, TASK_UNINTERRUPTIBLE);
 	prepare_to_wait(&pasid_state->wq, &wait, TASK_UNINTERRUPTIBLE);
 
 
-	if (atomic_dec_and_test(&pasid_state->count))
-		put_device_state(pasid_state->device_state);
-	else
+	if (!atomic_dec_and_test(&pasid_state->count))
 		schedule();
 		schedule();
 
 
 	finish_wait(&pasid_state->wq, &wait);
 	finish_wait(&pasid_state->wq, &wait);