+ conn->ksnc_tx_mono = NULL;
+ atomic_set (&conn->ksnc_tx_nob, 0);
+
+ LIBCFS_ALLOC(hello, offsetof(ksock_hello_msg_t,
+ kshm_ips[LNET_MAX_INTERFACES]));
+ if (hello == NULL) {
+ rc = -ENOMEM;
+ goto failed_1;
+ }
+
+ /* stash conn's local and remote addrs */
+ rc = ksocknal_lib_get_conn_addrs (conn);
+ if (rc != 0)
+ goto failed_1;
+
+ /* Find out/confirm peer's NID and connection type and get the
+ * vector of interfaces she's willing to let me connect to.
+ * Passive connections use the listener timeout since the peer sends
+ * eagerly */
+
+ if (active) {
+ LASSERT(ni == route->ksnr_peer->ksnp_ni);
+
+ /* Active connection sends HELLO eagerly */
+ hello->kshm_nips = ksocknal_local_ipvec(ni, hello->kshm_ips);
+ peerid = route->ksnr_peer->ksnp_id;
+ conn->ksnc_proto = route->ksnr_proto;
+
+ rc = ksocknal_send_hello (ni, conn, peerid.nid, hello);
+ if (rc != 0)
+ goto failed_1;
+ } else {
+ peerid.nid = LNET_NID_ANY;
+ peerid.pid = LNET_PID_ANY;
+
+ /* Passive, get protocol from peer */
+ conn->ksnc_proto = NULL;
+ }
+
+ rc = ksocknal_recv_hello (ni, conn, hello, &peerid, &incarnation);
+ if (rc < 0) {
+ if (rc == -EALREADY) {
+ /* only active connection loses conn race */
+ LASSERT (active);
+
+ CDEBUG(D_NET, "Lost connection race with %s\n",
+ libcfs_id2str(peerid));
+ /* Not an actual failure: return +ve RC so active
+ * connector can back off */
+ rc = EALREADY;
+ }
+ goto failed_1;
+ }
+
+ if (active && route->ksnr_proto != conn->ksnc_proto) {
+ /* Active connecting, and different protocol is returned */
+ CDEBUG(D_NET, "Connecting by %d.x protocol is rejected,"
+ " compatible version %d.x found.\n",
+ route->ksnr_proto->pro_version,
+ conn->ksnc_proto->pro_version);
+ /* Not an actual failure: return +ve RC so active
+ * connector can back off */
+ rc = EPROTO;
+
+ /* Retry with peer's protocol later */
+ route->ksnr_proto = conn->ksnc_proto;
+
+ goto failed_1;
+ }
+
+ LASSERT (peerid.nid != LNET_NID_ANY);
+
+ if (active) {
+ peer = route->ksnr_peer;
+ ksocknal_peer_addref(peer);
+
+ /* additional routes after interface exchange? */
+ ksocknal_create_routes(peer, conn->ksnc_port,
+ hello->kshm_ips, hello->kshm_nips);
+
+ /* setup the socket AFTER I've received hello (it disables
+ * SO_LINGER). I might call back to the acceptor who may want
+ * to send a protocol version response and then close the
+ * socket; this ensures the socket only tears down after the
+ * response has been sent. */
+ rc = ksocknal_lib_setup_sock(sock);
+
+ write_lock_bh (global_lock);
+
+ if (rc != 0)
+ goto failed_2;
+ } else {
+ rc = ksocknal_create_peer(&peer, ni, peerid);
+ if (rc != 0)
+ goto failed_1;
+
+ write_lock_bh (global_lock);
+
+ peer2 = ksocknal_find_peer_locked(ni, peerid);
+ if (peer2 == NULL) {
+ /* NB this puts an "empty" peer in the peer
+ * table (which takes my ref) */
+ list_add_tail(&peer->ksnp_list,
+ ksocknal_nid2peerlist(peerid.nid));
+ } else {
+ ksocknal_peer_decref(peer);
+ peer = peer2;
+ }
+
+ /* +1 ref for me */
+ ksocknal_peer_addref(peer);
+ peer->ksnp_accepting++;
+
+ /* Am I already connecting to this guy? Resolve in
+ * favour of higher NID... */
+ rc = 0;
+ if (peerid.nid < ni->ni_nid) {
+ list_for_each(tmp, &peer->ksnp_routes) {
+ route = list_entry(tmp, ksock_route_t,
+ ksnr_list);
+
+ if (route->ksnr_ipaddr != conn->ksnc_ipaddr)
+ continue;
+
+ if (route->ksnr_connecting) {
+ rc = EALREADY; /* not a failure */
+ warn = "connection race";
+ }
+
+ break;
+ }
+ }
+ route = NULL;
+
+ write_unlock_bh (global_lock);
+
+ if (rc != 0) {
+ /* set CONN_NONE makes returned HELLO acknowledge I
+ * lost a connection race */
+ conn->ksnc_type = SOCKLND_CONN_NONE;
+ hello->kshm_nips = 0;
+ ksocknal_send_hello(ni, conn, peerid.nid, hello);
+ } else {
+ hello->kshm_nips = ksocknal_select_ips(peer, hello->kshm_ips,
+ hello->kshm_nips);
+ rc = ksocknal_send_hello(ni, conn, peerid.nid, hello);
+
+ /* Setup the socket (it disables SO_LINGER). I don't
+ * do it if I'm sending a negative response to ensure
+ * the response isn't discarded when I close the socket
+ * immediately after sending it. */
+ if (rc == 0)
+ rc = ksocknal_lib_setup_sock(sock);
+ }
+
+ write_lock_bh (global_lock);
+ peer->ksnp_accepting--;
+
+ if (rc != 0)
+ goto failed_2;
+ }
+
+ if (peer->ksnp_closing ||
+ (active && route->ksnr_deleted)) {
+ /* peer/route got closed under me */
+ rc = -ESTALE;
+ warn = "peer/route removed";
+ goto failed_2;
+ }
+
+ /* Refuse to duplicate an existing connection, unless this is a
+ * loopback connection */
+ if (conn->ksnc_ipaddr != conn->ksnc_myipaddr) {
+ list_for_each(tmp, &peer->ksnp_conns) {
+ conn2 = list_entry(tmp, ksock_conn_t, ksnc_list);
+
+ if (conn2->ksnc_ipaddr != conn->ksnc_ipaddr ||
+ conn2->ksnc_myipaddr != conn->ksnc_myipaddr ||
+ conn2->ksnc_type != conn->ksnc_type ||
+ conn2->ksnc_incarnation != incarnation)
+ continue;
+
+ rc = 0; /* more of a NOOP than a failure */
+ warn = "duplicate";
+ goto failed_2;
+ }
+ }
+
+ /* If the connection created by this route didn't bind to the IP
+ * address the route connected to, the connection/route matching
+ * code below probably isn't going to work. */
+ if (active &&
+ route->ksnr_ipaddr != conn->ksnc_ipaddr) {
+ CERROR("Route %s %u.%u.%u.%u connected to %u.%u.%u.%u\n",
+ libcfs_id2str(peer->ksnp_id),
+ HIPQUAD(route->ksnr_ipaddr),
+ HIPQUAD(conn->ksnc_ipaddr));
+ }
+
+ /* Search for a route corresponding to the new connection and
+ * create an association. This allows incoming connections created
+ * by routes in my peer to match my own route entries so I don't
+ * continually create duplicate routes. */
+ list_for_each (tmp, &peer->ksnp_routes) {
+ route = list_entry(tmp, ksock_route_t, ksnr_list);
+
+ if (route->ksnr_ipaddr != conn->ksnc_ipaddr)
+ continue;
+
+ ksocknal_associate_route_conn_locked(route, conn);
+ break;
+ }
+
+ conn->ksnc_peer = peer; /* conn takes my ref on peer */
+ conn->ksnc_incarnation = incarnation;
+ peer->ksnp_last_alive = cfs_time_current();
+ peer->ksnp_error = 0;
+
+ sched = ksocknal_choose_scheduler_locked (irq);
+ sched->kss_nconns++;
+ conn->ksnc_scheduler = sched;
+
+ /* Set the deadline for the outgoing HELLO to drain */
+ conn->ksnc_tx_bufnob = SOCK_WMEM_QUEUED(sock);
+ conn->ksnc_tx_deadline = cfs_time_shift(*ksocknal_tunables.ksnd_timeout);
+ mb(); /* order with adding to peer's conn list */
+
+ list_add (&conn->ksnc_list, &peer->ksnp_conns);
+ ksocknal_conn_addref(conn);
+
+ ksocknal_new_packet(conn, 0);
+
+ /* NB my callbacks block while I hold ksnd_global_lock */
+ ksocknal_lib_set_callback(sock, conn);
+
+ /* Take all the packets blocking for a connection.
+ * NB, it might be nicer to share these blocked packets among any
+ * other connections that are becoming established. */
+ while (!list_empty (&peer->ksnp_tx_queue)) {
+ tx = list_entry (peer->ksnp_tx_queue.next,
+ ksock_tx_t, tx_list);
+
+ list_del (&tx->tx_list);
+ ksocknal_queue_tx_locked (tx, conn);
+ }
+
+ rc = ksocknal_close_stale_conns_locked(peer, incarnation);
+ write_unlock_bh (global_lock);
+
+ if (rc != 0)
+ CDEBUG(D_NET, "Closed %d stale conns to %s ip %d.%d.%d.%d\n",
+ rc, libcfs_id2str(conn->ksnc_peer->ksnp_id),
+ HIPQUAD(conn->ksnc_ipaddr));
+
+ ksocknal_lib_bind_irq (irq);
+
+ /* Call the callbacks right now to get things going. */
+ if (ksocknal_connsock_addref(conn) == 0) {
+ ksocknal_read_callback(conn);
+ ksocknal_write_callback(conn);
+ ksocknal_connsock_decref(conn);
+ }
+
+ CDEBUG(D_NET, "New conn %s %u.%u.%u.%u -> %u.%u.%u.%u/%d"
+ " incarnation:"LPD64" sched[%d]/%d\n",
+ libcfs_id2str(peerid), HIPQUAD(conn->ksnc_myipaddr),
+ HIPQUAD(conn->ksnc_ipaddr), conn->ksnc_port, incarnation,
+ (int)(conn->ksnc_scheduler - ksocknal_data.ksnd_schedulers), irq);
+
+ LIBCFS_FREE(hello, offsetof(ksock_hello_msg_t,
+ kshm_ips[LNET_MAX_INTERFACES]));
+
+ ksocknal_conn_decref(conn);
+ return (0);
+
+ failed_2:
+ if (!peer->ksnp_closing &&
+ list_empty (&peer->ksnp_conns) &&
+ list_empty (&peer->ksnp_routes)) {
+ list_add(&zombies, &peer->ksnp_tx_queue);
+ list_del_init(&peer->ksnp_tx_queue);
+ ksocknal_unlink_peer_locked(peer);
+ }
+
+ write_unlock_bh (global_lock);
+
+ if (warn != NULL) {
+ if (rc < 0)
+ CERROR("Not creating conn %s type %d: %s\n",
+ libcfs_id2str(peerid), conn->ksnc_type, warn);
+ else
+ CDEBUG(D_NET, "Not creating conn %s type %d: %s\n",
+ libcfs_id2str(peerid), conn->ksnc_type, warn);
+ }
+
+ ksocknal_txlist_done(ni, &zombies, 1);
+ ksocknal_peer_decref(peer);
+
+ failed_1:
+ if (hello != NULL)
+ LIBCFS_FREE(hello, offsetof(ksock_hello_msg_t,
+ kshm_ips[LNET_MAX_INTERFACES]));
+
+ LIBCFS_FREE (conn, sizeof(*conn));
+
+ failed_0:
+ libcfs_sock_release(sock);
+ return rc;
+}
+
+void
+ksocknal_close_conn_locked (ksock_conn_t *conn, int error)
+{
+ /* This just does the immmediate housekeeping, and queues the
+ * connection for the reaper to terminate.
+ * Caller holds ksnd_global_lock exclusively in irq context */
+ ksock_peer_t *peer = conn->ksnc_peer;
+ ksock_route_t *route;
+ ksock_conn_t *conn2;
+ struct list_head *tmp;
+
+ LASSERT (peer->ksnp_error == 0);
+ LASSERT (!conn->ksnc_closing);
+ conn->ksnc_closing = 1;
+
+ /* ksnd_deathrow_conns takes over peer's ref */
+ list_del (&conn->ksnc_list);
+
+ route = conn->ksnc_route;
+ if (route != NULL) {
+ /* dissociate conn from route... */
+ LASSERT (!route->ksnr_deleted);
+ LASSERT ((route->ksnr_connected & (1 << conn->ksnc_type)) != 0);
+
+ conn2 = NULL;
+ list_for_each(tmp, &peer->ksnp_conns) {
+ conn2 = list_entry(tmp, ksock_conn_t, ksnc_list);
+
+ if (conn2->ksnc_route == route &&
+ conn2->ksnc_type == conn->ksnc_type)
+ break;
+
+ conn2 = NULL;
+ }
+ if (conn2 == NULL)
+ route->ksnr_connected &= ~(1 << conn->ksnc_type);
+
+ conn->ksnc_route = NULL;
+
+#if 0 /* irrelevent with only eager routes */
+ list_del (&route->ksnr_list); /* make route least favourite */
+ list_add_tail (&route->ksnr_list, &peer->ksnp_routes);
+#endif
+ ksocknal_route_decref(route); /* drop conn's ref on route */
+ }