/* SMB_DIRECT negotiation timeout (for the server) in seconds */
#define SMB_DIRECT_NEGOTIATE_TIMEOUT 5
+/* The timeout to wait for a keepalive message from peer in seconds */
+#define SMB_DIRECT_KEEPALIVE_SEND_INTERVAL 120
+
+/* The timeout to wait for a keepalive message from peer in seconds */
+#define SMB_DIRECT_KEEPALIVE_RECV_TIMEOUT 5
+
/*
* Default maximum number of RDMA read/write outstanding on this connection
* This value is possibly decreased during QP creation on hardware limit
*/
disable_work(&sc->disconnect_work);
disable_work(&sc->recv_io.posted.refill_work);
+ disable_delayed_work(&sc->idle.timer_work);
disable_work(&sc->idle.immediate_work);
switch (sc->status) {
smb_direct_post_send_data(t, NULL, NULL, 0, 0);
}
+static void smb_direct_idle_connection_timer(struct work_struct *work)
+{
+ struct smbdirect_socket *sc =
+ container_of(work, struct smbdirect_socket, idle.timer_work.work);
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+ struct smb_direct_transport *t =
+ container_of(sc, struct smb_direct_transport, socket);
+
+ if (sc->idle.keepalive != SMBDIRECT_KEEPALIVE_NONE) {
+ smb_direct_disconnect_rdma_connection(t);
+ return;
+ }
+
+ if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+ return;
+
+ /*
+ * Now use the keepalive timeout (instead of keepalive interval)
+ * in order to wait for a response
+ */
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING;
+ mod_delayed_work(smb_direct_wq, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->keepalive_timeout_msec));
+ queue_work(smb_direct_wq, &sc->idle.immediate_work);
+}
+
static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id)
{
struct smb_direct_transport *t;
sp->max_fragmented_recv_size = smb_direct_max_fragmented_recv_size;
sp->max_recv_size = smb_direct_max_receive_size;
sp->max_read_write_size = smb_direct_max_read_write_size;
+ sp->keepalive_interval_msec = SMB_DIRECT_KEEPALIVE_SEND_INTERVAL * 1000;
+ sp->keepalive_timeout_msec = SMB_DIRECT_KEEPALIVE_RECV_TIMEOUT * 1000;
sc->rdma.cm_id = cm_id;
cm_id->context = t;
INIT_WORK(&sc->recv_io.posted.refill_work,
smb_direct_post_recv_credits);
INIT_WORK(&sc->idle.immediate_work, smb_direct_send_immediate_work);
+ INIT_DELAYED_WORK(&sc->idle.timer_work, smb_direct_idle_connection_timer);
conn = ksmbd_conn_alloc();
if (!conn)
wake_up_all(&sc->send_io.pending.zero_wait_queue);
disable_work_sync(&sc->recv_io.posted.refill_work);
+ disable_delayed_work_sync(&sc->idle.timer_work);
disable_work_sync(&sc->idle.immediate_work);
if (sc->ib.qp) {
ib_dma_sync_single_for_cpu(wc->qp->device, recvmsg->sge.addr,
recvmsg->sge.length, DMA_FROM_DEVICE);
+ /*
+ * Reset timer to the keepalive interval in
+ * order to trigger our next keepalive message.
+ */
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_NONE;
+ mod_delayed_work(smb_direct_wq, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->keepalive_interval_msec));
+
switch (sc->recv_io.expected) {
case SMBDIRECT_EXPECT_NEGOTIATE_REQ:
if (wc->byte_len < sizeof(struct smbdirect_negotiate_req)) {
return new_credits;
}
+static int manage_keep_alive_before_sending(struct smb_direct_transport *t)
+{
+ struct smbdirect_socket *sc = &t->socket;
+ struct smbdirect_socket_parameters *sp = &sc->parameters;
+
+ if (sc->idle.keepalive == SMBDIRECT_KEEPALIVE_PENDING) {
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_SENT;
+ /*
+ * Now use the keepalive timeout (instead of keepalive interval)
+ * in order to wait for a response
+ */
+ mod_delayed_work(smb_direct_wq, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->keepalive_timeout_msec));
+ return 1;
+ }
+ return 0;
+}
+
static int smb_direct_post_send(struct smb_direct_transport *t,
struct ib_send_wr *wr)
{
packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(t));
packet->flags = 0;
+ if (manage_keep_alive_before_sending(t))
+ packet->flags |= cpu_to_le16(SMBDIRECT_FLAG_RESPONSE_REQUESTED);
+
packet->reserved = 0;
if (!size)
packet->data_offset = 0;
conn_param.rnr_retry_count = SMB_DIRECT_CM_RNR_RETRY;
conn_param.flow_control = 0;
+ /*
+ * start with the negotiate timeout and SMBDIRECT_KEEPALIVE_PENDING
+ * so that the timer will cause a disconnect.
+ */
+ sc->idle.keepalive = SMBDIRECT_KEEPALIVE_PENDING;
+ mod_delayed_work(smb_direct_wq, &sc->idle.timer_work,
+ msecs_to_jiffies(sp->negotiate_timeout_msec));
+
WARN_ON_ONCE(sc->status != SMBDIRECT_SOCKET_RDMA_CONNECT_NEEDED);
sc->status = SMBDIRECT_SOCKET_RDMA_CONNECT_RUNNING;
ret = rdma_accept(sc->rdma.cm_id, &conn_param);