#include <asm/page.h>
#include <linux/nmi.h>
+#include <linux/pagemap.h>
#include "gnilnd.h"
/* this is useful when needed to debug wire corruption. */
* as scheduled */
int
-_kgnilnd_schedule_conn(kgn_conn_t *conn, const char *caller, int line, int refheld)
+_kgnilnd_schedule_conn(kgn_conn_t *conn, const char *caller, int line, int refheld, int lock_held)
{
kgn_device_t *dev = conn->gnc_device;
int sched;
conn, sched);
CDEBUG(D_INFO, "scheduling conn 0x%p caller %s:%d\n", conn, caller, line);
-
- spin_lock(&dev->gnd_lock);
+ if (!lock_held)
+ spin_lock(&dev->gnd_lock);
list_add_tail(&conn->gnc_schedlist, &dev->gnd_ready_conns);
- spin_unlock(&dev->gnd_lock);
+ if (!lock_held)
+ spin_unlock(&dev->gnd_lock);
set_mb(conn->gnc_last_sched_ask, jiffies);
rc = 1;
} else {
/* make sure thread(s) going to process conns - but let it make
* separate decision from conn schedule */
+ if (!lock_held)
+ kgnilnd_schedule_device(dev);
+ return rc;
+}
+
+int
+_kgnilnd_schedule_delay_conn(kgn_conn_t *conn)
+{
+ kgn_device_t *dev = conn->gnc_device;
+ int rc = 0;
+ spin_lock(&dev->gnd_lock);
+ if (list_empty(&conn->gnc_delaylist)) {
+ list_add_tail(&conn->gnc_delaylist, &dev->gnd_delay_conns);
+ rc = 1;
+ }
+ spin_unlock(&dev->gnd_lock);
+
kgnilnd_schedule_device(dev);
return rc;
}
/* Only free the buffer if we used it */
if (tx->tx_buffer_copy != NULL) {
- vfree(tx->tx_buffer_copy);
+ kgnilnd_vfree(tx->tx_buffer_copy, tx->tx_rdma_desc.length);
tx->tx_buffer_copy = NULL;
CDEBUG(D_MALLOC, "vfreed buffer2\n");
}
return sum;
}
-inline __u16
+__u16
kgnilnd_cksum_kiov(unsigned int nkiov, lnet_kiov_t *kiov,
unsigned int offset, unsigned int nob, int dump_blob)
{
* transfer.
*/
static void
-kgnilnd_parse_lnet_rdma(lnet_msg_t *lntmsg, unsigned int *niov,
+kgnilnd_parse_lnet_rdma(struct lnet_msg *lntmsg, unsigned int *niov,
unsigned int *offset, unsigned int *nob,
lnet_kiov_t **kiov, int put_len)
{
{
unsigned int niov, offset, nob;
lnet_kiov_t *kiov;
- lnet_msg_t *lntmsg = tx->tx_lntmsg[0];
+ struct lnet_msg *lntmsg = tx->tx_lntmsg[0];
int dump_cksum = (*kgnilnd_tunables.kgn_checksum_dump > 1);
GNITX_ASSERTF(tx, ((tx->tx_msg.gnm_type == GNILND_MSG_PUT_DONE) ||
__u16 cksum;
unsigned int niov, offset, nob;
lnet_kiov_t *kiov;
- lnet_msg_t *lntmsg = tx->tx_lntmsg[0];
+ struct lnet_msg *lntmsg = tx->tx_lntmsg[0];
int dump_on_err = *kgnilnd_tunables.kgn_checksum_dump;
/* we can only match certain requests */
void
kgnilnd_tx_done(kgn_tx_t *tx, int completion)
{
- lnet_msg_t *lntmsg0, *lntmsg1;
+ struct lnet_msg *lntmsg0, *lntmsg1;
int status0, status1;
- lnet_ni_t *ni = NULL;
+ struct lnet_ni *ni = NULL;
kgn_conn_t *conn = tx->tx_conn;
LASSERT(!in_interrupt());
* could free up lnet credits, resulting in a call chain back into
* the LND via kgnilnd_send and friends */
- lnet_finalize(ni, lntmsg0, status0);
+ lnet_finalize(lntmsg0, status0);
if (lntmsg1 != NULL) {
- lnet_finalize(ni, lntmsg1, status1);
+ lnet_finalize(lntmsg1, status1);
}
}
return 0;
}
-static inline int
-kgnilnd_tx_should_retry(kgn_conn_t *conn, kgn_tx_t *tx)
+static inline void
+kgnilnd_tx_log_retrans(kgn_conn_t *conn, kgn_tx_t *tx)
{
- int max_retrans = *kgnilnd_tunables.kgn_max_retransmits;
int log_retrans;
- int log_retrans_level;
-
- /* I need kgni credits to send this. Replace tx at the head of the
- * fmaq and I'll get rescheduled when credits appear */
- tx->tx_state = 0;
- tx->tx_retrans++;
- conn->gnc_tx_retrans++;
- log_retrans = ((tx->tx_retrans < 25) || ((tx->tx_retrans % 25) == 0) ||
- (tx->tx_retrans > (max_retrans / 2)));
- log_retrans_level = tx->tx_retrans < (max_retrans / 2) ? D_NET : D_NETERROR;
- /* Decision time - either error, warn or just retransmit */
+ log_retrans = ((tx->tx_retrans < 25) || ((tx->tx_retrans % 25) == 0));
/* we don't care about TX timeout - it could be that the network is slower
* or throttled. We'll keep retranmitting - so if the network is so slow
* that we fill up our mailbox, we'll keep trying to resend that msg
* until we exceed the max_retrans _or_ gnc_last_rx expires, indicating
* that he hasn't send us any traffic in return */
-
- if (tx->tx_retrans > max_retrans) {
- /* this means we are not backing off the retransmits
- * in a healthy manner and are likely chewing up the
- * CPU cycles quite badly */
- GNIDBG_TOMSG(D_ERROR, &tx->tx_msg,
- "SOFTWARE BUG: too many retransmits (%d) for tx id %x "
- "conn 0x%p->%s\n",
- tx->tx_retrans, tx->tx_id, conn,
- libcfs_nid2str(conn->gnc_peer->gnp_nid));
-
- /* yes - double errors to help debug this condition */
- GNIDBG_TOMSG(D_NETERROR, &tx->tx_msg, "connection dead. "
- "unable to send to %s for %lu secs (%d tries)",
- libcfs_nid2str(tx->tx_conn->gnc_peer->gnp_nid),
- cfs_duration_sec(jiffies - tx->tx_cred_wait),
- tx->tx_retrans);
-
- kgnilnd_close_conn(conn, -ETIMEDOUT);
-
- /* caller should terminate */
- RETURN(0);
- } else {
- /* some reasonable throttling of the debug message */
- if (log_retrans) {
- unsigned long now = jiffies;
- /* XXX Nic: Mystical TX debug here... */
- GNIDBG_SMSG_CREDS(log_retrans_level, conn);
- GNIDBG_TOMSG(log_retrans_level, &tx->tx_msg,
- "NOT_DONE on conn 0x%p->%s id %x retrans %d wait %dus"
- " last_msg %uus/%uus last_cq %uus/%uus",
- conn, libcfs_nid2str(conn->gnc_peer->gnp_nid),
- tx->tx_id, tx->tx_retrans,
- jiffies_to_usecs(now - tx->tx_cred_wait),
- jiffies_to_usecs(now - conn->gnc_last_tx),
- jiffies_to_usecs(now - conn->gnc_last_rx),
- jiffies_to_usecs(now - conn->gnc_last_tx_cq),
- jiffies_to_usecs(now - conn->gnc_last_rx_cq));
- }
- /* caller should retry */
- RETURN(1);
+
+ /* some reasonable throttling of the debug message */
+ if (log_retrans) {
+ unsigned long now = jiffies;
+ /* XXX Nic: Mystical TX debug here... */
+ /* We expect retransmissions so only log when D_NET is enabled */
+ GNIDBG_SMSG_CREDS(D_NET, conn);
+ GNIDBG_TOMSG(D_NET, &tx->tx_msg,
+ "NOT_DONE on conn 0x%p->%s id %x retrans %d wait %dus"
+ " last_msg %uus/%uus last_cq %uus/%uus",
+ conn, libcfs_nid2str(conn->gnc_peer->gnp_nid),
+ tx->tx_id, tx->tx_retrans,
+ jiffies_to_usecs(now - tx->tx_cred_wait),
+ jiffies_to_usecs(now - conn->gnc_last_tx),
+ jiffies_to_usecs(now - conn->gnc_last_rx),
+ jiffies_to_usecs(now - conn->gnc_last_tx_cq),
+ jiffies_to_usecs(now - conn->gnc_last_rx_cq));
}
}
{
kgn_conn_t *conn = tx->tx_conn;
kgn_msg_t *msg = &tx->tx_msg;
- int retry_send;
gni_return_t rrc;
unsigned long newest_last_rx, timeout;
unsigned long now;
return 0;
case GNI_RC_NOT_DONE:
- /* XXX Nic: We need to figure out how to track this
- * - there are bound to be good reasons for it,
- * but we want to know when it happens */
+ /* Jshimek: We can get GNI_RC_NOT_DONE for 3 reasons currently
+ * 1: out of mbox credits
+ * 2: out of mbox payload credits
+ * 3: On Aries out of dla credits
+ */
kgnilnd_conn_mutex_unlock(&conn->gnc_smsg_mutex);
kgnilnd_gl_mutex_unlock(&conn->gnc_device->gnd_cq_mutex);
/* We'll handle this error inline - makes the calling logic much more
return -EAGAIN;
}
- retry_send = kgnilnd_tx_should_retry(conn, tx);
- if (retry_send) {
- /* add to head of list for the state and retries */
- spin_lock(state_lock);
- kgnilnd_tx_add_state_locked(tx, conn->gnc_peer, conn, state, 0);
- spin_unlock(state_lock);
-
- /* We only reschedule for a certain number of retries, then
- * we will wait for the CQ events indicating a release of SMSG
- * credits */
- if (tx->tx_retrans < (*kgnilnd_tunables.kgn_max_retransmits/4)) {
- kgnilnd_schedule_conn(conn);
- return 0;
- } else {
- /* CQ event coming in signifies either TX completed or
- * RX receive. Either of these *could* free up credits
- * in the SMSG mbox and we should try sending again */
- GNIDBG_TX(D_NET, tx, "waiting for CQID %u event to resend",
- tx->tx_conn->gnc_cqid);
- /* use +ve return code to let upper layers know they
- * should stop looping on sends */
- return EAGAIN;
- }
+ /* I need kgni credits to send this. Replace tx at the head of the
+ * fmaq and I'll get rescheduled when credits appear. Reset the tx_state
+ * and bump retrans counts since we are requeueing the tx.
+ */
+ tx->tx_state = 0;
+ tx->tx_retrans++;
+ conn->gnc_tx_retrans++;
+
+ kgnilnd_tx_log_retrans(conn, tx);
+ /* add to head of list for the state and retries */
+ spin_lock(state_lock);
+ kgnilnd_tx_add_state_locked(tx, conn->gnc_peer, conn, state, 0);
+ spin_unlock(state_lock);
+
+ /* We only reschedule for a certain number of retries, then
+ * we will wait for the CQ events indicating a release of SMSG
+ * credits */
+ if (tx->tx_retrans < *kgnilnd_tunables.kgn_max_retransmits) {
+ kgnilnd_schedule_conn(conn);
+ return 0;
} else {
- return -EAGAIN;
+ /* CQ event coming in signifies either TX completed or
+ * RX receive. Either of these *could* free up credits
+ * in the SMSG mbox and we should try sending again */
+ GNIDBG_TX(D_NET, tx, "waiting for CQID %u event to resend",
+ tx->tx_conn->gnc_cqid);
+ kgnilnd_schedule_delay_conn(conn);
+ /* use +ve return code to let upper layers know they
+ * should stop looping on sends */
+ return EAGAIN;
}
default:
/* handle bad retcode gracefully */
}
/* lets us know if we can push this RDMA through now */
-inline int
+static int
kgnilnd_auth_rdma_bytes(kgn_device_t *dev, kgn_tx_t *tx)
{
long bytes_left;
}
void
-kgnilnd_launch_tx(kgn_tx_t *tx, kgn_net_t *net, lnet_process_id_t *target)
+kgnilnd_launch_tx(kgn_tx_t *tx, kgn_net_t *net, struct lnet_process_id *target)
{
kgn_peer_t *peer;
kgn_peer_t *new_peer = NULL;
}
/* don't create a connection if the peer is marked down */
- if (peer->gnp_down == GNILND_RCA_NODE_DOWN) {
+ if (peer->gnp_state != GNILND_PEER_UP) {
read_unlock(&kgnilnd_data.kgn_peer_conn_lock);
rc = -ENETRESET;
GOTO(no_peer, rc);
kgnilnd_add_peer_locked(target->nid, new_peer, &peer);
/* don't create a connection if the peer is not up */
- if (peer->gnp_down != GNILND_RCA_NODE_UP) {
+ if (peer->gnp_state != GNILND_PEER_UP) {
write_unlock(&kgnilnd_data.kgn_peer_conn_lock);
rc = -ENETRESET;
GOTO(no_peer, rc);
tx->tx_offset = ((__u64)((unsigned long)sink->gnrd_addr)) & 3;
if (tx->tx_offset)
- kgnilnd_admin_addref(kgnilnd_data.kgn_rev_offset);
+ atomic_inc(&kgnilnd_data.kgn_rev_offset);
if ((nob + tx->tx_offset) & 3) {
desc_nob = ((nob + tx->tx_offset) + (4 - ((nob + tx->tx_offset) & 3)));
- kgnilnd_admin_addref(kgnilnd_data.kgn_rev_length);
+ atomic_inc(&kgnilnd_data.kgn_rev_length);
} else {
desc_nob = (nob + tx->tx_offset);
}
if (tx->tx_buffer_copy == NULL) {
/* Allocate the largest copy buffer we will need, this will prevent us from overwriting data
* and require at most we allocate a few extra bytes. */
- tx->tx_buffer_copy = vmalloc(desc_nob);
+ tx->tx_buffer_copy = kgnilnd_vzalloc(desc_nob);
if (!tx->tx_buffer_copy) {
/* allocation of buffer failed nak the rdma */
kgnilnd_tx_done(tx, -EFAULT);
return 0;
}
- kgnilnd_admin_addref(kgnilnd_data.kgn_rev_copy_buff);
+ atomic_inc(&kgnilnd_data.kgn_rev_copy_buff);
rc = kgnilnd_mem_register(conn->gnc_device->gnd_handle, (__u64)tx->tx_buffer_copy, desc_nob, NULL, GNI_MEM_READWRITE, &tx->tx_buffer_copy_map_key);
if (rc != GNI_RC_SUCCESS) {
/* Registration Failed nak rdma and kill the tx. */
- vfree(tx->tx_buffer_copy);
+ kgnilnd_vfree(tx->tx_buffer_copy,
+ desc_nob);
tx->tx_buffer_copy = NULL;
kgnilnd_nak_rdma(tx->tx_conn, tx->tx_msg.gnm_type, -EFAULT, cookie, tx->tx_msg.gnm_srcnid);
kgnilnd_tx_done(tx, -EFAULT);
tx->tx_rdma_desc.remote_mem_hndl = sink->gnrd_key;
tx->tx_rdma_desc.length = desc_nob;
tx->tx_nob_rdma = nob;
- if (*kgnilnd_tunables.kgn_bte_dlvr_mode)
- tx->tx_rdma_desc.dlvr_mode = *kgnilnd_tunables.kgn_bte_dlvr_mode;
+ if (post_type == GNI_POST_RDMA_PUT && *kgnilnd_tunables.kgn_bte_put_dlvr_mode)
+ tx->tx_rdma_desc.dlvr_mode = *kgnilnd_tunables.kgn_bte_put_dlvr_mode;
+ if (post_type == GNI_POST_RDMA_GET && *kgnilnd_tunables.kgn_bte_get_dlvr_mode)
+ tx->tx_rdma_desc.dlvr_mode = *kgnilnd_tunables.kgn_bte_get_dlvr_mode;
/* prep final completion message */
kgnilnd_init_msg(&tx->tx_msg, type, tx->tx_msg.gnm_srcnid);
tx->tx_msg.gnm_u.completion.gncm_cookie = cookie;
kgnilnd_unmap_buffer(tx, 0);
if (tx->tx_buffer_copy != NULL) {
- vfree(tx->tx_buffer_copy);
+ kgnilnd_vfree(tx->tx_buffer_copy, desc_nob);
tx->tx_buffer_copy = NULL;
}
LASSERTF(rrc == GNI_RC_SUCCESS, "bad rrc %d\n", rrc);
GNIDBG_SMSG_CREDS(D_NET, conn);
+ kgnilnd_schedule_conn(conn);
+
return;
}
}
int
-kgnilnd_send(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg)
+kgnilnd_send(struct lnet_ni *ni, void *private, struct lnet_msg *lntmsg)
{
- lnet_hdr_t *hdr = &lntmsg->msg_hdr;
+ struct lnet_hdr *hdr = &lntmsg->msg_hdr;
int type = lntmsg->msg_type;
- lnet_process_id_t target = lntmsg->msg_target;
+ struct lnet_process_id target = lntmsg->msg_target;
int target_is_router = lntmsg->msg_target_is_router;
int routing = lntmsg->msg_routing;
unsigned int niov = lntmsg->msg_niov;
}
void
-kgnilnd_setup_rdma(lnet_ni_t *ni, kgn_rx_t *rx, lnet_msg_t *lntmsg, int mlen)
+kgnilnd_setup_rdma(struct lnet_ni *ni, kgn_rx_t *rx, struct lnet_msg *lntmsg, int mlen)
{
kgn_conn_t *conn = rx->grx_conn;
kgn_msg_t *rxmsg = rx->grx_msg;
kgnilnd_tx_done(tx, rc);
kgnilnd_nak_rdma(conn, done_type, rc, rxmsg->gnm_u.get.gngm_cookie, ni->ni_nid);
failed_0:
- lnet_finalize(ni, lntmsg, rc);
+ lnet_finalize(lntmsg, rc);
}
int
-kgnilnd_eager_recv(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg,
+kgnilnd_eager_recv(struct lnet_ni *ni, void *private, struct lnet_msg *lntmsg,
void **new_private)
{
kgn_rx_t *rx = private;
}
int
-kgnilnd_recv(lnet_ni_t *ni, void *private, lnet_msg_t *lntmsg,
+kgnilnd_recv(struct lnet_ni *ni, void *private, struct lnet_msg *lntmsg,
int delayed, unsigned int niov,
struct kvec *iov, lnet_kiov_t *kiov,
unsigned int offset, unsigned int mlen, unsigned int rlen)
/* someone closed the conn after we copied this out, nuke it */
kgnilnd_consume_rx(rx);
- lnet_finalize(ni, lntmsg, conn->gnc_error);
+ lnet_finalize(lntmsg, conn->gnc_error);
RETURN(0);
}
read_unlock(&kgnilnd_data.kgn_peer_conn_lock);
&rxmsg[1], 0, mlen);
kgnilnd_consume_rx(rx);
- lnet_finalize(ni, lntmsg, 0);
+ lnet_finalize(lntmsg, 0);
RETURN(0);
case GNILND_MSG_PUT_REQ:
/* LNET wants to truncate or drop transaction, sending NAK */
if (mlen == 0) {
kgnilnd_consume_rx(rx);
- lnet_finalize(ni, lntmsg, 0);
+ lnet_finalize(lntmsg, 0);
/* only error if lntmsg == NULL, otherwise we are just
* short circuiting the rdma process of 0 bytes */
/* LNET wants to truncate or drop transaction, sending NAK */
if (mlen == 0) {
kgnilnd_consume_rx(rx);
- lnet_finalize(ni, lntmsg, 0);
+ lnet_finalize(lntmsg, 0);
/* only error if lntmsg == NULL, otherwise we are just
* short circuiting the rdma process of 0 bytes */
/* LNET wants to truncate or drop transaction, sending NAK */
if (mlen == 0) {
kgnilnd_consume_rx(rx);
- lnet_finalize(ni, lntmsg, 0);
+ lnet_finalize(lntmsg, 0);
/* only error if lntmsg == NULL, otherwise we are just
* short circuiting the rdma process of 0 bytes */
if (time_after_eq(now, newest_last_rx + timeout)) {
uint32_t level = D_CONSOLE|D_NETERROR;
- if (conn->gnc_peer->gnp_down == GNILND_RCA_NODE_DOWN) {
+ if (conn->gnc_peer->gnp_state == GNILND_PEER_DOWN) {
level = D_NET;
}
GNIDBG_CONN(level, conn,
conn->gnc_close_recvd = GNILND_CLOSE_INJECT1;
conn->gnc_peer_error = -ETIMEDOUT;
}
+
+ if (*kgnilnd_tunables.kgn_to_reconn_disable &&
+ rc == -ETIMEDOUT) {
+ peer->gnp_state = GNILND_PEER_TIMED_OUT;
+ CDEBUG(D_WARNING, "%s conn timed out, will "
+ "reconnect upon request from peer\n",
+ libcfs_nid2str(conn->gnc_peer->gnp_nid));
+ }
/* Once we mark closed, any of the scheduler threads could
* get it and move through before we hit the fail loc code */
kgnilnd_close_conn_locked(conn, rc);
/* Don't reconnect if we are still trying to clear out old conns.
* This prevents us sending traffic on the new mbox before ensuring we are done
* with the old one */
- reconnect = (peer->gnp_down == GNILND_RCA_NODE_UP) &&
+ reconnect = (peer->gnp_state == GNILND_PEER_UP) &&
(atomic_read(&peer->gnp_dirty_eps) == 0);
/* fast reconnect after a timeout */
kgnilnd_recv_bte_get(kgn_tx_t *tx) {
unsigned niov, offset, nob;
lnet_kiov_t *kiov;
- lnet_msg_t *lntmsg = tx->tx_lntmsg[0];
+ struct lnet_msg *lntmsg = tx->tx_lntmsg[0];
kgnilnd_parse_lnet_rdma(lntmsg, &niov, &offset, &nob, &kiov, tx->tx_nob_rdma);
if (kiov != NULL) {
kgn_conn_t *conn = NULL;
int queued_fma, saw_reply, rc;
long num_processed = 0;
+ struct list_head *ctmp, *ctmpN;
for (;;) {
/* make sure we don't keep looping if we need to reset */
"SMSG send CQ %d not ready (data %#llx) "
"processed %ld\n", dev->gnd_id, event_data,
num_processed);
+
+ if (num_processed > 0) {
+ spin_lock(&dev->gnd_lock);
+ if (!list_empty(&dev->gnd_delay_conns)) {
+ list_for_each_safe(ctmp, ctmpN, &dev->gnd_delay_conns) {
+ conn = list_entry(ctmp, kgn_conn_t, gnc_delaylist);
+ list_del_init(&conn->gnc_delaylist);
+ CDEBUG(D_NET, "Moving Conn %p from delay queue to ready_queue\n", conn);
+ kgnilnd_schedule_conn_nolock(conn);
+ }
+ spin_unlock(&dev->gnd_lock);
+ kgnilnd_schedule_device(dev);
+ } else {
+ spin_unlock(&dev->gnd_lock);
+ }
+ }
return num_processed;
}
conn = list_first_entry(&dev->gnd_ready_conns, kgn_conn_t, gnc_schedlist);
list_del_init(&conn->gnc_schedlist);
+ /*
+ * Since we are processing conn now, we don't need to be on the delaylist any longer.
+ */
+
+ if (!list_empty(&conn->gnc_delaylist))
+ list_del_init(&conn->gnc_delaylist);
spin_unlock(&dev->gnd_lock);
conn_sched = xchg(&conn->gnc_scheduled, GNILND_CONN_PROCESS);
kgnilnd_conn_decref(conn);
up_write(&dev->gnd_conn_sem);
} else if (rc != 1) {
- kgnilnd_conn_decref(conn);
+ kgnilnd_conn_decref(conn);
}
/* clear this so that scheduler thread doesn't spin */
found_work = 0;
kgnilnd_conn_decref(conn);
up_write(&dev->gnd_conn_sem);
} else if (rc != 1) {
- kgnilnd_conn_decref(conn);
+ kgnilnd_conn_decref(conn);
}
/* check list again with lock held */