Day21 - hwmon Driver Cache Mechanism¶
๐ฏ Objective¶
In Day 20, the driver successfully integrated with the hwmon subsystem.
However, each read operation still triggered an I2C transaction, which is inefficient.
In this lab, we introduce a cache mechanism to:
- Reduce unnecessary I2C transactions
- Improve performance
- Implement a time-based update policy
๐ง Design Concept¶
Problem¶
Every read causes hardware access.
Solution: Add Cache Layer¶
๐งฉ Key Design¶
Cache Data Structure¶
struct myi2c_sensor_data {
struct i2c_client *client;
struct mutex lock;
bool initialized;
long temp_mc_cached;
unsigned long last_update;
unsigned int cache_interval_ms;
};
Cache Policy¶
Update Flow¶
๐ง Implementation¶
1. Cache Update Helper¶
static int myi2c_sensor_update_if_needed(struct myi2c_sensor_data *data)
{
int ret;
s16 raw;
unsigned long interval_jiffies;
if (!data->initialized)
goto do_update;
interval_jiffies = msecs_to_jiffies(data->cache_interval_ms);
if (time_before(jiffies, data->last_update + interval_jiffies))
return 0;
do_update:
ret = myi2c_sensor_update_once(data);
if (ret < 0)
return ret;
ret = myi2c_sensor_read_temp_raw(data, &raw);
if (ret < 0)
return ret;
data->temp_mc_cached = raw * 10;
data->last_update = jiffies;
data->initialized = true;
return 0;
}
2. Modify Read Path¶
static int myi2c_sensor_read_temp_mc(struct myi2c_sensor_data *data, long *temp_mc)
{
int ret;
mutex_lock(&data->lock);
ret = myi2c_sensor_update_if_needed(data);
if (ret < 0)
goto out;
*temp_mc = data->temp_mc_cached;
out:
mutex_unlock(&data->lock);
return ret;
}
โ๏ธ sysfs Control (Cache Interval)¶
Show¶
static ssize_t cache_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct myi2c_sensor_data *data = dev_get_drvdata(dev);
unsigned int interval;
mutex_lock(&data->lock);
interval = data->cache_interval_ms;
mutex_unlock(&data->lock);
return sysfs_emit(buf, "%u\n", interval);
}
Store¶
static ssize_t cache_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct myi2c_sensor_data *data = dev_get_drvdata(dev);
unsigned long val;
if (kstrtoul(buf, 0, &val))
return -EINVAL;
mutex_lock(&data->lock);
data->cache_interval_ms = (unsigned int)val;
mutex_unlock(&data->lock);
return count;
}
Attribute Registration¶
static DEVICE_ATTR_RW(cache_interval);
static struct attribute *myi2c_sensor_attrs[] = {
...
&dev_attr_cache_interval.attr,
NULL,
};
๐งช Testing¶
Test Script¶
Expected Behavior¶
Case 1: cache_interval = 0¶
Case 2: cache_interval = 200 ms¶
Case 3: cache_interval = 1000 ms¶
๐ง Key Learning Points¶
1. Driver Policy vs Mechanism¶
- Mechanism โ I2C read
- Policy โ when to read
2. Cache Responsibility¶
The hwmon subsystem does NOT handle caching.
๐ Driver must implement it.
3. Performance Trade-off¶
| Mode | Behavior |
|---|---|
| No cache | Accurate but slow |
| Cache | Efficient but slightly stale |
4. Thread Safety¶
- Cache access protected by mutex
- Prevent race conditions
๐ Result¶
The driver is now:
- hwmon-compliant
- performance-optimized
- policy-driven
- closer to production-level design
๐ Next Step¶
- Background polling (workqueue / timer)
- Interrupt-driven sensor
- Multi-channel support
- Integration with lm-sensors