I think this works, but it makes me scared.  I'd go for something like:

bool wrlocked = false;
r = _recalc_op_target(...., wrlocked);
if (r == -EAGAIN) {
   // redo lock, check for race
  wrlocked = true;
   r = _recalc_op_target(...., wrlocked);
   assert(r == 0);
}
...

so that the relocking is done up in the caller, the caller can react in case some other state is possibly invalidated, and we aren't relying on the is_wlocked() introspection methods.  Then inside _recalc_op_target we take care to not modify if the bool isn't true, etc.

Actually, looking at it a bit more closely, I wonder if it makes sense to separate recalc_op_target into two pieces: once that does the crush mapping calculation, and another that does the session setup... make that part adjust_session or something like that.  That way we avoid redoing the crush mapping if we redo the locks and the epoch didn't changed.