Skip to content

Commit

Permalink
i2c: Prevent priority inversion on top of bus lock
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Mika Kuoppala authored and Jean Delvare committed Dec 6, 2009
1 parent a0c11cd commit 194684e
Show file tree
Hide file tree
Showing 3 changed files with 10 additions and 10 deletions.
1 change: 1 addition & 0 deletions drivers/i2c/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
menuconfig I2C
tristate "I2C support"
depends on HAS_IOMEM
select RT_MUTEXES
---help---
I2C (pronounce: I-square-C) is a slow serial bus protocol used in
many micro controller applications and developed by Philips. SMBus,
Expand Down
12 changes: 6 additions & 6 deletions drivers/i2c/i2c-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
goto out_list;
}

mutex_init(&adap->bus_lock);
rt_mutex_init(&adap->bus_lock);

/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
Expand Down Expand Up @@ -1092,12 +1092,12 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
#endif

if (in_atomic() || irqs_disabled()) {
ret = mutex_trylock(&adap->bus_lock);
ret = rt_mutex_trylock(&adap->bus_lock);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
mutex_lock_nested(&adap->bus_lock, adap->level);
rt_mutex_lock(&adap->bus_lock);
}

/* Retry automatically on arbitration loss */
Expand All @@ -1109,7 +1109,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
mutex_unlock(&adap->bus_lock);
rt_mutex_unlock(&adap->bus_lock);

return ret;
} else {
Expand Down Expand Up @@ -1913,7 +1913,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
flags &= I2C_M_TEN | I2C_CLIENT_PEC;

if (adapter->algo->smbus_xfer) {
mutex_lock(&adapter->bus_lock);
rt_mutex_lock(&adapter->bus_lock);

/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
Expand All @@ -1927,7 +1927,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
orig_jiffies + adapter->timeout))
break;
}
mutex_unlock(&adapter->bus_lock);
rt_mutex_unlock(&adapter->bus_lock);
} else
res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
command, protocol, data);
Expand Down
7 changes: 3 additions & 4 deletions include/linux/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,7 @@ struct i2c_adapter {
void *algo_data;

/* data fields that are valid for all devices */
u8 level; /* nesting level for lockdep */
struct mutex bus_lock;
struct rt_mutex bus_lock;

int timeout; /* in jiffies */
int retries;
Expand Down Expand Up @@ -367,7 +366,7 @@ static inline void i2c_set_adapdata(struct i2c_adapter *dev, void *data)
*/
static inline void i2c_lock_adapter(struct i2c_adapter *adapter)
{
mutex_lock(&adapter->bus_lock);
rt_mutex_lock(&adapter->bus_lock);
}

/**
Expand All @@ -376,7 +375,7 @@ static inline void i2c_lock_adapter(struct i2c_adapter *adapter)
*/
static inline void i2c_unlock_adapter(struct i2c_adapter *adapter)
{
mutex_unlock(&adapter->bus_lock);
rt_mutex_unlock(&adapter->bus_lock);
}

/*flags for the client struct: */
Expand Down

0 comments on commit 194684e

Please sign in to comment.