| 2657 | } |
| 2658 | |
| 2659 | CtPtr ProtocolV2::handle_existing_connection(const AsyncConnectionRef& existing) { |
| 2660 | ldout(cct, 20) << __func__ << " existing=" << existing << dendl; |
| 2661 | |
| 2662 | std::unique_lock<std::mutex> l(existing->lock); |
| 2663 | |
| 2664 | ProtocolV2 *exproto = dynamic_cast<ProtocolV2 *>(existing->protocol.get()); |
| 2665 | if (!exproto) { |
| 2666 | ldout(cct, 1) << __func__ << " existing=" << existing << dendl; |
| 2667 | ceph_assert(false); |
| 2668 | } |
| 2669 | |
| 2670 | if (exproto->state == CLOSED) { |
| 2671 | ldout(cct, 1) << __func__ << " existing " << existing << " already closed." |
| 2672 | << dendl; |
| 2673 | l.unlock(); |
| 2674 | return send_server_ident(); |
| 2675 | } |
| 2676 | |
| 2677 | if (exproto->replacing) { |
| 2678 | ldout(cct, 1) << __func__ |
| 2679 | << " existing racing replace happened while replacing." |
| 2680 | << " existing=" << existing << dendl; |
| 2681 | auto wait = WaitFrame::Encode(); |
| 2682 | return WRITE(wait, "wait", read_frame); |
| 2683 | } |
| 2684 | // Detect a peer (client) restart and prefer the new connection. |
| 2685 | // Rationale: |
| 2686 | // - The client cookie is regenerated in send_client_ident() on client startup. |
| 2687 | // - If the existing connection (exproto) has a strictly higher peer_global_seq |
| 2688 | // than the incoming one, the peer’s sequence appears to have reset on the new |
| 2689 | // connection (exproto->peer_global_seq > peer_global_seq). |
| 2690 | // - Both sides present non-zero cookies and they differ, which strongly indicates |
| 2691 | // a new client instance (exproto->client_cookie != client_cookie). |
| 2692 | // - We only do this when the existing connection is not in a benign terminal/idle |
| 2693 | // state (READY or STANDBY); otherwise we might drop a healthy/idle connection. |
| 2694 | // Action: |
| 2695 | // Drop the existing connection in favor of the incoming one, then continue the |
| 2696 | // handshake on the new connection. |
| 2697 | if (exproto->peer_global_seq > peer_global_seq && |
| 2698 | exproto->client_cookie && client_cookie && |
| 2699 | exproto->client_cookie != client_cookie && |
| 2700 | exproto->state != READY && exproto->state != STANDBY) { |
| 2701 | ldout(cct, 1) << __func__ << " client has clearly restarted (ex_peer_global_seq=" |
| 2702 | << exproto->peer_global_seq << " > peer_global_seq=" << peer_global_seq |
| 2703 | << " && cookie changed: client_cookie=" << client_cookie << " != ex_client_cookie=" |
| 2704 | << exproto->client_cookie << "), " |
| 2705 | << "existing connection state is " << get_state_name(exproto->state) |
| 2706 | << " (not READY or STANDBY), dropping existing_connection=" << existing |
| 2707 | << " in favor of new_connection=" << connection << dendl; |
| 2708 | existing->protocol->stop(); |
| 2709 | existing->dispatch_queue->queue_reset(existing.get()); |
| 2710 | l.unlock(); |
| 2711 | return send_server_ident(); |
| 2712 | } |
| 2713 | |
| 2714 | if (exproto->peer_global_seq > peer_global_seq) { |
| 2715 | ldout(cct, 1) << __func__ << " this is a stale connection, ex_peer_global_seq=" |
| 2716 | << exproto->peer_global_seq |
nothing calls this directly
no test coverage detected