ceph: queue cap_snaps once per realm

We were forming a dirty list, and then queueing cap_snaps for each realm
_and_ its children, regardless of whether the children were already in the
dirty list. This meant we did it twice for some realms. Which in turn
meant we corrupted mdsc->snap_flush_list when the cap_snap was re-added to
the list it was already on, and could trigger an infinite loop.

We were also using recursion to do reach all the children, a no-no when
stack is limited.

Instead, (re)queue any children on the dirty list, avoiding processing
anything twice and avoiding any recursion.

Signed-off-by: Sage Weil <sage@newdream.net>

Sage Weil e8e1ba96 42961d23

+10 -4
+10 -4
fs/ceph/snap.c
··· 584 if (lastinode) 585 iput(lastinode); 586 587 - dout("queue_realm_cap_snaps %p %llx children\n", realm, realm->ino); 588 - list_for_each_entry(child, &realm->children, child_item) 589 - queue_realm_cap_snaps(child); 590 591 dout("queue_realm_cap_snaps %p %llx done\n", realm, realm->ino); 592 } 593 ··· 687 * queue cap snaps _after_ we've built the new snap contexts, 688 * so that i_head_snapc can be set appropriately. 689 */ 690 - list_for_each_entry(realm, &dirty_realms, dirty_item) { 691 queue_realm_cap_snaps(realm); 692 } 693
··· 584 if (lastinode) 585 iput(lastinode); 586 587 + list_for_each_entry(child, &realm->children, child_item) { 588 + dout("queue_realm_cap_snaps %p %llx queue child %p %llx\n", 589 + realm, realm->ino, child, child->ino); 590 + list_del_init(&child->dirty_item); 591 + list_add(&child->dirty_item, &realm->dirty_item); 592 + } 593 594 + list_del_init(&realm->dirty_item); 595 dout("queue_realm_cap_snaps %p %llx done\n", realm, realm->ino); 596 } 597 ··· 683 * queue cap snaps _after_ we've built the new snap contexts, 684 * so that i_head_snapc can be set appropriately. 685 */ 686 + while (!list_empty(&dirty_realms)) { 687 + realm = list_first_entry(&dirty_realms, struct ceph_snap_realm, 688 + dirty_item); 689 queue_realm_cap_snaps(realm); 690 } 691