@@ -331,6 +331,14 @@ def _verify_quiesce_wrapped(self, rank, status, root, root_inode, ops, cache, sp
331331 # check request/cap count is stopped
332332 # count inodes under /usr and count subops!
333333
334+ def quiesce_and_verify (self , path , timeout = 120 ):
335+ J = self .fs .rank_tell ("quiesce" , "path" , path )
336+ log .debug (f"{ J } " )
337+ reqid = self ._reqid_tostr (J ['op' ]['reqid' ])
338+ self ._wait_for_quiesce_complete (reqid , timeout = timeout )
339+ self ._verify_quiesce (root = path )
340+ return reqid
341+
334342class TestQuiesce (QuiesceTestCase ):
335343 """
336344 Single rank functional tests.
@@ -598,21 +606,39 @@ def test_quiesce_dir_fragment(self):
598606 That quiesce completes with fragmentation in the background.
599607 """
600608
601- self .config_set ('mds' , 'mds_bal_split_size' , '10' )
602- self .config_set ('mds' , 'mds_bal_merge_size' , '1' ) # do not merge
603- self .config_set ('mds' , 'mds_bal_split_bits' , '1' )
604- self ._configure_subvolume ()
605- self ._client_background_workload ()
606-
607- # time for the workload to get busy
608- time .sleep (5 )
609+ # the config should cause continuous merge-split wars
610+ self .config_set ('mds' , 'mds_bal_split_size' , '1' ) # split anything larger than one item ....
611+ self .config_set ('mds' , 'mds_bal_merge_size' , '2' ) # and then merge if only one item ]:-}
612+ self .config_set ('mds' , 'mds_bal_split_bits' , '2' )
609613
610- J = self .fs .rank_tell ("quiesce" , "path" , self .subvolume )
611- log .debug (f"{ J } " )
612- reqid = self ._reqid_tostr (J ['op' ]['reqid' ])
613- self ._wait_for_quiesce_complete (reqid )
614- self ._verify_quiesce (root = self .subvolume )
614+ self ._configure_subvolume ()
615615
616+ self .mount_a .run_shell_payload ("mkdir -p root/sub1" )
617+ self .mount_a .write_file ("root/sub1/file1" , "I'm file 1" )
618+ self .mount_a .run_shell_payload ("mkdir -p root/sub2" )
619+ self .mount_a .write_file ("root/sub2/file2" , "I'm file 2" )
620+
621+ sleep_for = 30
622+ log .info (f"Sleeping { sleep_for } seconds to warm up the balancer" )
623+ time .sleep (sleep_for )
624+
625+ for _ in range (30 ):
626+ sub1 = f"{ self .subvolume } /root/sub1"
627+ log .debug (f"Quiescing { sub1 } " )
628+ # with one of the subdirs quiesced, the freezing
629+ # of the parent dir (root) can't complete
630+ op1 = self .quiesce_and_verify (sub1 , timeout = 15 )
631+
632+ sub2 = f"{ self .subvolume } /root/sub2"
633+ log .debug (f"{ sub1 } quiesced: { op1 } . Quiescing { sub2 } " )
634+ # despite the parent dir freezing, we should be able
635+ # to quiesce the other subvolume
636+ op2 = self .quiesce_and_verify (sub2 , timeout = 15 )
637+
638+ log .debug (f"{ sub2 } quiesced: { op2 } . Killing the ops." )
639+ self .fs .kill_op (op1 )
640+ self .fs .kill_op (op2 )
641+ time .sleep (5 )
616642
617643class TestQuiesceMultiRank (QuiesceTestCase ):
618644 """
@@ -661,6 +687,50 @@ def test_quiesce_path_splitauth(self):
661687 op = self .fs .rank_tell (["quiesce" , "path" , self .subvolume , '--wait' ], rank = 0 )['op' ]
662688 self .assertEqual (op ['result' ], - 1 ) # EPERM
663689
690+ @unittest .skip ("https://tracker.ceph.com/issues/66152" )
691+ def test_quiesce_drops_remote_authpins_on_failure (self ):
692+ """
693+ That remote authpins are dropped when the request fails to acquire the quiesce lock
694+
695+ When the remote authpin is freezing, not dropping it is likely to deadlock a distributed quiesce
696+ """
697+ self ._configure_subvolume ()
698+
699+ # create two dirs for pinning
700+ self .mount_a .run_shell_payload ("mkdir -p pin0 pin1" )
701+ # enable export by populating the directories
702+ self .mount_a .run_shell_payload ("touch pin0/export_dummy pin1/export_dummy" )
703+ # pin the files to different ranks
704+ self .mount_a .setfattr ("pin0" , "ceph.dir.pin" , "0" )
705+ self .mount_a .setfattr ("pin1" , "ceph.dir.pin" , "1" )
706+
707+ # prepare the patient at rank 0
708+ self .mount_a .write_file ("pin0/thefile" , "I'm ready, doc" )
709+
710+ # wait for the export to settle
711+ self ._wait_subtrees ([(f"{ self .mntpnt } /pin0" , 0 ), (f"{ self .mntpnt } /pin1" , 1 )])
712+
713+ # Take the quiesce lock on the replica of the src file.
714+ # This is needed to cause the next command to block
715+ cmd = self .fs .run_ceph_cmd (f"tell mds.{ self .fs .name } :1 lock path { self .mntpnt } /pin0/thefile quiesce:x --await" )
716+ self .assertEqual (cmd .exitstatus , 0 )
717+
718+ # Simulate a rename by remote-auth-pin-freezing the file.
719+ # Also try to take the quiesce lock to cause the MDR
720+ # to block on quiesce with the remote authpin granted
721+ # Don't --await this time because we expect this to block
722+ cmd = self .fs .run_ceph_cmd (f"tell mds.{ self .fs .name } :1 lock path { self .mntpnt } /pin0/thefile quiesce:w --ap-freeze" )
723+ self .assertEqual (cmd .exitstatus , 0 )
724+
725+ # At this point, if everything works well, we should be able to
726+ # autpin and quiesce the file on the auth side.
727+ # If the op above fails to release remote authpins, then the inode
728+ # will still be authpin frozen, and that will disallow auth pinning the file
729+ # We are using a combination of --ap-dont-block and --await
730+ # to detect whether the file is authpinnable
731+ cmd = self .fs .run_ceph_cmd (f"tell mds.{ self .fs .name } :0 lock path { self .mntpnt } /pin0/thefile quiesce:x --ap-dont-block --await" )
732+ self .assertEqual (cmd .exitstatus , 0 )
733+
664734 def test_quiesce_authpin_wait (self ):
665735 """
666736 That a quiesce_inode op with outstanding remote authpin requests can be killed.
@@ -804,7 +874,7 @@ def test_quiesce_block_replicated(self):
804874 op = self .fs .rank_tell ("lock" , "path" , self .mntpnt + "/dir1/dir2" , "policy:r" , rank = 1 )
805875 p = self .mount_a .setfattr ("dir1/dir2" , "ceph.quiesce.block" , "1" , wait = False )
806876 sleep (2 ) # for req to block waiting for xlock on policylock
807- reqid = self ._reqid_tostr (op ['op' ][ ' reqid' ])
877+ reqid = self ._reqid_tostr (op ['reqid' ])
808878 self .fs .kill_op (reqid , rank = 1 )
809879 p .wait ()
810880
0 commit comments