This prevents an in-kernel division by zero which happens when we are asking for i915_chipset_val too quickly, or within a race condition between the power monitoring thread and userspace accesses via debugfs. The issue can be reproduced easily via the following command: while ``; do cat /sys/kernel/debug/dri/0/i915_emon_status; done This is particularly dangerous because it can be triggered by a non-privileged user by just reading the debugfs entry. This issue was also found independently by Konstantin Belousov <kostikbel@gmail.com>, who proposed a similar patch.
Unified · Split
| @@ -1454,6 +1454,14 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) · 1453 unmodified lines | ||
| 1454 | 1454 | |
| 1455 | 1455 | diff1 = now - dev_priv->last_time1; |
| 1456 | 1456 | |
| 1457 | + /* Prevent division-by-zero if we are asking too fast. | |
| 1458 | + * Also, we don't get interesting results if we are polling | |
| 1459 | + * faster than once in 10ms, so just return the saved value | |
| 1460 | + * in such cases. | |
| 1461 | + */ | |
| 1462 | + if (diff1 <= 10) | |
| 1463 | + return dev_priv->chipset_power; | |
| 1464 | + | |
| 1457 | 1465 | count1 = I915_READ(DMIEC); |
| 1458 | 1466 | count2 = I915_READ(DDREC); |
| 1459 | 1467 | count3 = I915_READ(CSIEC); |
| @@ -1484,6 +1492,8 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) · 24 unmodified lines | ||
| 1484 | 1492 | dev_priv->last_count1 = total_count; |
| 1485 | 1493 | dev_priv->last_time1 = now; |
| 1486 | 1494 | |
| 1495 | + dev_priv->chipset_power = ret; | |
| 1496 | + | |
| 1487 | 1497 | return ret; |
| 1488 | 1498 | } |
| 1489 | 1499 | |
| @@ -713,6 +713,7 @@ typedef struct drm_i915_private { · 712 unmodified lines | ||
| 713 | 713 | |
| 714 | 714 | u64 last_count1; |
| 715 | 715 | unsigned long last_time1; |
| 716 | + unsigned long chipset_power; | |
| 716 | 717 | u64 last_count2; |
| 717 | 718 | struct timespec last_time2; |
| 718 | 719 | unsigned long gfx_power; |