The Dangers of Read-Modify-Write

The Dangers of Read-Modify-Write

November 30, 2009

In registers containing several read/write configuration bits, a device driver often needs to change one bit without changing the others. One technique is for the device driver to keep a shadow copy of that register in its own data structures. The device driver modifies the desired bit in its copy, and then writes out the modified value. Another technique is for the device driver to first read the register, modify the desired bit, and then write out the modified value.

If the register is only used by the one device driver, then either the shadow copy or the read-modify-write method can be used safely. However, for registers potentially used by more than one device driver, such as global interrupt enable and GPIO registers, each device driver should use the read-modify-write method. It is difficult and cumbersome for each device driver to keep its own shadow copy when they must stay in sync when any one device driver makes a change. A hybrid approach where each device driver performs read-modify-write on a global shadow copy is possible but, depending on the OS and the architecture, the different device drivers might not be able to access a global shadow copy. Better yet is to have each device driver read the register to ensure they are working with a current and valid copy of the register. They can then modify it and write it back out.

However, having multiple device drivers accessing the same register (or global shadow copy) for read-modify-write operations can be dangerous. If the device drivers are not written carefully, one device driver could overwrite the changes of another device driver. The following gives an example:

  • Driver A reads the current value of the register and modifies its local copy of the register value.
  • Before Driver A can write the modified value back to the register, it is interrupted by Driver B.
  • Driver B reads the current value of the same register, modifies its local copy of the value, and writes the modified value to the register.
  • Driver B finishes its current job allowing Driver A to resume.
  • Driver A continues, writing its modified value (which does not include Driver B’s changes) to the register, wiping out the changes that Driver B made.

Race conditions like this can be very tricky to detect and debug. The problem is that the read-modify-write operation is not an atomic operation – it cannot be completed in one, uninterruptible step. Techniques such as disabling interrupts and using mutexes can reduce the likelihood of this occurring, but only if each and every device driver in all of their respective accesses use the same marshalling technique. If even one device driver doesn’t, the system is exposed to race conditions.

Because read-modify-write race conditions are very difficult to diagnose, hardware engineers should minimize the number of global registers shared by multiple device drivers.

  • Best Practice: Avoid read/write registers that more than one device driver will access.

In next month’s issue, I will discuss atomic access registers that eliminate this problem.

Until the next dangerous newsletter…

Share:

Comments