| /* |
| * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. |
| * |
| * Copyright (c) 2012, Intel Corporation. |
| * |
| * Author: Zach Brown <zab@zabbo.net> |
| * Author: Peter J. Braam <braam@clusterfs.com> |
| * Author: Phil Schwan <phil@clusterfs.com> |
| * Author: Eric Barton <eric@bartonsoftware.com> |
| * |
| * This file is part of Portals, http://www.sf.net/projects/sandiaportals/ |
| * |
| * Portals is free software; you can redistribute it and/or |
| * modify it under the terms of version 2 of the GNU General Public |
| * License as published by the Free Software Foundation. |
| * |
| * Portals is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with Portals; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| #include "socklnd.h" |
| |
| /* |
| * Protocol entries : |
| * pro_send_hello : send hello message |
| * pro_recv_hello : receive hello message |
| * pro_pack : pack message header |
| * pro_unpack : unpack message header |
| * pro_queue_tx_zcack() : Called holding BH lock: kss_lock |
| * return 1 if ACK is piggybacked, otherwise return 0 |
| * pro_queue_tx_msg() : Called holding BH lock: kss_lock |
| * return the ACK that piggybacked by my message, or NULL |
| * pro_handle_zcreq() : handler of incoming ZC-REQ |
| * pro_handle_zcack() : handler of incoming ZC-ACK |
| * pro_match_tx() : Called holding glock |
| */ |
| |
| static ksock_tx_t * |
| ksocknal_queue_tx_msg_v1(ksock_conn_t *conn, ksock_tx_t *tx_msg) |
| { |
| /* V1.x, just enqueue it */ |
| list_add_tail(&tx_msg->tx_list, &conn->ksnc_tx_queue); |
| return NULL; |
| } |
| |
| void |
| ksocknal_next_tx_carrier(ksock_conn_t *conn) |
| { |
| ksock_tx_t *tx = conn->ksnc_tx_carrier; |
| |
| /* Called holding BH lock: conn->ksnc_scheduler->kss_lock */ |
| LASSERT(!list_empty(&conn->ksnc_tx_queue)); |
| LASSERT(tx != NULL); |
| |
| /* Next TX that can carry ZC-ACK or LNet message */ |
| if (tx->tx_list.next == &conn->ksnc_tx_queue) { |
| /* no more packets queued */ |
| conn->ksnc_tx_carrier = NULL; |
| } else { |
| conn->ksnc_tx_carrier = list_entry(tx->tx_list.next, |
| ksock_tx_t, tx_list); |
| LASSERT(conn->ksnc_tx_carrier->tx_msg.ksm_type == tx->tx_msg.ksm_type); |
| } |
| } |
| |
| static int |
| ksocknal_queue_tx_zcack_v2(ksock_conn_t *conn, |
| ksock_tx_t *tx_ack, __u64 cookie) |
| { |
| ksock_tx_t *tx = conn->ksnc_tx_carrier; |
| |
| LASSERT(tx_ack == NULL || |
| tx_ack->tx_msg.ksm_type == KSOCK_MSG_NOOP); |
| |
| /* |
| * Enqueue or piggyback tx_ack / cookie |
| * . no tx can piggyback cookie of tx_ack (or cookie), just |
| * enqueue the tx_ack (if tx_ack != NUL) and return NULL. |
| * . There is tx can piggyback cookie of tx_ack (or cookie), |
| * piggyback the cookie and return the tx. |
| */ |
| if (tx == NULL) { |
| if (tx_ack != NULL) { |
| list_add_tail(&tx_ack->tx_list, |
| &conn->ksnc_tx_queue); |
| conn->ksnc_tx_carrier = tx_ack; |
| } |
| return 0; |
| } |
| |
| if (tx->tx_msg.ksm_type == KSOCK_MSG_NOOP) { |
| /* tx is noop zc-ack, can't piggyback zc-ack cookie */ |
| if (tx_ack != NULL) |
| list_add_tail(&tx_ack->tx_list, |
| &conn->ksnc_tx_queue); |
| return 0; |
| } |
| |
| LASSERT(tx->tx_msg.ksm_type == KSOCK_MSG_LNET); |
| LASSERT(tx->tx_msg.ksm_zc_cookies[1] == 0); |
| |
| if (tx_ack != NULL) |
| cookie = tx_ack->tx_msg.ksm_zc_cookies[1]; |
| |
| /* piggyback the zc-ack cookie */ |
| tx->tx_msg.ksm_zc_cookies[1] = cookie; |
| /* move on to the next TX which can carry cookie */ |
| ksocknal_next_tx_carrier(conn); |
| |
| return 1; |
| } |
| |
| static ksock_tx_t * |
| ksocknal_queue_tx_msg_v2(ksock_conn_t *conn, ksock_tx_t *tx_msg) |
| { |
| ksock_tx_t *tx = conn->ksnc_tx_carrier; |
| |
| /* |
| * Enqueue tx_msg: |
| * . If there is no NOOP on the connection, just enqueue |
| * tx_msg and return NULL |
| * . If there is NOOP on the connection, piggyback the cookie |
| * and replace the NOOP tx, and return the NOOP tx. |
| */ |
| if (tx == NULL) { /* nothing on queue */ |
| list_add_tail(&tx_msg->tx_list, &conn->ksnc_tx_queue); |
| conn->ksnc_tx_carrier = tx_msg; |
| return NULL; |
| } |
| |
| if (tx->tx_msg.ksm_type == KSOCK_MSG_LNET) { /* nothing to carry */ |
| list_add_tail(&tx_msg->tx_list, &conn->ksnc_tx_queue); |
| return NULL; |
| } |
| |
| LASSERT(tx->tx_msg.ksm_type == KSOCK_MSG_NOOP); |
| |
| /* There is a noop zc-ack can be piggybacked */ |
| tx_msg->tx_msg.ksm_zc_cookies[1] = tx->tx_msg.ksm_zc_cookies[1]; |
| ksocknal_next_tx_carrier(conn); |
| |
| /* use new_tx to replace the noop zc-ack packet */ |
| list_add(&tx_msg->tx_list, &tx->tx_list); |
| list_del(&tx->tx_list); |
| |
| return tx; |
| } |
| |
| static int |
| ksocknal_queue_tx_zcack_v3(ksock_conn_t *conn, |
| ksock_tx_t *tx_ack, __u64 cookie) |
| { |
| ksock_tx_t *tx; |
| |
| if (conn->ksnc_type != SOCKLND_CONN_ACK) |
| return ksocknal_queue_tx_zcack_v2(conn, tx_ack, cookie); |
| |
| /* non-blocking ZC-ACK (to router) */ |
| LASSERT(tx_ack == NULL || |
| tx_ack->tx_msg.ksm_type == KSOCK_MSG_NOOP); |
| |
| tx = conn->ksnc_tx_carrier; |
| if (tx == NULL) { |
| if (tx_ack != NULL) { |
| list_add_tail(&tx_ack->tx_list, |
| &conn->ksnc_tx_queue); |
| conn->ksnc_tx_carrier = tx_ack; |
| } |
| return 0; |
| } |
| |
| /* conn->ksnc_tx_carrier != NULL */ |
| |
| if (tx_ack != NULL) |
| cookie = tx_ack->tx_msg.ksm_zc_cookies[1]; |
| |
| if (cookie == SOCKNAL_KEEPALIVE_PING) /* ignore keepalive PING */ |
| return 1; |
| |
| if (tx->tx_msg.ksm_zc_cookies[1] == SOCKNAL_KEEPALIVE_PING) { |
| /* replace the keepalive PING with a real ACK */ |
| LASSERT(tx->tx_msg.ksm_zc_cookies[0] == 0); |
| tx->tx_msg.ksm_zc_cookies[1] = cookie; |
| return 1; |
| } |
| |
| if (cookie == tx->tx_msg.ksm_zc_cookies[0] || |
| cookie == tx->tx_msg.ksm_zc_cookies[1]) { |
| CWARN("%s: duplicated ZC cookie: %llu\n", |
| libcfs_id2str(conn->ksnc_peer->ksnp_id), cookie); |
| return 1; /* XXX return error in the future */ |
| } |
| |
| if (tx->tx_msg.ksm_zc_cookies[0] == 0) { |
| /* NOOP tx has only one ZC-ACK cookie, can carry at least one more */ |
| if (tx->tx_msg.ksm_zc_cookies[1] > cookie) { |
| tx->tx_msg.ksm_zc_cookies[0] = tx->tx_msg.ksm_zc_cookies[1]; |
| tx->tx_msg.ksm_zc_cookies[1] = cookie; |
| } else { |
| tx->tx_msg.ksm_zc_cookies[0] = cookie; |
| } |
| |
| if (tx->tx_msg.ksm_zc_cookies[0] - tx->tx_msg.ksm_zc_cookies[1] > 2) { |
| /* not likely to carry more ACKs, skip it to simplify logic */ |
| ksocknal_next_tx_carrier(conn); |
| } |
| |
| return 1; |
| } |
| |
| /* takes two or more cookies already */ |
| |
| if (tx->tx_msg.ksm_zc_cookies[0] > tx->tx_msg.ksm_zc_cookies[1]) { |
| __u64 tmp = 0; |
| |
| /* two separated cookies: (a+2, a) or (a+1, a) */ |
| LASSERT(tx->tx_msg.ksm_zc_cookies[0] - |
| tx->tx_msg.ksm_zc_cookies[1] <= 2); |
| |
| if (tx->tx_msg.ksm_zc_cookies[0] - |
| tx->tx_msg.ksm_zc_cookies[1] == 2) { |
| if (cookie == tx->tx_msg.ksm_zc_cookies[1] + 1) |
| tmp = cookie; |
| } else if (cookie == tx->tx_msg.ksm_zc_cookies[1] - 1) { |
| tmp = tx->tx_msg.ksm_zc_cookies[1]; |
| } else if (cookie == tx->tx_msg.ksm_zc_cookies[0] + 1) { |
| tmp = tx->tx_msg.ksm_zc_cookies[0]; |
| } |
| |
| if (tmp != 0) { |
| /* range of cookies */ |
| tx->tx_msg.ksm_zc_cookies[0] = tmp - 1; |
| tx->tx_msg.ksm_zc_cookies[1] = tmp + 1; |
| return 1; |
| } |
| |
| } else { |
| /* ksm_zc_cookies[0] < ksm_zc_cookies[1], it is range of cookies */ |
| if (cookie >= tx->tx_msg.ksm_zc_cookies[0] && |
| cookie <= tx->tx_msg.ksm_zc_cookies[1]) { |
| CWARN("%s: duplicated ZC cookie: %llu\n", |
| libcfs_id2str(conn->ksnc_peer->ksnp_id), cookie); |
| return 1; /* XXX: return error in the future */ |
| } |
| |
| if (cookie == tx->tx_msg.ksm_zc_cookies[1] + 1) { |
| tx->tx_msg.ksm_zc_cookies[1] = cookie; |
| return 1; |
| } |
| |
| if (cookie == tx->tx_msg.ksm_zc_cookies[0] - 1) { |
| tx->tx_msg.ksm_zc_cookies[0] = cookie; |
| return 1; |
| } |
| } |
| |
| /* failed to piggyback ZC-ACK */ |
| if (tx_ack != NULL) { |
| list_add_tail(&tx_ack->tx_list, &conn->ksnc_tx_queue); |
| /* the next tx can piggyback at least 1 ACK */ |
| ksocknal_next_tx_carrier(conn); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| ksocknal_match_tx(ksock_conn_t *conn, ksock_tx_t *tx, int nonblk) |
| { |
| int nob; |
| |
| #if SOCKNAL_VERSION_DEBUG |
| if (!*ksocknal_tunables.ksnd_typed_conns) |
| return SOCKNAL_MATCH_YES; |
| #endif |
| |
| if (tx == NULL || tx->tx_lnetmsg == NULL) { |
| /* noop packet */ |
| nob = offsetof(ksock_msg_t, ksm_u); |
| } else { |
| nob = tx->tx_lnetmsg->msg_len + |
| ((conn->ksnc_proto == &ksocknal_protocol_v1x) ? |
| sizeof(lnet_hdr_t) : sizeof(ksock_msg_t)); |
| } |
| |
| /* default checking for typed connection */ |
| switch (conn->ksnc_type) { |
| default: |
| CERROR("ksnc_type bad: %u\n", conn->ksnc_type); |
| LBUG(); |
| case SOCKLND_CONN_ANY: |
| return SOCKNAL_MATCH_YES; |
| |
| case SOCKLND_CONN_BULK_IN: |
| return SOCKNAL_MATCH_MAY; |
| |
| case SOCKLND_CONN_BULK_OUT: |
| if (nob < *ksocknal_tunables.ksnd_min_bulk) |
| return SOCKNAL_MATCH_MAY; |
| else |
| return SOCKNAL_MATCH_YES; |
| |
| case SOCKLND_CONN_CONTROL: |
| if (nob >= *ksocknal_tunables.ksnd_min_bulk) |
| return SOCKNAL_MATCH_MAY; |
| else |
| return SOCKNAL_MATCH_YES; |
| } |
| } |
| |
| static int |
| ksocknal_match_tx_v3(ksock_conn_t *conn, ksock_tx_t *tx, int nonblk) |
| { |
| int nob; |
| |
| if (tx == NULL || tx->tx_lnetmsg == NULL) |
| nob = offsetof(ksock_msg_t, ksm_u); |
| else |
| nob = tx->tx_lnetmsg->msg_len + sizeof(ksock_msg_t); |
| |
| switch (conn->ksnc_type) { |
| default: |
| CERROR("ksnc_type bad: %u\n", conn->ksnc_type); |
| LBUG(); |
| case SOCKLND_CONN_ANY: |
| return SOCKNAL_MATCH_NO; |
| |
| case SOCKLND_CONN_ACK: |
| if (nonblk) |
| return SOCKNAL_MATCH_YES; |
| else if (tx == NULL || tx->tx_lnetmsg == NULL) |
| return SOCKNAL_MATCH_MAY; |
| else |
| return SOCKNAL_MATCH_NO; |
| |
| case SOCKLND_CONN_BULK_OUT: |
| if (nonblk) |
| return SOCKNAL_MATCH_NO; |
| else if (nob < *ksocknal_tunables.ksnd_min_bulk) |
| return SOCKNAL_MATCH_MAY; |
| else |
| return SOCKNAL_MATCH_YES; |
| |
| case SOCKLND_CONN_CONTROL: |
| if (nonblk) |
| return SOCKNAL_MATCH_NO; |
| else if (nob >= *ksocknal_tunables.ksnd_min_bulk) |
| return SOCKNAL_MATCH_MAY; |
| else |
| return SOCKNAL_MATCH_YES; |
| } |
| } |
| |
| /* (Sink) handle incoming ZC request from sender */ |
| static int |
| ksocknal_handle_zcreq(ksock_conn_t *c, __u64 cookie, int remote) |
| { |
| ksock_peer_t *peer = c->ksnc_peer; |
| ksock_conn_t *conn; |
| ksock_tx_t *tx; |
| int rc; |
| |
| read_lock(&ksocknal_data.ksnd_global_lock); |
| |
| conn = ksocknal_find_conn_locked(peer, NULL, !!remote); |
| if (conn != NULL) { |
| ksock_sched_t *sched = conn->ksnc_scheduler; |
| |
| LASSERT(conn->ksnc_proto->pro_queue_tx_zcack != NULL); |
| |
| spin_lock_bh(&sched->kss_lock); |
| |
| rc = conn->ksnc_proto->pro_queue_tx_zcack(conn, NULL, cookie); |
| |
| spin_unlock_bh(&sched->kss_lock); |
| |
| if (rc) { /* piggybacked */ |
| read_unlock(&ksocknal_data.ksnd_global_lock); |
| return 0; |
| } |
| } |
| |
| read_unlock(&ksocknal_data.ksnd_global_lock); |
| |
| /* ACK connection is not ready, or can't piggyback the ACK */ |
| tx = ksocknal_alloc_tx_noop(cookie, !!remote); |
| if (tx == NULL) |
| return -ENOMEM; |
| |
| rc = ksocknal_launch_packet(peer->ksnp_ni, tx, peer->ksnp_id); |
| if (rc == 0) |
| return 0; |
| |
| ksocknal_free_tx(tx); |
| return rc; |
| } |
| |
| /* (Sender) handle ZC_ACK from sink */ |
| static int |
| ksocknal_handle_zcack(ksock_conn_t *conn, __u64 cookie1, __u64 cookie2) |
| { |
| ksock_peer_t *peer = conn->ksnc_peer; |
| ksock_tx_t *tx; |
| ksock_tx_t *tmp; |
| LIST_HEAD(zlist); |
| int count; |
| |
| if (cookie1 == 0) |
| cookie1 = cookie2; |
| |
| count = (cookie1 > cookie2) ? 2 : (cookie2 - cookie1 + 1); |
| |
| if (cookie2 == SOCKNAL_KEEPALIVE_PING && |
| conn->ksnc_proto == &ksocknal_protocol_v3x) { |
| /* keepalive PING for V3.x, just ignore it */ |
| return count == 1 ? 0 : -EPROTO; |
| } |
| |
| spin_lock(&peer->ksnp_lock); |
| |
| list_for_each_entry_safe(tx, tmp, |
| &peer->ksnp_zc_req_list, tx_zc_list) { |
| __u64 c = tx->tx_msg.ksm_zc_cookies[0]; |
| |
| if (c == cookie1 || c == cookie2 || (cookie1 < c && c < cookie2)) { |
| tx->tx_msg.ksm_zc_cookies[0] = 0; |
| list_del(&tx->tx_zc_list); |
| list_add(&tx->tx_zc_list, &zlist); |
| |
| if (--count == 0) |
| break; |
| } |
| } |
| |
| spin_unlock(&peer->ksnp_lock); |
| |
| while (!list_empty(&zlist)) { |
| tx = list_entry(zlist.next, ksock_tx_t, tx_zc_list); |
| list_del(&tx->tx_zc_list); |
| ksocknal_tx_decref(tx); |
| } |
| |
| return count == 0 ? 0 : -EPROTO; |
| } |
| |
| static int |
| ksocknal_send_hello_v1(ksock_conn_t *conn, ksock_hello_msg_t *hello) |
| { |
| struct socket *sock = conn->ksnc_sock; |
| lnet_hdr_t *hdr; |
| lnet_magicversion_t *hmv; |
| int rc; |
| int i; |
| |
| CLASSERT(sizeof(lnet_magicversion_t) == offsetof(lnet_hdr_t, src_nid)); |
| |
| LIBCFS_ALLOC(hdr, sizeof(*hdr)); |
| if (hdr == NULL) { |
| CERROR("Can't allocate lnet_hdr_t\n"); |
| return -ENOMEM; |
| } |
| |
| hmv = (lnet_magicversion_t *)&hdr->dest_nid; |
| |
| /* Re-organize V2.x message header to V1.x (lnet_hdr_t) |
| * header and send out */ |
| hmv->magic = cpu_to_le32 (LNET_PROTO_TCP_MAGIC); |
| hmv->version_major = cpu_to_le16 (KSOCK_PROTO_V1_MAJOR); |
| hmv->version_minor = cpu_to_le16 (KSOCK_PROTO_V1_MINOR); |
| |
| if (the_lnet.ln_testprotocompat != 0) { |
| /* single-shot proto check */ |
| LNET_LOCK(); |
| if ((the_lnet.ln_testprotocompat & 1) != 0) { |
| hmv->version_major++; /* just different! */ |
| the_lnet.ln_testprotocompat &= ~1; |
| } |
| if ((the_lnet.ln_testprotocompat & 2) != 0) { |
| hmv->magic = LNET_PROTO_MAGIC; |
| the_lnet.ln_testprotocompat &= ~2; |
| } |
| LNET_UNLOCK(); |
| } |
| |
| hdr->src_nid = cpu_to_le64 (hello->kshm_src_nid); |
| hdr->src_pid = cpu_to_le32 (hello->kshm_src_pid); |
| hdr->type = cpu_to_le32 (LNET_MSG_HELLO); |
| hdr->payload_length = cpu_to_le32 (hello->kshm_nips * sizeof(__u32)); |
| hdr->msg.hello.type = cpu_to_le32 (hello->kshm_ctype); |
| hdr->msg.hello.incarnation = cpu_to_le64 (hello->kshm_src_incarnation); |
| |
| rc = lnet_sock_write(sock, hdr, sizeof(*hdr), lnet_acceptor_timeout()); |
| if (rc != 0) { |
| CNETERR("Error %d sending HELLO hdr to %pI4h/%d\n", |
| rc, &conn->ksnc_ipaddr, conn->ksnc_port); |
| goto out; |
| } |
| |
| if (hello->kshm_nips == 0) |
| goto out; |
| |
| for (i = 0; i < (int) hello->kshm_nips; i++) { |
| hello->kshm_ips[i] = __cpu_to_le32 (hello->kshm_ips[i]); |
| } |
| |
| rc = lnet_sock_write(sock, hello->kshm_ips, |
| hello->kshm_nips * sizeof(__u32), |
| lnet_acceptor_timeout()); |
| if (rc != 0) { |
| CNETERR("Error %d sending HELLO payload (%d) to %pI4h/%d\n", |
| rc, hello->kshm_nips, |
| &conn->ksnc_ipaddr, conn->ksnc_port); |
| } |
| out: |
| LIBCFS_FREE(hdr, sizeof(*hdr)); |
| |
| return rc; |
| } |
| |
| static int |
| ksocknal_send_hello_v2(ksock_conn_t *conn, ksock_hello_msg_t *hello) |
| { |
| struct socket *sock = conn->ksnc_sock; |
| int rc; |
| |
| hello->kshm_magic = LNET_PROTO_MAGIC; |
| hello->kshm_version = conn->ksnc_proto->pro_version; |
| |
| if (the_lnet.ln_testprotocompat != 0) { |
| /* single-shot proto check */ |
| LNET_LOCK(); |
| if ((the_lnet.ln_testprotocompat & 1) != 0) { |
| hello->kshm_version++; /* just different! */ |
| the_lnet.ln_testprotocompat &= ~1; |
| } |
| LNET_UNLOCK(); |
| } |
| |
| rc = lnet_sock_write(sock, hello, offsetof(ksock_hello_msg_t, kshm_ips), |
| lnet_acceptor_timeout()); |
| if (rc != 0) { |
| CNETERR("Error %d sending HELLO hdr to %pI4h/%d\n", |
| rc, &conn->ksnc_ipaddr, conn->ksnc_port); |
| return rc; |
| } |
| |
| if (hello->kshm_nips == 0) |
| return 0; |
| |
| rc = lnet_sock_write(sock, hello->kshm_ips, |
| hello->kshm_nips * sizeof(__u32), |
| lnet_acceptor_timeout()); |
| if (rc != 0) { |
| CNETERR("Error %d sending HELLO payload (%d) to %pI4h/%d\n", |
| rc, hello->kshm_nips, |
| &conn->ksnc_ipaddr, conn->ksnc_port); |
| } |
| |
| return rc; |
| } |
| |
| static int |
| ksocknal_recv_hello_v1(ksock_conn_t *conn, ksock_hello_msg_t *hello, |
| int timeout) |
| { |
| struct socket *sock = conn->ksnc_sock; |
| lnet_hdr_t *hdr; |
| int rc; |
| int i; |
| |
| LIBCFS_ALLOC(hdr, sizeof(*hdr)); |
| if (hdr == NULL) { |
| CERROR("Can't allocate lnet_hdr_t\n"); |
| return -ENOMEM; |
| } |
| |
| rc = lnet_sock_read(sock, &hdr->src_nid, |
| sizeof(*hdr) - offsetof(lnet_hdr_t, src_nid), |
| timeout); |
| if (rc != 0) { |
| CERROR("Error %d reading rest of HELLO hdr from %pI4h\n", |
| rc, &conn->ksnc_ipaddr); |
| LASSERT(rc < 0 && rc != -EALREADY); |
| goto out; |
| } |
| |
| /* ...and check we got what we expected */ |
| if (hdr->type != cpu_to_le32 (LNET_MSG_HELLO)) { |
| CERROR("Expecting a HELLO hdr, but got type %d from %pI4h\n", |
| le32_to_cpu(hdr->type), |
| &conn->ksnc_ipaddr); |
| rc = -EPROTO; |
| goto out; |
| } |
| |
| hello->kshm_src_nid = le64_to_cpu(hdr->src_nid); |
| hello->kshm_src_pid = le32_to_cpu(hdr->src_pid); |
| hello->kshm_src_incarnation = le64_to_cpu(hdr->msg.hello.incarnation); |
| hello->kshm_ctype = le32_to_cpu(hdr->msg.hello.type); |
| hello->kshm_nips = le32_to_cpu(hdr->payload_length) / |
| sizeof(__u32); |
| |
| if (hello->kshm_nips > LNET_MAX_INTERFACES) { |
| CERROR("Bad nips %d from ip %pI4h\n", |
| hello->kshm_nips, &conn->ksnc_ipaddr); |
| rc = -EPROTO; |
| goto out; |
| } |
| |
| if (hello->kshm_nips == 0) |
| goto out; |
| |
| rc = lnet_sock_read(sock, hello->kshm_ips, |
| hello->kshm_nips * sizeof(__u32), timeout); |
| if (rc != 0) { |
| CERROR("Error %d reading IPs from ip %pI4h\n", |
| rc, &conn->ksnc_ipaddr); |
| LASSERT(rc < 0 && rc != -EALREADY); |
| goto out; |
| } |
| |
| for (i = 0; i < (int) hello->kshm_nips; i++) { |
| hello->kshm_ips[i] = __le32_to_cpu(hello->kshm_ips[i]); |
| |
| if (hello->kshm_ips[i] == 0) { |
| CERROR("Zero IP[%d] from ip %pI4h\n", |
| i, &conn->ksnc_ipaddr); |
| rc = -EPROTO; |
| break; |
| } |
| } |
| out: |
| LIBCFS_FREE(hdr, sizeof(*hdr)); |
| |
| return rc; |
| } |
| |
| static int |
| ksocknal_recv_hello_v2(ksock_conn_t *conn, ksock_hello_msg_t *hello, int timeout) |
| { |
| struct socket *sock = conn->ksnc_sock; |
| int rc; |
| int i; |
| |
| if (hello->kshm_magic == LNET_PROTO_MAGIC) |
| conn->ksnc_flip = 0; |
| else |
| conn->ksnc_flip = 1; |
| |
| rc = lnet_sock_read(sock, &hello->kshm_src_nid, |
| offsetof(ksock_hello_msg_t, kshm_ips) - |
| offsetof(ksock_hello_msg_t, kshm_src_nid), |
| timeout); |
| if (rc != 0) { |
| CERROR("Error %d reading HELLO from %pI4h\n", |
| rc, &conn->ksnc_ipaddr); |
| LASSERT(rc < 0 && rc != -EALREADY); |
| return rc; |
| } |
| |
| if (conn->ksnc_flip) { |
| __swab32s(&hello->kshm_src_pid); |
| __swab64s(&hello->kshm_src_nid); |
| __swab32s(&hello->kshm_dst_pid); |
| __swab64s(&hello->kshm_dst_nid); |
| __swab64s(&hello->kshm_src_incarnation); |
| __swab64s(&hello->kshm_dst_incarnation); |
| __swab32s(&hello->kshm_ctype); |
| __swab32s(&hello->kshm_nips); |
| } |
| |
| if (hello->kshm_nips > LNET_MAX_INTERFACES) { |
| CERROR("Bad nips %d from ip %pI4h\n", |
| hello->kshm_nips, &conn->ksnc_ipaddr); |
| return -EPROTO; |
| } |
| |
| if (hello->kshm_nips == 0) |
| return 0; |
| |
| rc = lnet_sock_read(sock, hello->kshm_ips, |
| hello->kshm_nips * sizeof(__u32), timeout); |
| if (rc != 0) { |
| CERROR("Error %d reading IPs from ip %pI4h\n", |
| rc, &conn->ksnc_ipaddr); |
| LASSERT(rc < 0 && rc != -EALREADY); |
| return rc; |
| } |
| |
| for (i = 0; i < (int) hello->kshm_nips; i++) { |
| if (conn->ksnc_flip) |
| __swab32s(&hello->kshm_ips[i]); |
| |
| if (hello->kshm_ips[i] == 0) { |
| CERROR("Zero IP[%d] from ip %pI4h\n", |
| i, &conn->ksnc_ipaddr); |
| return -EPROTO; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static void |
| ksocknal_pack_msg_v1(ksock_tx_t *tx) |
| { |
| /* V1.x has no KSOCK_MSG_NOOP */ |
| LASSERT(tx->tx_msg.ksm_type != KSOCK_MSG_NOOP); |
| LASSERT(tx->tx_lnetmsg != NULL); |
| |
| tx->tx_iov[0].iov_base = &tx->tx_lnetmsg->msg_hdr; |
| tx->tx_iov[0].iov_len = sizeof(lnet_hdr_t); |
| |
| tx->tx_resid = tx->tx_nob = tx->tx_lnetmsg->msg_len + sizeof(lnet_hdr_t); |
| } |
| |
| static void |
| ksocknal_pack_msg_v2(ksock_tx_t *tx) |
| { |
| tx->tx_iov[0].iov_base = &tx->tx_msg; |
| |
| if (tx->tx_lnetmsg != NULL) { |
| LASSERT(tx->tx_msg.ksm_type != KSOCK_MSG_NOOP); |
| |
| tx->tx_msg.ksm_u.lnetmsg.ksnm_hdr = tx->tx_lnetmsg->msg_hdr; |
| tx->tx_iov[0].iov_len = sizeof(ksock_msg_t); |
| tx->tx_resid = tx->tx_nob = sizeof(ksock_msg_t) + tx->tx_lnetmsg->msg_len; |
| } else { |
| LASSERT(tx->tx_msg.ksm_type == KSOCK_MSG_NOOP); |
| |
| tx->tx_iov[0].iov_len = offsetof(ksock_msg_t, ksm_u.lnetmsg.ksnm_hdr); |
| tx->tx_resid = tx->tx_nob = offsetof(ksock_msg_t, ksm_u.lnetmsg.ksnm_hdr); |
| } |
| /* Don't checksum before start sending, because packet can be piggybacked with ACK */ |
| } |
| |
| static void |
| ksocknal_unpack_msg_v1(ksock_msg_t *msg) |
| { |
| msg->ksm_csum = 0; |
| msg->ksm_type = KSOCK_MSG_LNET; |
| msg->ksm_zc_cookies[0] = msg->ksm_zc_cookies[1] = 0; |
| } |
| |
| static void |
| ksocknal_unpack_msg_v2(ksock_msg_t *msg) |
| { |
| return; /* Do nothing */ |
| } |
| |
| ksock_proto_t ksocknal_protocol_v1x = { |
| .pro_version = KSOCK_PROTO_V1, |
| .pro_send_hello = ksocknal_send_hello_v1, |
| .pro_recv_hello = ksocknal_recv_hello_v1, |
| .pro_pack = ksocknal_pack_msg_v1, |
| .pro_unpack = ksocknal_unpack_msg_v1, |
| .pro_queue_tx_msg = ksocknal_queue_tx_msg_v1, |
| .pro_handle_zcreq = NULL, |
| .pro_handle_zcack = NULL, |
| .pro_queue_tx_zcack = NULL, |
| .pro_match_tx = ksocknal_match_tx |
| }; |
| |
| ksock_proto_t ksocknal_protocol_v2x = { |
| .pro_version = KSOCK_PROTO_V2, |
| .pro_send_hello = ksocknal_send_hello_v2, |
| .pro_recv_hello = ksocknal_recv_hello_v2, |
| .pro_pack = ksocknal_pack_msg_v2, |
| .pro_unpack = ksocknal_unpack_msg_v2, |
| .pro_queue_tx_msg = ksocknal_queue_tx_msg_v2, |
| .pro_queue_tx_zcack = ksocknal_queue_tx_zcack_v2, |
| .pro_handle_zcreq = ksocknal_handle_zcreq, |
| .pro_handle_zcack = ksocknal_handle_zcack, |
| .pro_match_tx = ksocknal_match_tx |
| }; |
| |
| ksock_proto_t ksocknal_protocol_v3x = { |
| .pro_version = KSOCK_PROTO_V3, |
| .pro_send_hello = ksocknal_send_hello_v2, |
| .pro_recv_hello = ksocknal_recv_hello_v2, |
| .pro_pack = ksocknal_pack_msg_v2, |
| .pro_unpack = ksocknal_unpack_msg_v2, |
| .pro_queue_tx_msg = ksocknal_queue_tx_msg_v2, |
| .pro_queue_tx_zcack = ksocknal_queue_tx_zcack_v3, |
| .pro_handle_zcreq = ksocknal_handle_zcreq, |
| .pro_handle_zcack = ksocknal_handle_zcack, |
| .pro_match_tx = ksocknal_match_tx_v3 |
| }; |