Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: Prevent priority inversion on top of bus lock

Low priority thread holding the i2c bus mutex could block higher
priority threads to access the bus resulting in unacceptable
latencies. Change the mutex type to rt_mutex preventing priority
inversion.

Tested-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@nokia.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>

authored by

Mika Kuoppala and committed by
Jean Delvare
194684e5 a0c11cdd

+10 -10
+1
drivers/i2c/Kconfig
··· 5 5 menuconfig I2C 6 6 tristate "I2C support" 7 7 depends on HAS_IOMEM 8 + select RT_MUTEXES 8 9 ---help--- 9 10 I2C (pronounce: I-square-C) is a slow serial bus protocol used in 10 11 many micro controller applications and developed by Philips. SMBus,
+6 -6
drivers/i2c/i2c-core.c
··· 584 584 goto out_list; 585 585 } 586 586 587 - mutex_init(&adap->bus_lock); 587 + rt_mutex_init(&adap->bus_lock); 588 588 589 589 /* Set default timeout to 1 second if not already set */ 590 590 if (adap->timeout == 0) ··· 1092 1092 #endif 1093 1093 1094 1094 if (in_atomic() || irqs_disabled()) { 1095 - ret = mutex_trylock(&adap->bus_lock); 1095 + ret = rt_mutex_trylock(&adap->bus_lock); 1096 1096 if (!ret) 1097 1097 /* I2C activity is ongoing. */ 1098 1098 return -EAGAIN; 1099 1099 } else { 1100 - mutex_lock_nested(&adap->bus_lock, adap->level); 1100 + rt_mutex_lock(&adap->bus_lock); 1101 1101 } 1102 1102 1103 1103 /* Retry automatically on arbitration loss */ ··· 1109 1109 if (time_after(jiffies, orig_jiffies + adap->timeout)) 1110 1110 break; 1111 1111 } 1112 - mutex_unlock(&adap->bus_lock); 1112 + rt_mutex_unlock(&adap->bus_lock); 1113 1113 1114 1114 return ret; 1115 1115 } else { ··· 1913 1913 flags &= I2C_M_TEN | I2C_CLIENT_PEC; 1914 1914 1915 1915 if (adapter->algo->smbus_xfer) { 1916 - mutex_lock(&adapter->bus_lock); 1916 + rt_mutex_lock(&adapter->bus_lock); 1917 1917 1918 1918 /* Retry automatically on arbitration loss */ 1919 1919 orig_jiffies = jiffies; ··· 1927 1927 orig_jiffies + adapter->timeout)) 1928 1928 break; 1929 1929 } 1930 - mutex_unlock(&adapter->bus_lock); 1930 + rt_mutex_unlock(&adapter->bus_lock); 1931 1931 } else 1932 1932 res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, 1933 1933 command, protocol, data);
+3 -4
include/linux/i2c.h
··· 338 338 void *algo_data; 339 339 340 340 /* data fields that are valid for all devices */ 341 - u8 level; /* nesting level for lockdep */ 342 - struct mutex bus_lock; 341 + struct rt_mutex bus_lock; 343 342 344 343 int timeout; /* in jiffies */ 345 344 int retries; ··· 366 367 */ 367 368 static inline void i2c_lock_adapter(struct i2c_adapter *adapter) 368 369 { 369 - mutex_lock(&adapter->bus_lock); 370 + rt_mutex_lock(&adapter->bus_lock); 370 371 } 371 372 372 373 /** ··· 375 376 */ 376 377 static inline void i2c_unlock_adapter(struct i2c_adapter *adapter) 377 378 { 378 - mutex_unlock(&adapter->bus_lock); 379 + rt_mutex_unlock(&adapter->bus_lock); 379 380 } 380 381 381 382 /*flags for the client struct: */