spin_unlock(&conn->ibc_lock);
}
-static int kiblnd_resolve_addr(struct rdma_cm_id *cmid,
- struct sockaddr_in *srcaddr,
- struct sockaddr_in *dstaddr,
- int timeout_ms)
+static int
+kiblnd_resolve_addr_cap(struct rdma_cm_id *cmid,
+ struct sockaddr_in *srcaddr,
+ struct sockaddr_in *dstaddr,
+ int timeout_ms)
{
unsigned short port;
int rc;
+ LASSERT(capable(CAP_NET_BIND_SERVICE));
+
/* allow the port to be reused */
rc = rdma_set_reuseaddr(cmid, 1);
if (rc != 0) {
return rc;
}
+static int
+kiblnd_resolve_addr(struct rdma_cm_id *cmid,
+ struct sockaddr_in *srcaddr,
+ struct sockaddr_in *dstaddr,
+ int timeout_ms)
+{
+ const struct cred *old_creds = NULL;
+ struct cred *new_creds;
+ int rc;
+
+ if (!capable(CAP_NET_BIND_SERVICE)) {
+ new_creds = prepare_creds();
+ if (!new_creds)
+ return -ENOMEM;
+
+ cap_raise(new_creds->cap_effective, CAP_NET_BIND_SERVICE);
+ old_creds = override_creds(new_creds);
+ }
+
+ rc = kiblnd_resolve_addr_cap(cmid, srcaddr, dstaddr, timeout_ms);
+
+ if (old_creds)
+ revert_creds(old_creds);
+
+ return rc;
+}
+
static void
kiblnd_connect_peer(struct kib_peer_ni *peer_ni)
{
int
kiblnd_thread_start(int (*fn)(void *arg), void *arg, char *name)
{
- struct task_struct *task = kthread_run(fn, arg, name);
+ struct task_struct *task = kthread_run(fn, arg, "%s", name);
if (IS_ERR(task))
return PTR_ERR(task);
kiblnd_abort_txs(struct kib_conn *conn, struct list_head *txs)
{
LIST_HEAD(zombies);
- struct list_head *tmp;
- struct list_head *nxt;
+ struct kib_tx *nxt;
struct kib_tx *tx;
spin_lock(&conn->ibc_lock);
- list_for_each_safe(tmp, nxt, txs) {
- tx = list_entry(tmp, struct kib_tx, tx_list);
-
+ list_for_each_entry_safe(tx, nxt, txs, tx_list) {
if (txs == &conn->ibc_active_txs) {
LASSERT(!tx->tx_queued);
LASSERT(tx->tx_waiting ||
if (tx->tx_sending == 0) {
tx->tx_queued = 0;
list_move(&tx->tx_list, &zombies);
+ } else {
+ /* keep tx until cq destroy */
+ list_move(&tx->tx_list, &conn->ibc_zombie_txs);
+ conn->ibc_waits ++;
}
}
kiblnd_txlist_done(&zombies, -ECONNABORTED, LNET_MSG_STATUS_OK);
}
+static int
+kiblnd_tx_may_discard(struct kib_conn *conn)
+{
+ int rc = 0;
+ struct kib_tx *nxt;
+ struct kib_tx *tx;
+
+ spin_lock(&conn->ibc_lock);
+
+ list_for_each_entry_safe(tx, nxt, &conn->ibc_zombie_txs, tx_list) {
+ if (tx->tx_sending > 0 && tx->tx_lntmsg[0] &&
+ lnet_md_discarded(tx->tx_lntmsg[0]->msg_md)) {
+ tx->tx_sending --;
+ if (tx->tx_sending == 0) {
+ kiblnd_conn_decref(tx->tx_conn);
+ tx->tx_conn = NULL;
+ rc = 1;
+ }
+ }
+ }
+
+ spin_unlock(&conn->ibc_lock);
+ return rc;
+}
+
static void
kiblnd_finalise_conn(struct kib_conn *conn)
{
CNETERR("Deleting messages for %s: connection failed\n",
libcfs_nid2str(peer_ni->ibp_nid));
- kiblnd_txlist_done(&zombies, error,
- LNET_MSG_STATUS_LOCAL_DROPPED);
+ if (error == -EHOSTUNREACH || error == -ETIMEDOUT)
+ kiblnd_txlist_done(&zombies, error,
+ LNET_MSG_STATUS_NETWORK_TIMEOUT);
+ else
+ kiblnd_txlist_done(&zombies, error,
+ LNET_MSG_STATUS_LOCAL_DROPPED);
}
static void
(conn->ibc_state == IBLND_CONN_PASSIVE_WAIT &&
peer_ni->ibp_accepting > 0));
- LIBCFS_FREE(conn->ibc_connvars, sizeof(*conn->ibc_connvars));
- conn->ibc_connvars = NULL;
+ LIBCFS_FREE(conn->ibc_connvars, sizeof(*conn->ibc_connvars));
+ conn->ibc_connvars = NULL;
- if (status != 0) {
- /* failed to establish connection */
- kiblnd_peer_connect_failed(peer_ni, active, status);
- kiblnd_finalise_conn(conn);
- return;
- }
+ if (status != 0) {
+ /* failed to establish connection */
+ kiblnd_peer_connect_failed(peer_ni, active, status);
+ kiblnd_finalise_conn(conn);
+ return;
+ }
- /* connection established */
+ /* connection established */
write_lock_irqsave(&kiblnd_data.kib_global_lock, flags);
+ /* reset retry count */
+ peer_ni->ibp_retries = 0;
+
conn->ibc_last_send = ktime_get();
- kiblnd_set_conn_state(conn, IBLND_CONN_ESTABLISHED);
- kiblnd_peer_alive(peer_ni);
+ kiblnd_set_conn_state(conn, IBLND_CONN_ESTABLISHED);
+ kiblnd_peer_alive(peer_ni);
/* Add conn to peer_ni's list and nuke any dangling conns from a different
* peer_ni instance... */
{
int rc;
+#ifdef HAVE_RDMA_REJECT_4ARGS
+ rc = rdma_reject(cmid, rej, sizeof(*rej), IB_CM_REJ_CONSUMER_DEFINED);
+#else
rc = rdma_reject(cmid, rej, sizeof(*rej));
+#endif
if (rc != 0)
CWARN("Error %d sending reject\n", rc);
goto out;
}
- switch (why) {
- default:
- reason = "Unknown";
- break;
+ if (peer_ni->ibp_retries > *kiblnd_tunables.kib_retry_count) {
+ reason = "retry count exceeded due to no listener";
+ goto out;
+ }
+
+ switch (why) {
+ default:
+ reason = "Unknown";
+ break;
case IBLND_REJECT_RDMA_FRAGS: {
struct lnet_ioctl_config_o2iblnd_tunables *tunables;
IBLND_REJECT_CONN_STALE, NULL);
break;
- case IB_CM_REJ_INVALID_SERVICE_ID:
+ case IB_CM_REJ_INVALID_SERVICE_ID:
+ peer_ni->ibp_retries++;
kiblnd_check_reconnect(conn, IBLND_MSG_VERSION, 0,
IBLND_REJECT_INVALID_SRV_ID, NULL);
- CNETERR("%s rejected: no listener at %d\n",
- libcfs_nid2str(peer_ni->ibp_nid),
- *kiblnd_tunables.kib_service);
- break;
+ CNETERR("%s rejected: no listener at %d\n",
+ libcfs_nid2str(peer_ni->ibp_nid),
+ *kiblnd_tunables.kib_service);
+ break;
case IB_CM_REJ_CONSUMER_DEFINED:
if (priv_nob >= offsetof(struct kib_rej, ibr_padding)) {
}
if (ktime_compare(ktime_get(), tx->tx_deadline) >= 0) {
- CERROR("Timed out tx: %s, %lld seconds\n",
+ CERROR("Timed out tx: %s(WSQ:%d%d%d), %lld seconds\n",
kiblnd_queue2str(conn, txs),
+ tx->tx_waiting, tx->tx_sending, tx->tx_queued,
+ kiblnd_timeout() +
ktime_ms_delta(ktime_get(),
tx->tx_deadline) / MSEC_PER_SEC);
return 1;
if (!list_empty(&timedout_txs))
kiblnd_txlist_done(&timedout_txs, -ETIMEDOUT,
- LNET_MSG_STATUS_LOCAL_TIMEOUT);
+ LNET_MSG_STATUS_NETWORK_TIMEOUT);
/* Handle timeout by closing the whole
* connection. We can only be sure RDMA activity
}
if (!list_empty(&kiblnd_data.kib_connd_conns)) {
+ int wait;
conn = list_entry(kiblnd_data.kib_connd_conns.next,
struct kib_conn, ibc_list);
list_del(&conn->ibc_list);
dropped_lock = 1;
kiblnd_disconnect_conn(conn);
- kiblnd_conn_decref(conn);
+ wait = conn->ibc_waits;
+ if (wait == 0) /* keep ref for connd_wait, see below */
+ kiblnd_conn_decref(conn);
spin_lock_irqsave(lock, flags);
+
+ if (wait)
+ list_add_tail(&conn->ibc_list,
+ &kiblnd_data.kib_connd_waits);
}
while (reconn < KIB_RECONN_BREAK) {
spin_lock_irqsave(lock, flags);
}
+ if (!list_empty(&kiblnd_data.kib_connd_waits)) {
+ conn = list_entry(kiblnd_data.kib_connd_waits.next,
+ struct kib_conn, ibc_list);
+ list_del(&conn->ibc_list);
+ spin_unlock_irqrestore(lock, flags);
+
+ dropped_lock = kiblnd_tx_may_discard(conn);
+ if (dropped_lock)
+ kiblnd_conn_decref(conn);
+
+ spin_lock_irqsave(lock, flags);
+ if (dropped_lock == 0)
+ list_add_tail(&conn->ibc_list,
+ &kiblnd_data.kib_connd_waits);
+ }
+
/* careful with the jiffy wrap... */
timeout = (int)(deadline - jiffies);
if (timeout <= 0) {