There are some improvements that could be made to make the boundary between HeapRegionRemSet and G1CardSet more clear:
HeapRegionRemSet deals with addresses, heap regions
G1CardSet deals with card indexes in the heap, card regions and cards within card regions
The G1CardSet::add_card() and G1CardSet::contains_card() methods are fuzzy about that, they require the caller (HeapRegionRemSet) to know about card regions and cards within regions.
It is better to let these two methods take card indexes and the splitting and such is done by G1CardSet.
Also look at the iteration API to see whether improvements in that regard could be made.
If split_card() is moved to G1CardSet, the assert at the end of the method can be tightened.