RDMA/rtrs: Use new shared CQ mechanism
[linux-2.6-microblaze.git] / drivers / infiniband / ulp / rtrs / rtrs-clt.c
index 67f86c4..4e9cf06 100644 (file)
@@ -31,6 +31,8 @@
  */
 #define RTRS_RECONNECT_SEED 8
 
+#define FIRST_CONN 0x01
+
 MODULE_DESCRIPTION("RDMA Transport Client");
 MODULE_LICENSE("GPL");
 
@@ -178,18 +180,18 @@ struct rtrs_clt_con *rtrs_permit_to_clt_con(struct rtrs_clt_sess *sess,
 }
 
 /**
- * __rtrs_clt_change_state() - change the session state through session state
+ * rtrs_clt_change_state() - change the session state through session state
  * machine.
  *
  * @sess: client session to change the state of.
  * @new_state: state to change to.
  *
- * returns true if successful, false if the requested state can not be set.
+ * returns true if sess's state is changed to new state, otherwise return false.
  *
  * Locks:
  * state_wq lock must be hold.
  */
-static bool __rtrs_clt_change_state(struct rtrs_clt_sess *sess,
+static bool rtrs_clt_change_state(struct rtrs_clt_sess *sess,
                                     enum rtrs_clt_state new_state)
 {
        enum rtrs_clt_state old_state;
@@ -286,7 +288,7 @@ static bool rtrs_clt_change_state_from_to(struct rtrs_clt_sess *sess,
 
        spin_lock_irq(&sess->state_wq.lock);
        if (sess->state == old_state)
-               changed = __rtrs_clt_change_state(sess, new_state);
+               changed = rtrs_clt_change_state(sess, new_state);
        spin_unlock_irq(&sess->state_wq.lock);
 
        return changed;
@@ -323,7 +325,7 @@ static void rtrs_rdma_error_recovery(struct rtrs_clt_con *con)
 
 static void rtrs_clt_fast_reg_done(struct ib_cq *cq, struct ib_wc *wc)
 {
-       struct rtrs_clt_con *con = cq->cq_context;
+       struct rtrs_clt_con *con = to_clt_con(wc->qp->qp_context);
 
        if (unlikely(wc->status != IB_WC_SUCCESS)) {
                rtrs_err(con->c.sess, "Failed IB_WR_REG_MR: %s\n",
@@ -343,7 +345,7 @@ static void rtrs_clt_inv_rkey_done(struct ib_cq *cq, struct ib_wc *wc)
 {
        struct rtrs_clt_io_req *req =
                container_of(wc->wr_cqe, typeof(*req), inv_cqe);
-       struct rtrs_clt_con *con = cq->cq_context;
+       struct rtrs_clt_con *con = to_clt_con(wc->qp->qp_context);
 
        if (unlikely(wc->status != IB_WC_SUCCESS)) {
                rtrs_err(con->c.sess, "Failed IB_WR_LOCAL_INV: %s\n",
@@ -494,7 +496,7 @@ static void rtrs_clt_recv_done(struct rtrs_clt_con *con, struct ib_wc *wc)
        int err;
        struct rtrs_clt_sess *sess = to_clt_sess(con->c.sess);
 
-       WARN_ON(sess->flags != RTRS_MSG_NEW_RKEY_F);
+       WARN_ON((sess->flags & RTRS_MSG_NEW_RKEY_F) == 0);
        iu = container_of(wc->wr_cqe, struct rtrs_iu,
                          cqe);
        err = rtrs_iu_post_recv(&con->c, iu);
@@ -514,7 +516,7 @@ static void rtrs_clt_rkey_rsp_done(struct rtrs_clt_con *con, struct ib_wc *wc)
        u32 buf_id;
        int err;
 
-       WARN_ON(sess->flags != RTRS_MSG_NEW_RKEY_F);
+       WARN_ON((sess->flags & RTRS_MSG_NEW_RKEY_F) == 0);
 
        iu = container_of(wc->wr_cqe, struct rtrs_iu, cqe);
 
@@ -584,7 +586,7 @@ static int rtrs_post_recv_empty_x2(struct rtrs_con *con, struct ib_cqe *cqe)
 
 static void rtrs_clt_rdma_done(struct ib_cq *cq, struct ib_wc *wc)
 {
-       struct rtrs_clt_con *con = cq->cq_context;
+       struct rtrs_clt_con *con = to_clt_con(wc->qp->qp_context);
        struct rtrs_clt_sess *sess = to_clt_sess(con->c.sess);
        u32 imm_type, imm_payload;
        bool w_inval = false;
@@ -621,12 +623,12 @@ static void rtrs_clt_rdma_done(struct ib_cq *cq, struct ib_wc *wc)
                } else if (imm_type == RTRS_HB_MSG_IMM) {
                        WARN_ON(con->c.cid);
                        rtrs_send_hb_ack(&sess->s);
-                       if (sess->flags == RTRS_MSG_NEW_RKEY_F)
+                       if (sess->flags & RTRS_MSG_NEW_RKEY_F)
                                return  rtrs_clt_recv_done(con, wc);
                } else if (imm_type == RTRS_HB_ACK_IMM) {
                        WARN_ON(con->c.cid);
                        sess->s.hb_missed_cnt = 0;
-                       if (sess->flags == RTRS_MSG_NEW_RKEY_F)
+                       if (sess->flags & RTRS_MSG_NEW_RKEY_F)
                                return  rtrs_clt_recv_done(con, wc);
                } else {
                        rtrs_wrn(con->c.sess, "Unknown IMM type %u\n",
@@ -654,7 +656,7 @@ static void rtrs_clt_rdma_done(struct ib_cq *cq, struct ib_wc *wc)
                WARN_ON(!(wc->wc_flags & IB_WC_WITH_INVALIDATE ||
                          wc->wc_flags & IB_WC_WITH_IMM));
                WARN_ON(wc->wr_cqe->done != rtrs_clt_rdma_done);
-               if (sess->flags == RTRS_MSG_NEW_RKEY_F) {
+               if (sess->flags & RTRS_MSG_NEW_RKEY_F) {
                        if (wc->wc_flags & IB_WC_WITH_INVALIDATE)
                                return  rtrs_clt_recv_done(con, wc);
 
@@ -664,7 +666,6 @@ static void rtrs_clt_rdma_done(struct ib_cq *cq, struct ib_wc *wc)
        case IB_WC_RDMA_WRITE:
                /*
                 * post_send() RDMA write completions of IO reqs (read/write)
-                * and hb
                 */
                break;
 
@@ -680,7 +681,7 @@ static int post_recv_io(struct rtrs_clt_con *con, size_t q_size)
        struct rtrs_clt_sess *sess = to_clt_sess(con->c.sess);
 
        for (i = 0; i < q_size; i++) {
-               if (sess->flags == RTRS_MSG_NEW_RKEY_F) {
+               if (sess->flags & RTRS_MSG_NEW_RKEY_F) {
                        struct rtrs_iu *iu = &con->rsp_ius[i];
 
                        err = rtrs_iu_post_recv(&con->c, iu);
@@ -1318,6 +1319,12 @@ out_err:
 
 static void free_permits(struct rtrs_clt *clt)
 {
+       if (clt->permits_map) {
+               size_t sz = clt->queue_depth;
+
+               wait_event(clt->permits_wait,
+                          find_first_bit(clt->permits_map, sz) >= sz);
+       }
        kfree(clt->permits_map);
        clt->permits_map = NULL;
        kfree(clt->permits);
@@ -1353,21 +1360,14 @@ static bool rtrs_clt_change_state_get_old(struct rtrs_clt_sess *sess,
        bool changed;
 
        spin_lock_irq(&sess->state_wq.lock);
-       *old_state = sess->state;
-       changed = __rtrs_clt_change_state(sess, new_state);
+       if (old_state)
+               *old_state = sess->state;
+       changed = rtrs_clt_change_state(sess, new_state);
        spin_unlock_irq(&sess->state_wq.lock);
 
        return changed;
 }
 
-static bool rtrs_clt_change_state(struct rtrs_clt_sess *sess,
-                                  enum rtrs_clt_state new_state)
-{
-       enum rtrs_clt_state old_state;
-
-       return rtrs_clt_change_state_get_old(sess, new_state, &old_state);
-}
-
 static void rtrs_clt_hb_err_handler(struct rtrs_con *c)
 {
        struct rtrs_clt_con *con = container_of(c, typeof(*con), c);
@@ -1511,7 +1511,7 @@ static void destroy_con(struct rtrs_clt_con *con)
 static int create_con_cq_qp(struct rtrs_clt_con *con)
 {
        struct rtrs_clt_sess *sess = to_clt_sess(con->c.sess);
-       u16 wr_queue_size;
+       u32 max_send_wr, max_recv_wr, cq_size;
        int err, cq_vector;
        struct rtrs_msg_rkey_rsp *rsp;
 
@@ -1523,7 +1523,8 @@ static int create_con_cq_qp(struct rtrs_clt_con *con)
                 * + 2 for drain and heartbeat
                 * in case qp gets into error state
                 */
-               wr_queue_size = SERVICE_CON_QUEUE_DEPTH * 3 + 2;
+               max_send_wr = SERVICE_CON_QUEUE_DEPTH * 2 + 2;
+               max_recv_wr = SERVICE_CON_QUEUE_DEPTH * 2 + 2;
                /* We must be the first here */
                if (WARN_ON(sess->s.dev))
                        return -EINVAL;
@@ -1555,25 +1556,29 @@ static int create_con_cq_qp(struct rtrs_clt_con *con)
 
                /* Shared between connections */
                sess->s.dev_ref++;
-               wr_queue_size =
+               max_send_wr =
                        min_t(int, sess->s.dev->ib_dev->attrs.max_qp_wr,
                              /* QD * (REQ + RSP + FR REGS or INVS) + drain */
                              sess->queue_depth * 3 + 1);
+               max_recv_wr =
+                       min_t(int, sess->s.dev->ib_dev->attrs.max_qp_wr,
+                             sess->queue_depth * 3 + 1);
        }
        /* alloc iu to recv new rkey reply when server reports flags set */
-       if (sess->flags == RTRS_MSG_NEW_RKEY_F || con->c.cid == 0) {
-               con->rsp_ius = rtrs_iu_alloc(wr_queue_size, sizeof(*rsp),
+       if (sess->flags & RTRS_MSG_NEW_RKEY_F || con->c.cid == 0) {
+               con->rsp_ius = rtrs_iu_alloc(max_recv_wr, sizeof(*rsp),
                                              GFP_KERNEL, sess->s.dev->ib_dev,
                                              DMA_FROM_DEVICE,
                                              rtrs_clt_rdma_done);
                if (!con->rsp_ius)
                        return -ENOMEM;
-               con->queue_size = wr_queue_size;
+               con->queue_size = max_recv_wr;
        }
+       cq_size = max_send_wr + max_recv_wr;
        cq_vector = con->cpu % sess->s.dev->ib_dev->num_comp_vectors;
        err = rtrs_cq_qp_create(&sess->s, &con->c, sess->max_send_sge,
-                                cq_vector, wr_queue_size, wr_queue_size,
-                                IB_POLL_SOFTIRQ);
+                                cq_vector, cq_size, max_send_wr,
+                                max_recv_wr, IB_POLL_SOFTIRQ);
        /*
         * In case of error we do not bother to clean previous allocations,
         * since destroy_con_cq_qp() must be called.
@@ -1657,6 +1662,7 @@ static int rtrs_rdma_route_resolved(struct rtrs_clt_con *con)
                .cid_num = cpu_to_le16(sess->s.con_num),
                .recon_cnt = cpu_to_le16(sess->s.recon_cnt),
        };
+       msg.first_conn = sess->for_new_clt ? FIRST_CONN : 0;
        uuid_copy(&msg.sess_uuid, &sess->s.uuid);
        uuid_copy(&msg.paths_uuid, &clt->paths_uuid);
 
@@ -1742,6 +1748,8 @@ static int rtrs_rdma_conn_established(struct rtrs_clt_con *con,
                scnprintf(sess->hca_name, sizeof(sess->hca_name),
                          sess->s.dev->ib_dev->name);
                sess->s.src_addr = con->c.cm_id->route.addr.src_addr;
+               /* set for_new_clt, to allow future reconnect on any path */
+               sess->for_new_clt = 1;
        }
 
        return 0;
@@ -1788,7 +1796,7 @@ static int rtrs_rdma_conn_rejected(struct rtrs_clt_con *con,
 
 static void rtrs_clt_close_conns(struct rtrs_clt_sess *sess, bool wait)
 {
-       if (rtrs_clt_change_state(sess, RTRS_CLT_CLOSING))
+       if (rtrs_clt_change_state_get_old(sess, RTRS_CLT_CLOSING, NULL))
                queue_work(rtrs_wq, &sess->close_work);
        if (wait)
                flush_work(&sess->close_work);
@@ -2174,7 +2182,7 @@ static void rtrs_clt_close_work(struct work_struct *work)
 
        cancel_delayed_work_sync(&sess->reconnect_dwork);
        rtrs_clt_stop_and_destroy_conns(sess);
-       rtrs_clt_change_state(sess, RTRS_CLT_CLOSED);
+       rtrs_clt_change_state_get_old(sess, RTRS_CLT_CLOSED, NULL);
 }
 
 static int init_conns(struct rtrs_clt_sess *sess)
@@ -2226,14 +2234,14 @@ destroy:
         * doing rdma_resolve_addr(), switch to CONNECTION_ERR state
         * manually to keep reconnecting.
         */
-       rtrs_clt_change_state(sess, RTRS_CLT_CONNECTING_ERR);
+       rtrs_clt_change_state_get_old(sess, RTRS_CLT_CONNECTING_ERR, NULL);
 
        return err;
 }
 
 static void rtrs_clt_info_req_done(struct ib_cq *cq, struct ib_wc *wc)
 {
-       struct rtrs_clt_con *con = cq->cq_context;
+       struct rtrs_clt_con *con = to_clt_con(wc->qp->qp_context);
        struct rtrs_clt_sess *sess = to_clt_sess(con->c.sess);
        struct rtrs_iu *iu;
 
@@ -2243,7 +2251,7 @@ static void rtrs_clt_info_req_done(struct ib_cq *cq, struct ib_wc *wc)
        if (unlikely(wc->status != IB_WC_SUCCESS)) {
                rtrs_err(sess->clt, "Sess info request send failed: %s\n",
                          ib_wc_status_msg(wc->status));
-               rtrs_clt_change_state(sess, RTRS_CLT_CONNECTING_ERR);
+               rtrs_clt_change_state_get_old(sess, RTRS_CLT_CONNECTING_ERR, NULL);
                return;
        }
 
@@ -2315,7 +2323,7 @@ static int process_info_rsp(struct rtrs_clt_sess *sess,
 
 static void rtrs_clt_info_rsp_done(struct ib_cq *cq, struct ib_wc *wc)
 {
-       struct rtrs_clt_con *con = cq->cq_context;
+       struct rtrs_clt_con *con = to_clt_con(wc->qp->qp_context);
        struct rtrs_clt_sess *sess = to_clt_sess(con->c.sess);
        struct rtrs_msg_info_rsp *msg;
        enum rtrs_clt_state state;
@@ -2367,7 +2375,7 @@ static void rtrs_clt_info_rsp_done(struct ib_cq *cq, struct ib_wc *wc)
 out:
        rtrs_clt_update_wc_stats(con);
        rtrs_iu_free(iu, sess->s.dev->ib_dev, 1);
-       rtrs_clt_change_state(sess, state);
+       rtrs_clt_change_state_get_old(sess, state, NULL);
 }
 
 static int rtrs_send_sess_info(struct rtrs_clt_sess *sess)
@@ -2423,7 +2431,6 @@ static int rtrs_send_sess_info(struct rtrs_clt_sess *sess)
                        err = -ECONNRESET;
                else
                        err = -ETIMEDOUT;
-               goto out;
        }
 
 out:
@@ -2433,7 +2440,7 @@ out:
                rtrs_iu_free(rx_iu, sess->s.dev->ib_dev, 1);
        if (unlikely(err))
                /* If we've never taken async path because of malloc problems */
-               rtrs_clt_change_state(sess, RTRS_CLT_CONNECTING_ERR);
+               rtrs_clt_change_state_get_old(sess, RTRS_CLT_CONNECTING_ERR, NULL);
 
        return err;
 }
@@ -2490,7 +2497,7 @@ static void rtrs_clt_reconnect_work(struct work_struct *work)
        /* Stop everything */
        rtrs_clt_stop_and_destroy_conns(sess);
        msleep(RTRS_RECONNECT_BACKOFF);
-       if (rtrs_clt_change_state(sess, RTRS_CLT_CONNECTING)) {
+       if (rtrs_clt_change_state_get_old(sess, RTRS_CLT_CONNECTING, NULL)) {
                err = init_sess(sess);
                if (err)
                        goto reconnect_again;
@@ -2499,7 +2506,7 @@ static void rtrs_clt_reconnect_work(struct work_struct *work)
        return;
 
 reconnect_again:
-       if (rtrs_clt_change_state(sess, RTRS_CLT_RECONNECTING)) {
+       if (rtrs_clt_change_state_get_old(sess, RTRS_CLT_RECONNECTING, NULL)) {
                sess->stats->reconnects.fail_cnt++;
                delay_ms = clt->reconnect_delay_sec * 1000;
                queue_delayed_work(rtrs_wq, &sess->reconnect_dwork,
@@ -2565,11 +2572,8 @@ static struct rtrs_clt *alloc_clt(const char *sessname, size_t paths_num,
        clt->dev.class = rtrs_clt_dev_class;
        clt->dev.release = rtrs_clt_dev_release;
        err = dev_set_name(&clt->dev, "%s", sessname);
-       if (err) {
-               free_percpu(clt->pcpu_path);
-               kfree(clt);
-               return ERR_PTR(err);
-       }
+       if (err)
+               goto err;
        /*
         * Suppress user space notification until
         * sysfs files are created
@@ -2577,44 +2581,35 @@ static struct rtrs_clt *alloc_clt(const char *sessname, size_t paths_num,
        dev_set_uevent_suppress(&clt->dev, true);
        err = device_register(&clt->dev);
        if (err) {
-               free_percpu(clt->pcpu_path);
                put_device(&clt->dev);
-               return ERR_PTR(err);
+               goto err;
        }
 
        clt->kobj_paths = kobject_create_and_add("paths", &clt->dev.kobj);
        if (!clt->kobj_paths) {
-               free_percpu(clt->pcpu_path);
-               device_unregister(&clt->dev);
-               return NULL;
+               err = -ENOMEM;
+               goto err_dev;
        }
        err = rtrs_clt_create_sysfs_root_files(clt);
        if (err) {
-               free_percpu(clt->pcpu_path);
                kobject_del(clt->kobj_paths);
                kobject_put(clt->kobj_paths);
-               device_unregister(&clt->dev);
-               return ERR_PTR(err);
+               goto err_dev;
        }
        dev_set_uevent_suppress(&clt->dev, false);
        kobject_uevent(&clt->dev.kobj, KOBJ_ADD);
 
        return clt;
-}
-
-static void wait_for_inflight_permits(struct rtrs_clt *clt)
-{
-       if (clt->permits_map) {
-               size_t sz = clt->queue_depth;
-
-               wait_event(clt->permits_wait,
-                          find_first_bit(clt->permits_map, sz) >= sz);
-       }
+err_dev:
+       device_unregister(&clt->dev);
+err:
+       free_percpu(clt->pcpu_path);
+       kfree(clt);
+       return ERR_PTR(err);
 }
 
 static void free_clt(struct rtrs_clt *clt)
 {
-       wait_for_inflight_permits(clt);
        free_permits(clt);
        free_percpu(clt->pcpu_path);
        mutex_destroy(&clt->paths_ev_mutex);
@@ -2672,6 +2667,8 @@ struct rtrs_clt *rtrs_clt_open(struct rtrs_clt_ops *ops,
                        err = PTR_ERR(sess);
                        goto close_all_sess;
                }
+               if (!i)
+                       sess->for_new_clt = 1;
                list_add_tail_rcu(&sess->s.entry, &clt->paths_list);
 
                err = init_sess(sess);
@@ -2702,8 +2699,7 @@ close_all_sess:
                rtrs_clt_close_conns(sess, true);
                kobject_put(&sess->kobj);
        }
-       rtrs_clt_destroy_sysfs_root_files(clt);
-       rtrs_clt_destroy_sysfs_root_folders(clt);
+       rtrs_clt_destroy_sysfs_root(clt);
        free_clt(clt);
 
 out:
@@ -2720,8 +2716,7 @@ void rtrs_clt_close(struct rtrs_clt *clt)
        struct rtrs_clt_sess *sess, *tmp;
 
        /* Firstly forbid sysfs access */
-       rtrs_clt_destroy_sysfs_root_files(clt);
-       rtrs_clt_destroy_sysfs_root_folders(clt);
+       rtrs_clt_destroy_sysfs_root(clt);
 
        /* Now it is safe to iterate over all paths without locks */
        list_for_each_entry_safe(sess, tmp, &clt->paths_list, s.entry) {