Thanks. So there is no "specialized data structure" for tracking dirty pages, the page dirty status is tracked within the regular page cache hierarchy. And the obvious in-line solution (use a wraparound counter for tracking dirty state, increase dirty values for new pages while writing out lower values) isn't easy because the tag is a bitfield. So the easiest solution would be to have two separate dirty markers (e.g. dirty_a and dirty_b), and have the writeout process toggle the new-dirty marker from A to B so it can safely write dirty_a pages without interference. And vice-versa for the next writeout.
There is just one caveat with regards to the previous solution: pages that are already pending writeout still need to be locked. In the previous design, a write to a page already pending writeout could be duplicated into the new dirty queue. But since it seems that a page can exist only once in the page cache, it cannot be written to without possibly violating write barriers. So the write must block until the writeout process has un-dirtied the page.
Armchair programming is fun, isn't it? :)