* RE: [openib-general] Re: [PATCH 1/2] iWARP Connection Manager.
@ 2006-06-01 22:28 Caitlin Bestler
2006-06-02 13:57 ` Steve Wise
0 siblings, 1 reply; 4+ messages in thread
From: Caitlin Bestler @ 2006-06-01 22:28 UTC (permalink / raw)
To: Tom Tucker, Sean Hefty
Cc: Steve Wise, netdev, rdreier, linux-kernel, openib-general
>>
>> There's a difference between trying to handle the user calling
>> disconnect/destroy at the same time a call to accept/connect is
>> active, versus the user calling disconnect/destroy after
>> accept/connect have returned. In the latter case, I think you're
>> fine. In the first case, this is allowing a user to call
> destroy at the same time that they're calling accept/connect.
>> Additionally, there's no guarantee that the F_CONNECT_WAIT flag has
>> been set by accept/connect by the time disconnect/destroy tests it.
>
> The problem is that we can't synchronously cancel an
> outstanding connect request. Once we've asked the adapter to
> connect, we can't tell him to stop, we have to wait for it to
> fail. During the time period between when we ask to connect
> and the adapter says yeah-or-nay, the user hits ctrl-C. This
> is the case where disconnect and/or destroy gets called and
> we have to block it waiting for the outstanding connect
> request to complete.
>
> One alternative to this approach is to do the kfree of the
> cm_id in the deref logic. This was the original design and
> leaves the object around to handle the completion of the
> connect and still allows the app to clean up and go away
> without all this waitin' around. When the adapter finally
> finishes and releases it's reference, the object is kfree'd.
>
> Hope this helps.
>
Why couldn't you synchronously put the cm_id in a state of
"pending delete" and do the actual delete when the RNIC
provides a response to the request? There could even be
an optional method to see if the device is capable of
cancelling the request. I know it can't yank a SYN back
from the wire, but it could refrain from retransmitting.
^ permalink raw reply [flat|nested] 4+ messages in thread* RE: [openib-general] Re: [PATCH 1/2] iWARP Connection Manager. 2006-06-01 22:28 [openib-general] Re: [PATCH 1/2] iWARP Connection Manager Caitlin Bestler @ 2006-06-02 13:57 ` Steve Wise 0 siblings, 0 replies; 4+ messages in thread From: Steve Wise @ 2006-06-02 13:57 UTC (permalink / raw) To: Caitlin Bestler Cc: Tom Tucker, Sean Hefty, netdev, rdreier, linux-kernel, openib-general > > > > The problem is that we can't synchronously cancel an > > outstanding connect request. Once we've asked the adapter to > > connect, we can't tell him to stop, we have to wait for it to > > fail. During the time period between when we ask to connect > > and the adapter says yeah-or-nay, the user hits ctrl-C. This > > is the case where disconnect and/or destroy gets called and > > we have to block it waiting for the outstanding connect > > request to complete. > > > > One alternative to this approach is to do the kfree of the > > cm_id in the deref logic. This was the original design and > > leaves the object around to handle the completion of the > > connect and still allows the app to clean up and go away > > without all this waitin' around. When the adapter finally > > finishes and releases it's reference, the object is kfree'd. > > > > Hope this helps. > > > Why couldn't you synchronously put the cm_id in a state of > "pending delete" and do the actual delete when the RNIC > provides a response to the request? This is Tom's "alternative" mentioned above. The provider already keeps an explicit reference on the cm_id while it might possibly deliver an event on that cm_id. So if you change deref to kfree the cm_id on its last deref (when the refcnt reaches 0), then you can avoid blocking during destroy... > There could even be > an optional method to see if the device is capable of > cancelling the request. I know it can't yank a SYN back > from the wire, but it could refrain from retransmitting. I would suggest we don't add this optional method until we see an RNIC that supports canceling a connect request or accept synchronously... Steve. ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 0/2][RFC] iWARP Core Support
@ 2006-05-31 18:26 Steve Wise
2006-05-31 18:26 ` [PATCH 1/2] iWARP Connection Manager Steve Wise
0 siblings, 1 reply; 4+ messages in thread
From: Steve Wise @ 2006-05-31 18:26 UTC (permalink / raw)
To: rdreier, mshefty; +Cc: linux-kernel, netdev, openib-general
This patchset defines the modifications to the Linux infiniband subsystem
to support iWARP devices. We're submitting it for review now with the
goal for inclusion in the 2.6.19 kernel. This code has gone through
several reviews in the openib-general list. Now we are submitting it
for external review by the linux community.
This StGIT patchset is cloned from Roland Dreier's infiniband.git
for-2.6.18 branch. The patchset consists of 2 patches:
1 - New iWARP CM implementation.
2 - Core changes to support iWARP.
Signed-off-by: Tom Tucker <tom@opengridcomputing.com>
Signed-off-by: Steve Wise <swise@opengridcomputing.com>
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 1/2] iWARP Connection Manager. 2006-05-31 18:26 [PATCH 0/2][RFC] iWARP Core Support Steve Wise @ 2006-05-31 18:26 ` Steve Wise 2006-05-31 22:22 ` Sean Hefty 0 siblings, 1 reply; 4+ messages in thread From: Steve Wise @ 2006-05-31 18:26 UTC (permalink / raw) To: rdreier, mshefty; +Cc: linux-kernel, netdev, openib-general This patch provides the new files implementing the iWARP Connection Manager. --- drivers/infiniband/core/iwcm.c | 887 ++++++++++++++++++++++++++++++++++++++++ include/rdma/iw_cm.h | 254 +++++++++++ include/rdma/iw_cm_private.h | 62 +++ 3 files changed, 1203 insertions(+), 0 deletions(-) diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c new file mode 100644 index 0000000..5657ee8 --- /dev/null +++ b/drivers/infiniband/core/iwcm.c @@ -0,0 +1,887 @@ +/* + * Copyright (c) 2004, 2005 Intel Corporation. All rights reserved. + * Copyright (c) 2004 Topspin Corporation. All rights reserved. + * Copyright (c) 2004, 2005 Voltaire Corporation. All rights reserved. + * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. + * Copyright (c) 2005 Network Appliance, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/idr.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/rbtree.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <rdma/iw_cm.h> +#include <rdma/iw_cm_private.h> +#include <rdma/ib_addr.h> + +MODULE_AUTHOR("Tom Tucker"); +MODULE_DESCRIPTION("iWARP CM"); +MODULE_LICENSE("Dual BSD/GPL"); + +static struct workqueue_struct *iwcm_wq; +struct iwcm_work { + struct work_struct work; + struct iwcm_id_private *cm_id; + struct list_head list; + struct iw_cm_event event; +}; + +/* + * Release a reference on cm_id. If the last reference is being removed + * and iw_destroy_cm_id is waiting, wake up the waiting thread. + */ +static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv) +{ + int ret = 0; + + BUG_ON(atomic_read(&cm_id_priv->refcount)==0); + if (atomic_dec_and_test(&cm_id_priv->refcount)) { + BUG_ON(!list_empty(&cm_id_priv->work_list)); + if (waitqueue_active(&cm_id_priv->destroy_wait)) { + BUG_ON(cm_id_priv->state != IW_CM_STATE_DESTROYING); + BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, + &cm_id_priv->flags)); + ret = 1; + wake_up(&cm_id_priv->destroy_wait); + } + } + + return ret; +} + +static void add_ref(struct iw_cm_id *cm_id) +{ + struct iwcm_id_private *cm_id_priv; + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + atomic_inc(&cm_id_priv->refcount); +} + +static void rem_ref(struct iw_cm_id *cm_id) +{ + struct iwcm_id_private *cm_id_priv; + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + iwcm_deref_id(cm_id_priv); +} + +static void cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event); + +struct iw_cm_id *iw_create_cm_id(struct ib_device *device, + iw_cm_handler cm_handler, + void *context) +{ + struct iwcm_id_private *cm_id_priv; + + cm_id_priv = kzalloc(sizeof *cm_id_priv, GFP_KERNEL); + if (!cm_id_priv) + return ERR_PTR(-ENOMEM); + + cm_id_priv->state = IW_CM_STATE_IDLE; + cm_id_priv->id.device = device; + cm_id_priv->id.cm_handler = cm_handler; + cm_id_priv->id.context = context; + cm_id_priv->id.event_handler = cm_event_handler; + cm_id_priv->id.add_ref = add_ref; + cm_id_priv->id.rem_ref = rem_ref; + spin_lock_init(&cm_id_priv->lock); + atomic_set(&cm_id_priv->refcount, 1); + init_waitqueue_head(&cm_id_priv->connect_wait); + init_waitqueue_head(&cm_id_priv->destroy_wait); + INIT_LIST_HEAD(&cm_id_priv->work_list); + + return &cm_id_priv->id; +} +EXPORT_SYMBOL(iw_create_cm_id); + + +static int iwcm_modify_qp_err(struct ib_qp *qp) +{ + struct ib_qp_attr qp_attr; + + if (!qp) + return -EINVAL; + + qp_attr.qp_state = IB_QPS_ERR; + return ib_modify_qp(qp, &qp_attr, IB_QP_STATE); +} + +/* + * This is really the RDMAC CLOSING state. It is most similar to the + * IB SQD QP state. + */ +static int iwcm_modify_qp_sqd(struct ib_qp *qp) +{ + struct ib_qp_attr qp_attr; + + BUG_ON(qp == NULL); + qp_attr.qp_state = IB_QPS_SQD; + return ib_modify_qp(qp, &qp_attr, IB_QP_STATE); +} + +/* + * CM_ID <-- CLOSING + * + * Block if a passive or active connection is currenlty being processed. Then + * process the event as follows: + * - If we are ESTABLISHED, move to CLOSING and modify the QP state + * based on the abrupt flag + * - If the connection is already in the CLOSING or IDLE state, the peer is + * disconnecting concurrently with us and we've already seen the + * DISCONNECT event -- ignore the request and return 0 + * - Disconnect on a listening endpoint returns -EINVAL + */ +int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt) +{ + struct iwcm_id_private *cm_id_priv; + unsigned long flags; + int ret = 0; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + /* Wait if we're currently in a connect or accept downcall */ + wait_event(cm_id_priv->connect_wait, + !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); + + spin_lock_irqsave(&cm_id_priv->lock, flags); + switch (cm_id_priv->state) { + case IW_CM_STATE_ESTABLISHED: + cm_id_priv->state = IW_CM_STATE_CLOSING; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + if (cm_id_priv->qp) { /* QP could be <nul> for user-mode client */ + if (abrupt) + ret = iwcm_modify_qp_err(cm_id_priv->qp); + else + ret = iwcm_modify_qp_sqd(cm_id_priv->qp); + /* + * If both sides are disconnecting the QP could + * already be in ERR or SQD states + */ + ret = 0; + } + else + ret = -EINVAL; + break; + case IW_CM_STATE_LISTEN: + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + ret = -EINVAL; + break; + case IW_CM_STATE_CLOSING: + /* remote peer closed first */ + case IW_CM_STATE_IDLE: + /* accept or connect returned !0 */ + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + break; + case IW_CM_STATE_CONN_RECV: + /* + * App called disconnect before/without calling accept after + * connect_request event delivered. + */ + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + break; + case IW_CM_STATE_CONN_SENT: + /* Can only get here if wait above fails */ + default: + BUG_ON(1); + } + + return ret; +} +EXPORT_SYMBOL(iw_cm_disconnect); + +/* + * CM_ID <-- DESTROYING + * + * Clean up all resources associated with the connection and release + * the initial reference taken by iw_create_cm_id. + */ +static void destroy_cm_id(struct iw_cm_id *cm_id) +{ + struct iwcm_id_private *cm_id_priv; + unsigned long flags; + int ret; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + /* Wait if we're currently in a connect or accept downcall. A + * listening endpoint should never block here. */ + wait_event(cm_id_priv->connect_wait, + !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); + + spin_lock_irqsave(&cm_id_priv->lock, flags); + switch (cm_id_priv->state) { + case IW_CM_STATE_LISTEN: + cm_id_priv->state = IW_CM_STATE_DESTROYING; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + /* destroy the listening endpoint */ + ret = cm_id->device->iwcm->destroy_listen(cm_id); + break; + case IW_CM_STATE_ESTABLISHED: + cm_id_priv->state = IW_CM_STATE_DESTROYING; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + /* Abrupt close of the connection */ + (void)iwcm_modify_qp_err(cm_id_priv->qp); + break; + case IW_CM_STATE_IDLE: + case IW_CM_STATE_CLOSING: + cm_id_priv->state = IW_CM_STATE_DESTROYING; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + break; + case IW_CM_STATE_CONN_RECV: + /* + * App called destroy before/without calling accept after + * receiving connection request event notification. + */ + cm_id_priv->state = IW_CM_STATE_DESTROYING; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + break; + case IW_CM_STATE_CONN_SENT: + case IW_CM_STATE_DESTROYING: + default: + BUG_ON(1); + break; + } + + spin_lock_irqsave(&cm_id_priv->lock, flags); + if (cm_id_priv->qp) { + cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); + cm_id_priv->qp = NULL; + } + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + + (void)iwcm_deref_id(cm_id_priv); +} + +/* + * This function is only called by the application thread and cannot + * be called by the event thread. The function will wait for all + * references to be released on the cm_id and then kfree the cm_id + * object. + */ +void iw_destroy_cm_id(struct iw_cm_id *cm_id) +{ + struct iwcm_id_private *cm_id_priv; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)); + + destroy_cm_id(cm_id); + + wait_event(cm_id_priv->destroy_wait, + !atomic_read(&cm_id_priv->refcount)); + + kfree(cm_id_priv); +} +EXPORT_SYMBOL(iw_destroy_cm_id); + +/* + * CM_ID <-- LISTEN + * + * Start listening for connect requests. Generates one CONNECT_REQUEST + * event for each inbound connect request. + */ +int iw_cm_listen(struct iw_cm_id *cm_id, int backlog) +{ + struct iwcm_id_private *cm_id_priv; + unsigned long flags; + int ret = 0; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + spin_lock_irqsave(&cm_id_priv->lock, flags); + switch (cm_id_priv->state) { + case IW_CM_STATE_IDLE: + cm_id_priv->state = IW_CM_STATE_LISTEN; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + ret = cm_id->device->iwcm->create_listen(cm_id, backlog); + if (ret) + cm_id_priv->state = IW_CM_STATE_IDLE; + break; + default: + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(iw_cm_listen); + +/* + * CM_ID <-- IDLE + * + * Rejects an inbound connection request. No events are generated. + */ +int iw_cm_reject(struct iw_cm_id *cm_id, + const void *private_data, + u8 private_data_len) +{ + struct iwcm_id_private *cm_id_priv; + unsigned long flags; + int ret; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + + spin_lock_irqsave(&cm_id_priv->lock, flags); + if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + wake_up_all(&cm_id_priv->connect_wait); + return -EINVAL; + } + cm_id_priv->state = IW_CM_STATE_IDLE; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + + ret = cm_id->device->iwcm->reject(cm_id, private_data, + private_data_len); + + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + wake_up_all(&cm_id_priv->connect_wait); + + return ret; +} +EXPORT_SYMBOL(iw_cm_reject); + +/* + * CM_ID <-- ESTABLISHED + * + * Accepts an inbound connection request and generates an ESTABLISHED + * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block + * until the ESTABLISHED event is received from the provider. + */ +int iw_cm_accept(struct iw_cm_id *cm_id, + struct iw_cm_conn_param *iw_param) +{ + struct iwcm_id_private *cm_id_priv; + struct ib_qp *qp; + unsigned long flags; + int ret; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + + spin_lock_irqsave(&cm_id_priv->lock, flags); + if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + wake_up_all(&cm_id_priv->connect_wait); + return -EINVAL; + } + /* Get the ib_qp given the QPN */ + qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); + if (!qp) { + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + return -EINVAL; + } + cm_id->device->iwcm->add_ref(qp); + cm_id_priv->qp = qp; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + + ret = cm_id->device->iwcm->accept(cm_id, iw_param); + if (ret) { + /* An error on accept precludes provider events */ + BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV); + cm_id_priv->state = IW_CM_STATE_IDLE; + spin_lock_irqsave(&cm_id_priv->lock, flags); + if (cm_id_priv->qp) { + cm_id->device->iwcm->rem_ref(qp); + cm_id_priv->qp = NULL; + } + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + printk("Accept failed, ret=%d\n", ret); + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + wake_up_all(&cm_id_priv->connect_wait); + } + + return ret; +} +EXPORT_SYMBOL(iw_cm_accept); + +/* + * Active Side: CM_ID <-- CONN_SENT + * + * If successful, results in the generation of a CONNECT_REPLY + * event. iw_cm_disconnect and iw_cm_destroy will block until the + * CONNECT_REPLY event is received from the provider. + */ +int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) +{ + struct iwcm_id_private *cm_id_priv; + int ret = 0; + unsigned long flags; + struct ib_qp *qp; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + + spin_lock_irqsave(&cm_id_priv->lock, flags); + if (cm_id_priv->state != IW_CM_STATE_IDLE) { + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + wake_up_all(&cm_id_priv->connect_wait); + return -EINVAL; + } + + /* Get the ib_qp given the QPN */ + qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); + if (!qp) { + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + return -EINVAL; + } + cm_id->device->iwcm->add_ref(qp); + cm_id_priv->qp = qp; + cm_id_priv->state = IW_CM_STATE_CONN_SENT; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + + ret = cm_id->device->iwcm->connect(cm_id, iw_param); + if (ret) { + spin_lock_irqsave(&cm_id_priv->lock, flags); + if (cm_id_priv->qp) { + cm_id->device->iwcm->rem_ref(qp); + cm_id_priv->qp = NULL; + } + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT); + cm_id_priv->state = IW_CM_STATE_IDLE; + printk("Connect failed, ret=%d\n", ret); + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + wake_up_all(&cm_id_priv->connect_wait); + } + + return ret; +} +EXPORT_SYMBOL(iw_cm_connect); + +/* + * Passive Side: new CM_ID <-- CONN_RECV + * + * Handles an inbound connect request. The function creates a new + * iw_cm_id to represent the new connection and inherits the client + * callback function and other attributes from the listening parent. + * + * The work item contains a pointer to the listen_cm_id and the event. The + * listen_cm_id contains the client cm_handler, context and + * device. These are copied when the device is cloned. The event + * contains the new four tuple. + * + * An error on the child should not affect the parent, so this + * function does not return a value. + */ +static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv, + struct iw_cm_event *iw_event) +{ + unsigned long flags; + struct iw_cm_id *cm_id; + struct iwcm_id_private *cm_id_priv; + int ret; + + /* The provider should never generate a connection request + * event with a bad status. + */ + BUG_ON(iw_event->status); + + /* We could be destroying the listening id. If so, ignore this + * upcall. */ + spin_lock_irqsave(&listen_id_priv->lock, flags); + if (listen_id_priv->state != IW_CM_STATE_LISTEN) { + spin_unlock_irqrestore(&listen_id_priv->lock, flags); + return; + } + spin_unlock_irqrestore(&listen_id_priv->lock, flags); + + cm_id = iw_create_cm_id(listen_id_priv->id.device, + listen_id_priv->id.cm_handler, + listen_id_priv->id.context); + /* If the cm_id could not be created, ignore the request */ + if (IS_ERR(cm_id)) + return; + + cm_id->provider_data = iw_event->provider_data; + cm_id->local_addr = iw_event->local_addr; + cm_id->remote_addr = iw_event->remote_addr; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + cm_id_priv->state = IW_CM_STATE_CONN_RECV; + + /* Call the client CM handler */ + ret = cm_id->cm_handler(cm_id, iw_event); + if (ret) { + printk("destroying child id %p, ret=%d\n", + cm_id, ret); + set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); + destroy_cm_id(cm_id); + if (atomic_read(&cm_id_priv->refcount)==0) + kfree(cm_id); + } +} + +/* + * Passive Side: CM_ID <-- ESTABLISHED + * + * The provider generated an ESTABLISHED event which means that + * the MPA negotion has completed successfully and we are now in MPA + * FPDU mode. + * + * This event can only be received in the CONN_RECV state. If the + * remote peer closed, the ESTABLISHED event would be received followed + * by the CLOSE event. If the app closes, it will block until we wake + * it up after processing this event. + */ +static int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv, + struct iw_cm_event *iw_event) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&cm_id_priv->lock, flags); + + /* We clear the CONNECT_WAIT bit here to allow the callback + * function to call iw_cm_disconnect. Calling iw_destroy_cm_id + * from a callback handler is not allowed */ + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + switch (cm_id_priv->state) { + case IW_CM_STATE_CONN_RECV: + cm_id_priv->state = IW_CM_STATE_ESTABLISHED; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); + break; + default: + BUG_ON(1); + } + wake_up_all(&cm_id_priv->connect_wait); + + return ret; +} + +/* + * Active Side: CM_ID <-- ESTABLISHED + * + * The app has called connect and is waiting for the established event to + * post it's requests to the server. This event will wake up anyone + * blocked in iw_cm_disconnect or iw_destroy_id. + */ +static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv, + struct iw_cm_event *iw_event) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&cm_id_priv->lock, flags); + /* Clear the connect wait bit so a callback function calling + * iw_cm_disconnect will not wait and deadlock this thread */ + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); + switch (cm_id_priv->state) { + case IW_CM_STATE_CONN_SENT: + if (iw_event->status == IW_CM_EVENT_STATUS_ACCEPTED) { + cm_id_priv->id.local_addr = iw_event->local_addr; + cm_id_priv->id.remote_addr = iw_event->remote_addr; + cm_id_priv->state = IW_CM_STATE_ESTABLISHED; + } else { + /* REJECTED or RESET */ + cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); + cm_id_priv->qp = NULL; + cm_id_priv->state = IW_CM_STATE_IDLE; + } + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); + break; + default: + BUG_ON(1); + } + /* Wake up waiters on connect complete */ + wake_up_all(&cm_id_priv->connect_wait); + + return ret; +} + +/* + * CM_ID <-- CLOSING + * + * If in the ESTABLISHED state, move to CLOSING. + */ +static void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv, + struct iw_cm_event *iw_event) +{ + unsigned long flags; + + spin_lock_irqsave(&cm_id_priv->lock, flags); + if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED) + cm_id_priv->state = IW_CM_STATE_CLOSING; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); +} + +/* + * CM_ID <-- IDLE + * + * If in the ESTBLISHED or CLOSING states, the QP will have have been + * moved by the provider to the ERR state. Disassociate the CM_ID from + * the QP, move to IDLE, and remove the 'connected' reference. + * + * If in some other state, the cm_id was destroyed asynchronously. + * This is the last reference that will result in waking up + * the app thread blocked in iw_destroy_cm_id. + */ +static int cm_close_handler(struct iwcm_id_private *cm_id_priv, + struct iw_cm_event *iw_event) +{ + unsigned long flags; + int ret = 0; + /* TT */printk("%s:%d cm_id_priv=%p, state=%d\n", + __FUNCTION__, __LINE__, + cm_id_priv,cm_id_priv->state); + spin_lock_irqsave(&cm_id_priv->lock, flags); + + if (cm_id_priv->qp) { + cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); + cm_id_priv->qp = NULL; + } + switch (cm_id_priv->state) { + case IW_CM_STATE_ESTABLISHED: + case IW_CM_STATE_CLOSING: + cm_id_priv->state = IW_CM_STATE_IDLE; + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); + break; + case IW_CM_STATE_DESTROYING: + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + break; + default: + BUG_ON(1); + } + + return ret; +} + +static int process_event(struct iwcm_id_private *cm_id_priv, + struct iw_cm_event *iw_event) +{ + int ret = 0; + + switch (iw_event->event) { + case IW_CM_EVENT_CONNECT_REQUEST: + cm_conn_req_handler(cm_id_priv, iw_event); + break; + case IW_CM_EVENT_CONNECT_REPLY: + ret = cm_conn_rep_handler(cm_id_priv, iw_event); + break; + case IW_CM_EVENT_ESTABLISHED: + ret = cm_conn_est_handler(cm_id_priv, iw_event); + break; + case IW_CM_EVENT_DISCONNECT: + cm_disconnect_handler(cm_id_priv, iw_event); + break; + case IW_CM_EVENT_CLOSE: + ret = cm_close_handler(cm_id_priv, iw_event); + break; + default: + BUG_ON(1); + } + + return ret; +} + +/* + * Process events on the work_list for the cm_id. If the callback + * function requests that the cm_id be deleted, a flag is set in the + * cm_id flags to indicate that when the last reference is + * removed, the cm_id is to be destroyed. This is necessary to + * distinguish between an object that will be destroyed by the app + * thread asleep on the destroy_wait list vs. an object destroyed + * here synchronously when the last reference is removed. + */ +static void cm_work_handler(void *arg) +{ + struct iwcm_work *work = (struct iwcm_work*)arg; + struct iwcm_id_private *cm_id_priv = work->cm_id; + unsigned long flags; + int empty; + int ret = 0; + + spin_lock_irqsave(&cm_id_priv->lock, flags); + empty = list_empty(&cm_id_priv->work_list); + while (!empty) { + work = list_entry(cm_id_priv->work_list.next, + struct iwcm_work, list); + list_del_init(&work->list); + empty = list_empty(&cm_id_priv->work_list); + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + + ret = process_event(cm_id_priv, &work->event); + kfree(work); + if (ret) { + set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); + destroy_cm_id(&cm_id_priv->id); + } + BUG_ON(atomic_read(&cm_id_priv->refcount)==0); + if (iwcm_deref_id(cm_id_priv)) + return; + + if (atomic_read(&cm_id_priv->refcount)==0 && + test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) { + kfree(cm_id_priv); + return; + } + spin_lock_irqsave(&cm_id_priv->lock, flags); + } + spin_unlock_irqrestore(&cm_id_priv->lock, flags); +} + +/* + * This function is called on interrupt context. Schedule events on + * the iwcm_wq thread to allow callback functions to downcall into + * the CM and/or block. Events are queued to a per-CM_ID + * work_list. If this is the first event on the work_list, the work + * element is also queued on the iwcm_wq thread. + * + * Each event holds a reference on the cm_id. Until the last posted + * event has been delivered and processed, the cm_id cannot be + * deleted. + */ +static void cm_event_handler(struct iw_cm_id *cm_id, + struct iw_cm_event *iw_event) +{ + struct iwcm_work *work; + struct iwcm_id_private *cm_id_priv; + unsigned long flags; + + work = kmalloc(sizeof *work, GFP_ATOMIC); + if (!work) + return; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + atomic_inc(&cm_id_priv->refcount); + + INIT_WORK(&work->work, cm_work_handler, work); + work->cm_id = cm_id_priv; + work->event = *iw_event; + + spin_lock_irqsave(&cm_id_priv->lock, flags); + if (list_empty(&cm_id_priv->work_list)) { + list_add_tail(&work->list, &cm_id_priv->work_list); + queue_work(iwcm_wq, &work->work); + } else + list_add_tail(&work->list, &cm_id_priv->work_list); + spin_unlock_irqrestore(&cm_id_priv->lock, flags); +} + +static int iwcm_init_qp_init_attr(struct iwcm_id_private *cm_id_priv, + struct ib_qp_attr *qp_attr, + int *qp_attr_mask) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&cm_id_priv->lock, flags); + switch (cm_id_priv->state) { + case IW_CM_STATE_IDLE: + case IW_CM_STATE_CONN_SENT: + case IW_CM_STATE_CONN_RECV: + case IW_CM_STATE_ESTABLISHED: + *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS; + qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | + IB_ACCESS_REMOTE_WRITE| + IB_ACCESS_REMOTE_READ; + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + return ret; +} + +static int iwcm_init_qp_rts_attr(struct iwcm_id_private *cm_id_priv, + struct ib_qp_attr *qp_attr, + int *qp_attr_mask) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&cm_id_priv->lock, flags); + switch (cm_id_priv->state) { + case IW_CM_STATE_IDLE: + case IW_CM_STATE_CONN_SENT: + case IW_CM_STATE_CONN_RECV: + case IW_CM_STATE_ESTABLISHED: + *qp_attr_mask = 0; + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + spin_unlock_irqrestore(&cm_id_priv->lock, flags); + return ret; +} + +int iw_cm_init_qp_attr(struct iw_cm_id *cm_id, + struct ib_qp_attr *qp_attr, + int *qp_attr_mask) +{ + struct iwcm_id_private *cm_id_priv; + int ret; + + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); + switch (qp_attr->qp_state) { + case IB_QPS_INIT: + case IB_QPS_RTR: + ret = iwcm_init_qp_init_attr(cm_id_priv, + qp_attr, qp_attr_mask); + break; + case IB_QPS_RTS: + ret = iwcm_init_qp_rts_attr(cm_id_priv, + qp_attr, qp_attr_mask); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +EXPORT_SYMBOL(iw_cm_init_qp_attr); + +static int __init iw_cm_init(void) +{ + iwcm_wq = create_singlethread_workqueue("iw_cm_wq"); + if (!iwcm_wq) + return -ENOMEM; + + return 0; +} + +static void __exit iw_cm_cleanup(void) +{ + destroy_workqueue(iwcm_wq); +} + +module_init(iw_cm_init); +module_exit(iw_cm_cleanup); diff --git a/include/rdma/iw_cm.h b/include/rdma/iw_cm.h new file mode 100644 index 0000000..0752a94 --- /dev/null +++ b/include/rdma/iw_cm.h @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2005 Network Appliance, Inc. All rights reserved. + * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#if !defined(IW_CM_H) +#define IW_CM_H + +#include <linux/in.h> +#include <rdma/ib_cm.h> + +struct iw_cm_id; + +enum iw_cm_event_type { + IW_CM_EVENT_CONNECT_REQUEST = 1, /* connect request received */ + IW_CM_EVENT_CONNECT_REPLY, /* reply from active connect request */ + IW_CM_EVENT_ESTABLISHED, /* passive side accept successful */ + IW_CM_EVENT_DISCONNECT, /* orderly shutdown */ + IW_CM_EVENT_CLOSE /* close complete */ +}; +enum iw_cm_event_status { + IW_CM_EVENT_STATUS_OK = 0, /* request successful */ + IW_CM_EVENT_STATUS_ACCEPTED = 0, /* connect request accepted */ + IW_CM_EVENT_STATUS_REJECTED, /* connect request rejected */ + IW_CM_EVENT_STATUS_TIMEOUT, /* the operation timed out */ + IW_CM_EVENT_STATUS_RESET, /* reset from remote peer */ + IW_CM_EVENT_STATUS_EINVAL, /* asynchronous failure for bad parm */ +}; +struct iw_cm_event { + enum iw_cm_event_type event; + enum iw_cm_event_status status; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + void *private_data; + u8 private_data_len; + void* provider_data; +}; + +/** + * iw_cm_handler - Function to be called by the IW CM when delivering events + * to the client. + * + * @cm_id: The IW CM identifier associated with the event. + * @event: Pointer to the event structure. + */ +typedef int (*iw_cm_handler)(struct iw_cm_id *cm_id, + struct iw_cm_event *event); + +/** + * iw_event_handler - Function called by the provider when delivering provider + * events to the IW CM. + * + * @cm_id: The IW CM identifier associated with the event. + * @event: Pointer to the event structure. + */ +typedef void (*iw_event_handler)(struct iw_cm_id *cm_id, + struct iw_cm_event *event); +struct iw_cm_id { + iw_cm_handler cm_handler; /* client callback function */ + void *context; /* client cb context */ + struct ib_device *device; + struct sockaddr_in local_addr; + struct sockaddr_in remote_addr; + void *provider_data; /* provider private data */ + iw_event_handler event_handler; /* cb for provider + events */ + /* Used by provider to add and remove refs on IW cm_id */ + void (*add_ref)(struct iw_cm_id *); + void (*rem_ref)(struct iw_cm_id *); +}; + +struct iw_cm_conn_param { + const void *private_data; + u16 private_data_len; + u32 ord; + u32 ird; + u32 qpn; +}; + +struct iw_cm_verbs { + void (*add_ref)(struct ib_qp *qp); + + void (*rem_ref)(struct ib_qp *qp); + + struct ib_qp * (*get_qp)(struct ib_device *device, + int qpn); + + int (*connect)(struct iw_cm_id *cm_id, + struct iw_cm_conn_param *conn_param); + + int (*accept)(struct iw_cm_id *cm_id, + struct iw_cm_conn_param *conn_param); + + int (*reject)(struct iw_cm_id *cm_id, + const void *pdata, u8 pdata_len); + + int (*create_listen)(struct iw_cm_id *cm_id, + int backlog); + + int (*destroy_listen)(struct iw_cm_id *cm_id); +}; + +/** + * iw_create_cm_id - Create an IW CM identifier. + * + * @device: The IB device on which to create the IW CM identier. + * @event_handler: User callback invoked to report events associated with the + * returned IW CM identifier. + * @context: User specified context associated with the id. + */ +struct iw_cm_id *iw_create_cm_id(struct ib_device *device, + iw_cm_handler cm_handler, void *context); + +/** + * iw_destroy_cm_id - Destroy an IW CM identifier. + * + * @cm_id: The previously created IW CM identifier to destroy. + * + * The client can assume that no events will be delivered for the CM ID after + * this function returns. + */ +void iw_destroy_cm_id(struct iw_cm_id *cm_id); + +/** + * iw_cm_bind_qp - Unbind the specified IW CM identifier and QP + * + * @cm_id: The IW CM idenfier to unbind from the QP. + * @qp: The QP + * + * This is called by the provider when destroying the QP to ensure + * that any references held by the IWCM are released. It may also + * be called by the IWCM when destroying a CM_ID to that any + * references held by the provider are released. + */ +void iw_cm_unbind_qp(struct iw_cm_id *cm_id, struct ib_qp *qp); + +/** + * iw_cm_get_qp - Return the ib_qp associated with a QPN + * + * @ib_device: The IB device + * @qpn: The queue pair number + */ +struct ib_qp *iw_cm_get_qp(struct ib_device *device, int qpn); + +/** + * iw_cm_listen - Listen for incoming connection requests on the + * specified IW CM id. + * + * @cm_id: The IW CM identifier. + * @backlog: The maximum number of outstanding un-accepted inbound listen + * requests to queue. + * + * The source address and port number are specified in the IW CM identifier + * structure. + */ +int iw_cm_listen(struct iw_cm_id *cm_id, int backlog); + +/** + * iw_cm_accept - Called to accept an incoming connect request. + * + * @cm_id: The IW CM identifier associated with the connection request. + * @iw_param: Pointer to a structure containing connection establishment + * parameters. + * + * The specified cm_id will have been provided in the event data for a + * CONNECT_REQUEST event. Subsequent events related to this connection will be + * delivered to the specified IW CM identifier prior and may occur prior to + * the return of this function. If this function returns a non-zero value, the + * client can assume that no events will be delivered to the specified IW CM + * identifier. + */ +int iw_cm_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param); + +/** + * iw_cm_reject - Reject an incoming connection request. + * + * @cm_id: Connection identifier associated with the request. + * @private_daa: Pointer to data to deliver to the remote peer as part of the + * reject message. + * @private_data_len: The number of bytes in the private_data parameter. + * + * The client can assume that no events will be delivered to the specified IW + * CM identifier following the return of this function. The private_data + * buffer is available for reuse when this function returns. + */ +int iw_cm_reject(struct iw_cm_id *cm_id, const void *private_data, + u8 private_data_len); + +/** + * iw_cm_connect - Called to request a connection to a remote peer. + * + * @cm_id: The IW CM identifier for the connection. + * @iw_param: Pointer to a structure containing connection establishment + * parameters. + * + * Events may be delivered to the specified IW CM identifier prior to the + * return of this function. If this function returns a non-zero value, the + * client can assume that no events will be delivered to the specified IW CM + * identifier. + */ +int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param); + +/** + * iw_cm_disconnect - Close the specified connection. + * + * @cm_id: The IW CM identifier to close. + * @abrupt: If 0, the connection will be closed gracefully, otherwise, the + * connection will be reset. + * + * The IW CM identifier is still active until the IW_CM_EVENT_CLOSE event is + * delivered. + */ +int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt); + +/** + * iw_cm_init_qp_attr - Called to initialize the attributes of the QP + * associated with a IW CM identifier. + * + * @cm_id: The IW CM identifier associated with the QP + * @qp_attr: Pointer to the QP attributes structure. + * @qp_attr_mask: Pointer to a bit vector specifying which QP attributes are + * valid. + */ +int iw_cm_init_qp_attr(struct iw_cm_id *cm_id, struct ib_qp_attr *qp_attr, + int *qp_attr_mask); + +#endif /* IW_CM_H */ diff --git a/include/rdma/iw_cm_private.h b/include/rdma/iw_cm_private.h new file mode 100644 index 0000000..d07034e --- /dev/null +++ b/include/rdma/iw_cm_private.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2005 Network Appliance, Inc. All rights reserved. + * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#if !defined(IW_CM_PRIVATE_H) +#define IW_CM_PRIVATE_H + +#include <rdma/iw_cm.h> + +enum iw_cm_state { + IW_CM_STATE_IDLE, /* unbound, inactive */ + IW_CM_STATE_LISTEN, /* listen waiting for connect */ + IW_CM_STATE_CONN_RECV, /* inbound waiting for user accept */ + IW_CM_STATE_CONN_SENT, /* outbound waiting for peer accept */ + IW_CM_STATE_ESTABLISHED, /* established */ + IW_CM_STATE_CLOSING, /* disconnect */ + IW_CM_STATE_DESTROYING /* object being deleted */ +}; + +struct iwcm_id_private { + struct iw_cm_id id; + enum iw_cm_state state; + unsigned long flags; + struct ib_qp *qp; + wait_queue_head_t destroy_wait; + wait_queue_head_t connect_wait; + struct list_head work_list; + spinlock_t lock; + atomic_t refcount; +}; +#define IWCM_F_CALLBACK_DESTROY 1 +#define IWCM_F_CONNECT_WAIT 2 + +#endif /* IW_CM_PRIVATE_H */ ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 1/2] iWARP Connection Manager. 2006-05-31 18:26 ` [PATCH 1/2] iWARP Connection Manager Steve Wise @ 2006-05-31 22:22 ` Sean Hefty 2006-06-01 17:00 ` Steve Wise 2006-06-01 17:11 ` Tom Tucker 0 siblings, 2 replies; 4+ messages in thread From: Sean Hefty @ 2006-05-31 22:22 UTC (permalink / raw) To: Steve Wise; +Cc: rdreier, linux-kernel, netdev, openib-general Steve Wise wrote: > +/* > + * Release a reference on cm_id. If the last reference is being removed > + * and iw_destroy_cm_id is waiting, wake up the waiting thread. > + */ > +static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv) > +{ > + int ret = 0; > + > + BUG_ON(atomic_read(&cm_id_priv->refcount)==0); > + if (atomic_dec_and_test(&cm_id_priv->refcount)) { > + BUG_ON(!list_empty(&cm_id_priv->work_list)); > + if (waitqueue_active(&cm_id_priv->destroy_wait)) { > + BUG_ON(cm_id_priv->state != IW_CM_STATE_DESTROYING); > + BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, > + &cm_id_priv->flags)); > + ret = 1; > + wake_up(&cm_id_priv->destroy_wait); We recently changed the RDMA CM, IB CM, and a couple of other modules from using wait objects to completions. This avoids a race condition between decrementing the reference count, which allows destruction to proceed, and calling wake_up on a freed cm_id. My guess is that you may need to do the same. Can you also explain the use of the return value here? It's ignored below in rem_ref() and destroy_cm_id(). > +static void add_ref(struct iw_cm_id *cm_id) > +{ > + struct iwcm_id_private *cm_id_priv; > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + atomic_inc(&cm_id_priv->refcount); > +} > + > +static void rem_ref(struct iw_cm_id *cm_id) > +{ > + struct iwcm_id_private *cm_id_priv; > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + iwcm_deref_id(cm_id_priv); > +} > + > +/* > + * CM_ID <-- CLOSING > + * > + * Block if a passive or active connection is currenlty being processed. Then > + * process the event as follows: > + * - If we are ESTABLISHED, move to CLOSING and modify the QP state > + * based on the abrupt flag > + * - If the connection is already in the CLOSING or IDLE state, the peer is > + * disconnecting concurrently with us and we've already seen the > + * DISCONNECT event -- ignore the request and return 0 > + * - Disconnect on a listening endpoint returns -EINVAL > + */ > +int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt) > +{ > + struct iwcm_id_private *cm_id_priv; > + unsigned long flags; > + int ret = 0; > + > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + /* Wait if we're currently in a connect or accept downcall */ > + wait_event(cm_id_priv->connect_wait, > + !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); Am I understanding this check correctly? You're checking to see if the user has called iw_cm_disconnect() at the same time that they called iw_cm_connect() or iw_cm_accept(). Are connect / accept blocking, or are you just waiting for an event? > + > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + switch (cm_id_priv->state) { > + case IW_CM_STATE_ESTABLISHED: > + cm_id_priv->state = IW_CM_STATE_CLOSING; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + if (cm_id_priv->qp) { /* QP could be <nul> for user-mode client */ > + if (abrupt) > + ret = iwcm_modify_qp_err(cm_id_priv->qp); > + else > + ret = iwcm_modify_qp_sqd(cm_id_priv->qp); > + /* > + * If both sides are disconnecting the QP could > + * already be in ERR or SQD states > + */ > + ret = 0; > + } > + else > + ret = -EINVAL; > + break; > + case IW_CM_STATE_LISTEN: > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + ret = -EINVAL; > + break; > + case IW_CM_STATE_CLOSING: > + /* remote peer closed first */ > + case IW_CM_STATE_IDLE: > + /* accept or connect returned !0 */ > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + break; > + case IW_CM_STATE_CONN_RECV: > + /* > + * App called disconnect before/without calling accept after > + * connect_request event delivered. > + */ > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + break; > + case IW_CM_STATE_CONN_SENT: > + /* Can only get here if wait above fails */ > + default: > + BUG_ON(1); > + } > + > + return ret; > +} > +EXPORT_SYMBOL(iw_cm_disconnect); > +static void destroy_cm_id(struct iw_cm_id *cm_id) > +{ > + struct iwcm_id_private *cm_id_priv; > + unsigned long flags; > + int ret; > + > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + /* Wait if we're currently in a connect or accept downcall. A > + * listening endpoint should never block here. */ > + wait_event(cm_id_priv->connect_wait, > + !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); Same question/comment as above. > + > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + switch (cm_id_priv->state) { > + case IW_CM_STATE_LISTEN: > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + /* destroy the listening endpoint */ > + ret = cm_id->device->iwcm->destroy_listen(cm_id); > + break; > + case IW_CM_STATE_ESTABLISHED: > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + /* Abrupt close of the connection */ > + (void)iwcm_modify_qp_err(cm_id_priv->qp); > + break; > + case IW_CM_STATE_IDLE: > + case IW_CM_STATE_CLOSING: > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + break; > + case IW_CM_STATE_CONN_RECV: > + /* > + * App called destroy before/without calling accept after > + * receiving connection request event notification. > + */ > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + break; > + case IW_CM_STATE_CONN_SENT: > + case IW_CM_STATE_DESTROYING: > + default: > + BUG_ON(1); > + break; > + } > + > + spin_lock_irqsave(&cm_id_priv->lock, flags); As an alternative, you could hold the lock from above, an let the LISTEN / ESTABLISHED state checks release and reacquire. > + if (cm_id_priv->qp) { > + cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); > + cm_id_priv->qp = NULL; > + } > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + > + (void)iwcm_deref_id(cm_id_priv); > +} > + > +/* > + * This function is only called by the application thread and cannot > + * be called by the event thread. The function will wait for all > + * references to be released on the cm_id and then kfree the cm_id > + * object. > + */ > +void iw_destroy_cm_id(struct iw_cm_id *cm_id) > +{ > + struct iwcm_id_private *cm_id_priv; > + > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)); > + > + destroy_cm_id(cm_id); > + > + wait_event(cm_id_priv->destroy_wait, > + !atomic_read(&cm_id_priv->refcount)); > + > + kfree(cm_id_priv); > +} > +EXPORT_SYMBOL(iw_destroy_cm_id); > + > +/* > + * CM_ID <-- LISTEN > + * > + * Start listening for connect requests. Generates one CONNECT_REQUEST > + * event for each inbound connect request. > + */ > +int iw_cm_listen(struct iw_cm_id *cm_id, int backlog) > +{ > + struct iwcm_id_private *cm_id_priv; > + unsigned long flags; > + int ret = 0; > + > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + switch (cm_id_priv->state) { > + case IW_CM_STATE_IDLE: > + cm_id_priv->state = IW_CM_STATE_LISTEN; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + ret = cm_id->device->iwcm->create_listen(cm_id, backlog); > + if (ret) > + cm_id_priv->state = IW_CM_STATE_IDLE; > + break; > + default: > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + ret = -EINVAL; > + } > + > + return ret; > +} > +EXPORT_SYMBOL(iw_cm_listen); > + > +/* > + * CM_ID <-- IDLE > + * > + * Rejects an inbound connection request. No events are generated. > + */ > +int iw_cm_reject(struct iw_cm_id *cm_id, > + const void *private_data, > + u8 private_data_len) > +{ > + struct iwcm_id_private *cm_id_priv; > + unsigned long flags; > + int ret; > + > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + wake_up_all(&cm_id_priv->connect_wait); > + return -EINVAL; > + } > + cm_id_priv->state = IW_CM_STATE_IDLE; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + > + ret = cm_id->device->iwcm->reject(cm_id, private_data, > + private_data_len); > + > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + wake_up_all(&cm_id_priv->connect_wait); > + > + return ret; > +} > +EXPORT_SYMBOL(iw_cm_reject); > + > +/* > + * CM_ID <-- ESTABLISHED > + * > + * Accepts an inbound connection request and generates an ESTABLISHED > + * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block > + * until the ESTABLISHED event is received from the provider. > + */ This makes it sound like we're just waiting for an event. > +int iw_cm_accept(struct iw_cm_id *cm_id, > + struct iw_cm_conn_param *iw_param) > +{ > + struct iwcm_id_private *cm_id_priv; > + struct ib_qp *qp; > + unsigned long flags; > + int ret; > + > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + wake_up_all(&cm_id_priv->connect_wait); > + return -EINVAL; > + } > + /* Get the ib_qp given the QPN */ > + qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); > + if (!qp) { > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + return -EINVAL; > + } > + cm_id->device->iwcm->add_ref(qp); > + cm_id_priv->qp = qp; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + > + ret = cm_id->device->iwcm->accept(cm_id, iw_param); > + if (ret) { > + /* An error on accept precludes provider events */ > + BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV); > + cm_id_priv->state = IW_CM_STATE_IDLE; > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + if (cm_id_priv->qp) { > + cm_id->device->iwcm->rem_ref(qp); > + cm_id_priv->qp = NULL; > + } > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + printk("Accept failed, ret=%d\n", ret); > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + wake_up_all(&cm_id_priv->connect_wait); > + } > + > + return ret; > +} > +EXPORT_SYMBOL(iw_cm_accept); > + > +/* > + * Active Side: CM_ID <-- CONN_SENT > + * > + * If successful, results in the generation of a CONNECT_REPLY > + * event. iw_cm_disconnect and iw_cm_destroy will block until the > + * CONNECT_REPLY event is received from the provider. > + */ > +int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) > +{ > + struct iwcm_id_private *cm_id_priv; > + int ret = 0; > + unsigned long flags; > + struct ib_qp *qp; > + > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + if (cm_id_priv->state != IW_CM_STATE_IDLE) { > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + wake_up_all(&cm_id_priv->connect_wait); > + return -EINVAL; > + } > + > + /* Get the ib_qp given the QPN */ > + qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); > + if (!qp) { > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + return -EINVAL; > + } > + cm_id->device->iwcm->add_ref(qp); > + cm_id_priv->qp = qp; > + cm_id_priv->state = IW_CM_STATE_CONN_SENT; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + > + ret = cm_id->device->iwcm->connect(cm_id, iw_param); > + if (ret) { > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + if (cm_id_priv->qp) { > + cm_id->device->iwcm->rem_ref(qp); > + cm_id_priv->qp = NULL; > + } > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT); > + cm_id_priv->state = IW_CM_STATE_IDLE; > + printk("Connect failed, ret=%d\n", ret); > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + wake_up_all(&cm_id_priv->connect_wait); > + } > + > + return ret; > +} > +EXPORT_SYMBOL(iw_cm_connect); > + > +/* > + * Passive Side: new CM_ID <-- CONN_RECV > + * > + * Handles an inbound connect request. The function creates a new > + * iw_cm_id to represent the new connection and inherits the client > + * callback function and other attributes from the listening parent. > + * > + * The work item contains a pointer to the listen_cm_id and the event. The > + * listen_cm_id contains the client cm_handler, context and > + * device. These are copied when the device is cloned. The event > + * contains the new four tuple. > + * > + * An error on the child should not affect the parent, so this > + * function does not return a value. > + */ > +static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv, > + struct iw_cm_event *iw_event) > +{ > + unsigned long flags; > + struct iw_cm_id *cm_id; > + struct iwcm_id_private *cm_id_priv; > + int ret; > + > + /* The provider should never generate a connection request > + * event with a bad status. > + */ > + BUG_ON(iw_event->status); > + > + /* We could be destroying the listening id. If so, ignore this > + * upcall. */ > + spin_lock_irqsave(&listen_id_priv->lock, flags); > + if (listen_id_priv->state != IW_CM_STATE_LISTEN) { > + spin_unlock_irqrestore(&listen_id_priv->lock, flags); > + return; > + } > + spin_unlock_irqrestore(&listen_id_priv->lock, flags); > + > + cm_id = iw_create_cm_id(listen_id_priv->id.device, > + listen_id_priv->id.cm_handler, > + listen_id_priv->id.context); > + /* If the cm_id could not be created, ignore the request */ > + if (IS_ERR(cm_id)) > + return; > + > + cm_id->provider_data = iw_event->provider_data; > + cm_id->local_addr = iw_event->local_addr; > + cm_id->remote_addr = iw_event->remote_addr; > + > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > + cm_id_priv->state = IW_CM_STATE_CONN_RECV; > + > + /* Call the client CM handler */ > + ret = cm_id->cm_handler(cm_id, iw_event); > + if (ret) { > + printk("destroying child id %p, ret=%d\n", > + cm_id, ret); We probably don't always want to print a message here. > + set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); > + destroy_cm_id(cm_id); > + if (atomic_read(&cm_id_priv->refcount)==0) > + kfree(cm_id); > + } > +} > + > +/* > + * Passive Side: CM_ID <-- ESTABLISHED > + * > + * The provider generated an ESTABLISHED event which means that > + * the MPA negotion has completed successfully and we are now in MPA > + * FPDU mode. > + * > + * This event can only be received in the CONN_RECV state. If the > + * remote peer closed, the ESTABLISHED event would be received followed > + * by the CLOSE event. If the app closes, it will block until we wake > + * it up after processing this event. > + */ > +static int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv, > + struct iw_cm_event *iw_event) > +{ > + unsigned long flags; > + int ret = 0; > + > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + > + /* We clear the CONNECT_WAIT bit here to allow the callback > + * function to call iw_cm_disconnect. Calling iw_destroy_cm_id > + * from a callback handler is not allowed */ > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + switch (cm_id_priv->state) { > + case IW_CM_STATE_CONN_RECV: > + cm_id_priv->state = IW_CM_STATE_ESTABLISHED; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); > + break; > + default: > + BUG_ON(1); Can just BUG_ON the state and avoid the switch. Same comment applies below. > + } > + wake_up_all(&cm_id_priv->connect_wait); > + > + return ret; > +} > + > +/* > + * Active Side: CM_ID <-- ESTABLISHED > + * > + * The app has called connect and is waiting for the established event to > + * post it's requests to the server. This event will wake up anyone > + * blocked in iw_cm_disconnect or iw_destroy_id. > + */ > +static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv, > + struct iw_cm_event *iw_event) > +{ > + unsigned long flags; > + int ret = 0; > + > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + /* Clear the connect wait bit so a callback function calling > + * iw_cm_disconnect will not wait and deadlock this thread */ > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > + switch (cm_id_priv->state) { > + case IW_CM_STATE_CONN_SENT: > + if (iw_event->status == IW_CM_EVENT_STATUS_ACCEPTED) { > + cm_id_priv->id.local_addr = iw_event->local_addr; > + cm_id_priv->id.remote_addr = iw_event->remote_addr; > + cm_id_priv->state = IW_CM_STATE_ESTABLISHED; > + } else { > + /* REJECTED or RESET */ > + cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); > + cm_id_priv->qp = NULL; > + cm_id_priv->state = IW_CM_STATE_IDLE; > + } > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > + ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); > + break; > + default: > + BUG_ON(1); > + } > + /* Wake up waiters on connect complete */ > + wake_up_all(&cm_id_priv->connect_wait); > + > + return ret; > +} > + > +/* > + * CM_ID <-- CLOSING > + * > + * If in the ESTABLISHED state, move to CLOSING. > + */ > +static void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv, > + struct iw_cm_event *iw_event) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&cm_id_priv->lock, flags); > + if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED) > + cm_id_priv->state = IW_CM_STATE_CLOSING; > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > +} > + > +/* > + * CM_ID <-- IDLE > + * > + * If in the ESTBLISHED or CLOSING states, the QP will have have been > + * moved by the provider to the ERR state. Disassociate the CM_ID from > + * the QP, move to IDLE, and remove the 'connected' reference. > + * > + * If in some other state, the cm_id was destroyed asynchronously. > + * This is the last reference that will result in waking up > + * the app thread blocked in iw_destroy_cm_id. > + */ > +static int cm_close_handler(struct iwcm_id_private *cm_id_priv, > + struct iw_cm_event *iw_event) > +{ > + unsigned long flags; > + int ret = 0; > + /* TT */printk("%s:%d cm_id_priv=%p, state=%d\n", > + __FUNCTION__, __LINE__, > + cm_id_priv,cm_id_priv->state); Will want to remove this. - Sean ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 1/2] iWARP Connection Manager. 2006-05-31 22:22 ` Sean Hefty @ 2006-06-01 17:00 ` Steve Wise 2006-06-01 21:09 ` Sean Hefty 2006-06-01 17:11 ` Tom Tucker 1 sibling, 1 reply; 4+ messages in thread From: Steve Wise @ 2006-06-01 17:00 UTC (permalink / raw) To: Sean Hefty; +Cc: rdreier, linux-kernel, netdev, openib-general On Wed, 2006-05-31 at 15:22 -0700, Sean Hefty wrote: > Steve Wise wrote: > > +/* > > + * Release a reference on cm_id. If the last reference is being removed > > + * and iw_destroy_cm_id is waiting, wake up the waiting thread. > > + */ > > +static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv) > > +{ > > + int ret = 0; > > + > > + BUG_ON(atomic_read(&cm_id_priv->refcount)==0); > > + if (atomic_dec_and_test(&cm_id_priv->refcount)) { > > + BUG_ON(!list_empty(&cm_id_priv->work_list)); > > + if (waitqueue_active(&cm_id_priv->destroy_wait)) { > > + BUG_ON(cm_id_priv->state != IW_CM_STATE_DESTROYING); > > + BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, > > + &cm_id_priv->flags)); > > + ret = 1; > > + wake_up(&cm_id_priv->destroy_wait); > > We recently changed the RDMA CM, IB CM, and a couple of other modules from using > wait objects to completions. This avoids a race condition between decrementing > the reference count, which allows destruction to proceed, and calling wake_up on > a freed cm_id. My guess is that you may need to do the same. > Good catch. Yes, the IW CM suffers from the same race condition. I'll change this to use completions... > Can you also explain the use of the return value here? It's ignored below in > rem_ref() and destroy_cm_id(). > The return value is supposed to indicate whether this call to deref _may_ have resulted in waking up another thread and the cm_id being freed. Its used in cm_work_handler(), in conjunction with setting the IWCM_F_CALLBACK_DESTROY flag to know whether the cm_id needs to be freed on the callback path. > > +static void add_ref(struct iw_cm_id *cm_id) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + atomic_inc(&cm_id_priv->refcount); > > +} > > + > > +static void rem_ref(struct iw_cm_id *cm_id) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + iwcm_deref_id(cm_id_priv); > > +} > > + > > > +/* > > + * CM_ID <-- CLOSING > > + * > > + * Block if a passive or active connection is currenlty being processed. Then > > + * process the event as follows: > > + * - If we are ESTABLISHED, move to CLOSING and modify the QP state > > + * based on the abrupt flag > > + * - If the connection is already in the CLOSING or IDLE state, the peer is > > + * disconnecting concurrently with us and we've already seen the > > + * DISCONNECT event -- ignore the request and return 0 > > + * - Disconnect on a listening endpoint returns -EINVAL > > + */ > > +int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + unsigned long flags; > > + int ret = 0; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + /* Wait if we're currently in a connect or accept downcall */ > > + wait_event(cm_id_priv->connect_wait, > > + !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); > > Am I understanding this check correctly? You're checking to see if the user has > called iw_cm_disconnect() at the same time that they called iw_cm_connect() or > iw_cm_accept(). Are connect / accept blocking, or are you just waiting for an > event? The CM must wait for the low level provider to finish a connect() or accept() operation before telling the low level provider to disconnect via modifying the iwarp QP. Regardless of whether they block, this disconnect can happen concurrently with the connect/accept so we need to hold the disconnect until the connect/accept completes. > > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_ESTABLISHED: > > + cm_id_priv->state = IW_CM_STATE_CLOSING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + if (cm_id_priv->qp) { /* QP could be <nul> for user-mode client */ > > + if (abrupt) > > + ret = iwcm_modify_qp_err(cm_id_priv->qp); > > + else > > + ret = iwcm_modify_qp_sqd(cm_id_priv->qp); > > + /* > > + * If both sides are disconnecting the QP could > > + * already be in ERR or SQD states > > + */ > > + ret = 0; > > + } > > + else > > + ret = -EINVAL; > > + break; > > + case IW_CM_STATE_LISTEN: > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = -EINVAL; > > + break; > > + case IW_CM_STATE_CLOSING: > > + /* remote peer closed first */ > > + case IW_CM_STATE_IDLE: > > + /* accept or connect returned !0 */ > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + break; > > + case IW_CM_STATE_CONN_RECV: > > + /* > > + * App called disconnect before/without calling accept after > > + * connect_request event delivered. > > + */ > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + break; > > + case IW_CM_STATE_CONN_SENT: > > + /* Can only get here if wait above fails */ > > + default: > > + BUG_ON(1); > > + } > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_disconnect); > > +static void destroy_cm_id(struct iw_cm_id *cm_id) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + unsigned long flags; > > + int ret; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + /* Wait if we're currently in a connect or accept downcall. A > > + * listening endpoint should never block here. */ > > + wait_event(cm_id_priv->connect_wait, > > + !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); > > Same question/comment as above. > Same answer. > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_LISTEN: > > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + /* destroy the listening endpoint */ > > + ret = cm_id->device->iwcm->destroy_listen(cm_id); > > + break; > > + case IW_CM_STATE_ESTABLISHED: > > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + /* Abrupt close of the connection */ > > + (void)iwcm_modify_qp_err(cm_id_priv->qp); > > + break; > > + case IW_CM_STATE_IDLE: > > + case IW_CM_STATE_CLOSING: > > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + break; > > + case IW_CM_STATE_CONN_RECV: > > + /* > > + * App called destroy before/without calling accept after > > + * receiving connection request event notification. > > + */ > > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + break; > > + case IW_CM_STATE_CONN_SENT: > > + case IW_CM_STATE_DESTROYING: > > + default: > > + BUG_ON(1); > > + break; > > + } > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > As an alternative, you could hold the lock from above, an let the LISTEN / > ESTABLISHED state checks release and reacquire. > Yes, perhaps that's cleaner. > > + if (cm_id_priv->qp) { > > + cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); > > + cm_id_priv->qp = NULL; > > + } > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + > > + (void)iwcm_deref_id(cm_id_priv); > > +} > > + > > +/* > > + * This function is only called by the application thread and cannot > > + * be called by the event thread. The function will wait for all > > + * references to be released on the cm_id and then kfree the cm_id > > + * object. > > + */ > > +void iw_destroy_cm_id(struct iw_cm_id *cm_id) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)); > > + > > + destroy_cm_id(cm_id); > > + > > + wait_event(cm_id_priv->destroy_wait, > > + !atomic_read(&cm_id_priv->refcount)); > > + > > + kfree(cm_id_priv); > > +} > > +EXPORT_SYMBOL(iw_destroy_cm_id); > > + > > +/* > > + * CM_ID <-- LISTEN > > + * > > + * Start listening for connect requests. Generates one CONNECT_REQUEST > > + * event for each inbound connect request. > > + */ > > +int iw_cm_listen(struct iw_cm_id *cm_id, int backlog) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + unsigned long flags; > > + int ret = 0; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_IDLE: > > + cm_id_priv->state = IW_CM_STATE_LISTEN; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = cm_id->device->iwcm->create_listen(cm_id, backlog); > > + if (ret) > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + break; > > + default: > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = -EINVAL; > > + } > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_listen); > > + > > +/* > > + * CM_ID <-- IDLE > > + * > > + * Rejects an inbound connection request. No events are generated. > > + */ > > +int iw_cm_reject(struct iw_cm_id *cm_id, > > + const void *private_data, > > + u8 private_data_len) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + unsigned long flags; > > + int ret; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + return -EINVAL; > > + } > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + > > + ret = cm_id->device->iwcm->reject(cm_id, private_data, > > + private_data_len); > > + > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_reject); > > + > > +/* > > + * CM_ID <-- ESTABLISHED > > + * > > + * Accepts an inbound connection request and generates an ESTABLISHED > > + * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block > > + * until the ESTABLISHED event is received from the provider. > > + */ > > This makes it sound like we're just waiting for an event. > disconnect/destory paths wait for the provider to complete the accept or connect operation. > > +int iw_cm_accept(struct iw_cm_id *cm_id, > > + struct iw_cm_conn_param *iw_param) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + struct ib_qp *qp; > > + unsigned long flags; > > + int ret; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + return -EINVAL; > > + } > > + /* Get the ib_qp given the QPN */ > > + qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); > > + if (!qp) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + return -EINVAL; > > + } > > + cm_id->device->iwcm->add_ref(qp); > > + cm_id_priv->qp = qp; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + > > + ret = cm_id->device->iwcm->accept(cm_id, iw_param); > > + if (ret) { > > + /* An error on accept precludes provider events */ > > + BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV); > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->qp) { > > + cm_id->device->iwcm->rem_ref(qp); > > + cm_id_priv->qp = NULL; > > + } > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + printk("Accept failed, ret=%d\n", ret); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + } > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_accept); > > + > > +/* > > + * Active Side: CM_ID <-- CONN_SENT > > + * > > + * If successful, results in the generation of a CONNECT_REPLY > > + * event. iw_cm_disconnect and iw_cm_destroy will block until the > > + * CONNECT_REPLY event is received from the provider. > > + */ > > +int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + int ret = 0; > > + unsigned long flags; > > + struct ib_qp *qp; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->state != IW_CM_STATE_IDLE) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + return -EINVAL; > > + } > > + > > + /* Get the ib_qp given the QPN */ > > + qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); > > + if (!qp) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + return -EINVAL; > > + } > > + cm_id->device->iwcm->add_ref(qp); > > + cm_id_priv->qp = qp; > > + cm_id_priv->state = IW_CM_STATE_CONN_SENT; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + > > + ret = cm_id->device->iwcm->connect(cm_id, iw_param); > > + if (ret) { > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->qp) { > > + cm_id->device->iwcm->rem_ref(qp); > > + cm_id_priv->qp = NULL; > > + } > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT); > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + printk("Connect failed, ret=%d\n", ret); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + } > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_connect); > > + > > +/* > > + * Passive Side: new CM_ID <-- CONN_RECV > > + * > > + * Handles an inbound connect request. The function creates a new > > + * iw_cm_id to represent the new connection and inherits the client > > + * callback function and other attributes from the listening parent. > > + * > > + * The work item contains a pointer to the listen_cm_id and the event. The > > + * listen_cm_id contains the client cm_handler, context and > > + * device. These are copied when the device is cloned. The event > > + * contains the new four tuple. > > + * > > + * An error on the child should not affect the parent, so this > > + * function does not return a value. > > + */ > > +static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + struct iw_cm_id *cm_id; > > + struct iwcm_id_private *cm_id_priv; > > + int ret; > > + > > + /* The provider should never generate a connection request > > + * event with a bad status. > > + */ > > + BUG_ON(iw_event->status); > > + > > + /* We could be destroying the listening id. If so, ignore this > > + * upcall. */ > > + spin_lock_irqsave(&listen_id_priv->lock, flags); > > + if (listen_id_priv->state != IW_CM_STATE_LISTEN) { > > + spin_unlock_irqrestore(&listen_id_priv->lock, flags); > > + return; > > + } > > + spin_unlock_irqrestore(&listen_id_priv->lock, flags); > > + > > + cm_id = iw_create_cm_id(listen_id_priv->id.device, > > + listen_id_priv->id.cm_handler, > > + listen_id_priv->id.context); > > + /* If the cm_id could not be created, ignore the request */ > > + if (IS_ERR(cm_id)) > > + return; > > + > > + cm_id->provider_data = iw_event->provider_data; > > + cm_id->local_addr = iw_event->local_addr; > > + cm_id->remote_addr = iw_event->remote_addr; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + cm_id_priv->state = IW_CM_STATE_CONN_RECV; > > + > > + /* Call the client CM handler */ > > + ret = cm_id->cm_handler(cm_id, iw_event); > > + if (ret) { > > + printk("destroying child id %p, ret=%d\n", > > + cm_id, ret); > > We probably don't always want to print a message here. > Yes. I'll change this to a pr_debug(). > > + set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); > > + destroy_cm_id(cm_id); > > + if (atomic_read(&cm_id_priv->refcount)==0) > > + kfree(cm_id); > > + } > > +} > > + > > +/* > > + * Passive Side: CM_ID <-- ESTABLISHED > > + * > > + * The provider generated an ESTABLISHED event which means that > > + * the MPA negotion has completed successfully and we are now in MPA > > + * FPDU mode. > > + * > > + * This event can only be received in the CONN_RECV state. If the > > + * remote peer closed, the ESTABLISHED event would be received followed > > + * by the CLOSE event. If the app closes, it will block until we wake > > + * it up after processing this event. > > + */ > > +static int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + int ret = 0; > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + > > + /* We clear the CONNECT_WAIT bit here to allow the callback > > + * function to call iw_cm_disconnect. Calling iw_destroy_cm_id > > + * from a callback handler is not allowed */ > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_CONN_RECV: > > + cm_id_priv->state = IW_CM_STATE_ESTABLISHED; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); > > + break; > > + default: > > + BUG_ON(1); > > Can just BUG_ON the state and avoid the switch. Same comment applies below. > ok. > > + } > > + wake_up_all(&cm_id_priv->connect_wait); > > + > > + return ret; > > +} > > + > > +/* > > + * Active Side: CM_ID <-- ESTABLISHED > > + * > > + * The app has called connect and is waiting for the established event to > > + * post it's requests to the server. This event will wake up anyone > > + * blocked in iw_cm_disconnect or iw_destroy_id. > > + */ > > +static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + int ret = 0; > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + /* Clear the connect wait bit so a callback function calling > > + * iw_cm_disconnect will not wait and deadlock this thread */ > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_CONN_SENT: > > + if (iw_event->status == IW_CM_EVENT_STATUS_ACCEPTED) { > > + cm_id_priv->id.local_addr = iw_event->local_addr; > > + cm_id_priv->id.remote_addr = iw_event->remote_addr; > > + cm_id_priv->state = IW_CM_STATE_ESTABLISHED; > > + } else { > > + /* REJECTED or RESET */ > > + cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); > > + cm_id_priv->qp = NULL; > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + } > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); > > + break; > > + default: > > + BUG_ON(1); > > + } > > + /* Wake up waiters on connect complete */ > > + wake_up_all(&cm_id_priv->connect_wait); > > + > > + return ret; > > +} > > + > > +/* > > + * CM_ID <-- CLOSING > > + * > > + * If in the ESTABLISHED state, move to CLOSING. > > + */ > > +static void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED) > > + cm_id_priv->state = IW_CM_STATE_CLOSING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > +} > > + > > +/* > > + * CM_ID <-- IDLE > > + * > > + * If in the ESTBLISHED or CLOSING states, the QP will have have been > > + * moved by the provider to the ERR state. Disassociate the CM_ID from > > + * the QP, move to IDLE, and remove the 'connected' reference. > > + * > > + * If in some other state, the cm_id was destroyed asynchronously. > > + * This is the last reference that will result in waking up > > + * the app thread blocked in iw_destroy_cm_id. > > + */ > > +static int cm_close_handler(struct iwcm_id_private *cm_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + int ret = 0; > > + /* TT */printk("%s:%d cm_id_priv=%p, state=%d\n", > > + __FUNCTION__, __LINE__, > > + cm_id_priv,cm_id_priv->state); > > Will want to remove this. > oops. yes... > - Sean ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 1/2] iWARP Connection Manager. 2006-06-01 17:00 ` Steve Wise @ 2006-06-01 21:09 ` Sean Hefty 2006-06-01 22:21 ` [openib-general] " Tom Tucker 0 siblings, 1 reply; 4+ messages in thread From: Sean Hefty @ 2006-06-01 21:09 UTC (permalink / raw) To: Steve Wise; +Cc: rdreier, linux-kernel, netdev, openib-general Steve Wise wrote: >>>+int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt) >>>+{ >>>+ struct iwcm_id_private *cm_id_priv; >>>+ unsigned long flags; >>>+ int ret = 0; >>>+ >>>+ cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); >>>+ /* Wait if we're currently in a connect or accept downcall */ >>>+ wait_event(cm_id_priv->connect_wait, >>>+ !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); >> >>Am I understanding this check correctly? You're checking to see if the user has >>called iw_cm_disconnect() at the same time that they called iw_cm_connect() or >>iw_cm_accept(). Are connect / accept blocking, or are you just waiting for an >>event? > > > The CM must wait for the low level provider to finish a connect() or > accept() operation before telling the low level provider to disconnect > via modifying the iwarp QP. Regardless of whether they block, this > disconnect can happen concurrently with the connect/accept so we need to > hold the disconnect until the connect/accept completes. > > >>>+EXPORT_SYMBOL(iw_cm_disconnect); >>>+static void destroy_cm_id(struct iw_cm_id *cm_id) >>>+{ >>>+ struct iwcm_id_private *cm_id_priv; >>>+ unsigned long flags; >>>+ int ret; >>>+ >>>+ cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); >>>+ /* Wait if we're currently in a connect or accept downcall. A >>>+ * listening endpoint should never block here. */ >>>+ wait_event(cm_id_priv->connect_wait, >>>+ !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); >> >>Same question/comment as above. >> > > > Same answer. There's a difference between trying to handle the user calling disconnect/destroy at the same time a call to accept/connect is active, versus the user calling disconnect/destroy after accept/connect have returned. In the latter case, I think you're fine. In the first case, this is allowing a user to call destroy at the same time that they're calling accept/connect. Additionally, there's no guarantee that the F_CONNECT_WAIT flag has been set by accept/connect by the time disconnect/destroy tests it. - Sean ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [openib-general] Re: [PATCH 1/2] iWARP Connection Manager. 2006-06-01 21:09 ` Sean Hefty @ 2006-06-01 22:21 ` Tom Tucker 0 siblings, 0 replies; 4+ messages in thread From: Tom Tucker @ 2006-06-01 22:21 UTC (permalink / raw) To: Sean Hefty; +Cc: Steve Wise, netdev, rdreier, linux-kernel, openib-general On Thu, 2006-06-01 at 14:09 -0700, Sean Hefty wrote: > Steve Wise wrote: > >>>+int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt) > >>>+{ > >>>+ struct iwcm_id_private *cm_id_priv; > >>>+ unsigned long flags; > >>>+ int ret = 0; > >>>+ > >>>+ cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > >>>+ /* Wait if we're currently in a connect or accept downcall */ > >>>+ wait_event(cm_id_priv->connect_wait, > >>>+ !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); > >> > >>Am I understanding this check correctly? You're checking to see if the user has > >>called iw_cm_disconnect() at the same time that they called iw_cm_connect() or > >>iw_cm_accept(). Are connect / accept blocking, or are you just waiting for an > >>event? > > > > > > The CM must wait for the low level provider to finish a connect() or > > accept() operation before telling the low level provider to disconnect > > via modifying the iwarp QP. Regardless of whether they block, this > > disconnect can happen concurrently with the connect/accept so we need to > > hold the disconnect until the connect/accept completes. > > > > > >>>+EXPORT_SYMBOL(iw_cm_disconnect); > >>>+static void destroy_cm_id(struct iw_cm_id *cm_id) > >>>+{ > >>>+ struct iwcm_id_private *cm_id_priv; > >>>+ unsigned long flags; > >>>+ int ret; > >>>+ > >>>+ cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > >>>+ /* Wait if we're currently in a connect or accept downcall. A > >>>+ * listening endpoint should never block here. */ > >>>+ wait_event(cm_id_priv->connect_wait, > >>>+ !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); > >> > >>Same question/comment as above. > >> > > > > > > Same answer. > > There's a difference between trying to handle the user calling > disconnect/destroy at the same time a call to accept/connect is active, versus > the user calling disconnect/destroy after accept/connect have returned. In the > latter case, I think you're fine. In the first case, this is allowing a user to > call destroy at the same time that they're calling accept/connect. > Additionally, there's no guarantee that the F_CONNECT_WAIT flag has been set by > accept/connect by the time disconnect/destroy tests it. The problem is that we can't synchronously cancel an outstanding connect request. Once we've asked the adapter to connect, we can't tell him to stop, we have to wait for it to fail. During the time period between when we ask to connect and the adapter says yeah-or-nay, the user hits ctrl-C. This is the case where disconnect and/or destroy gets called and we have to block it waiting for the outstanding connect request to complete. One alternative to this approach is to do the kfree of the cm_id in the deref logic. This was the original design and leaves the object around to handle the completion of the connect and still allows the app to clean up and go away without all this waitin' around. When the adapter finally finishes and releases it's reference, the object is kfree'd. Hope this helps. > > - Sean > _______________________________________________ > openib-general mailing list > openib-general@openib.org > http://openib.org/mailman/listinfo/openib-general > > To unsubscribe, please visit http://openib.org/mailman/listinfo/openib-general ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [openib-general] Re: [PATCH 1/2] iWARP Connection Manager. 2006-05-31 22:22 ` Sean Hefty 2006-06-01 17:00 ` Steve Wise @ 2006-06-01 17:11 ` Tom Tucker 1 sibling, 0 replies; 4+ messages in thread From: Tom Tucker @ 2006-06-01 17:11 UTC (permalink / raw) To: Sean Hefty; +Cc: Steve Wise, netdev, rdreier, linux-kernel, openib-general On Wed, 2006-05-31 at 15:22 -0700, Sean Hefty wrote: > Steve Wise wrote: > > +/* > > + * Release a reference on cm_id. If the last reference is being removed > > + * and iw_destroy_cm_id is waiting, wake up the waiting thread. > > + */ > > +static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv) > > +{ > > + int ret = 0; > > + > > + BUG_ON(atomic_read(&cm_id_priv->refcount)==0); > > + if (atomic_dec_and_test(&cm_id_priv->refcount)) { > > + BUG_ON(!list_empty(&cm_id_priv->work_list)); > > + if (waitqueue_active(&cm_id_priv->destroy_wait)) { > > + BUG_ON(cm_id_priv->state != IW_CM_STATE_DESTROYING); > > + BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, > > + &cm_id_priv->flags)); > > + ret = 1; > > + wake_up(&cm_id_priv->destroy_wait); > > We recently changed the RDMA CM, IB CM, and a couple of other modules from using > wait objects to completions. This avoids a race condition between decrementing > the reference count, which allows destruction to proceed, and calling wake_up on > a freed cm_id. My guess is that you may need to do the same. > > Can you also explain the use of the return value here? It's ignored below in > rem_ref() and destroy_cm_id(). > > > +static void add_ref(struct iw_cm_id *cm_id) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + atomic_inc(&cm_id_priv->refcount); > > +} > > + > > +static void rem_ref(struct iw_cm_id *cm_id) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + iwcm_deref_id(cm_id_priv); > > +} > > + > > > +/* > > + * CM_ID <-- CLOSING > > + * > > + * Block if a passive or active connection is currenlty being processed. Then > > + * process the event as follows: > > + * - If we are ESTABLISHED, move to CLOSING and modify the QP state > > + * based on the abrupt flag > > + * - If the connection is already in the CLOSING or IDLE state, the peer is > > + * disconnecting concurrently with us and we've already seen the > > + * DISCONNECT event -- ignore the request and return 0 > > + * - Disconnect on a listening endpoint returns -EINVAL > > + */ > > +int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + unsigned long flags; > > + int ret = 0; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + /* Wait if we're currently in a connect or accept downcall */ > > + wait_event(cm_id_priv->connect_wait, > > + !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); > > Am I understanding this check correctly? You're checking to see if the user has > called iw_cm_disconnect() at the same time that they called iw_cm_connect() or > iw_cm_accept(). Are connect / accept blocking, or are you just waiting for an > event? Yes. The application (or the case I saw was user-mode exit logic after ctrl-C) cleaning up at random times relative to connection establishment. > > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_ESTABLISHED: > > + cm_id_priv->state = IW_CM_STATE_CLOSING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + if (cm_id_priv->qp) { /* QP could be <nul> for user-mode client */ > > + if (abrupt) > > + ret = iwcm_modify_qp_err(cm_id_priv->qp); > > + else > > + ret = iwcm_modify_qp_sqd(cm_id_priv->qp); > > + /* > > + * If both sides are disconnecting the QP could > > + * already be in ERR or SQD states > > + */ > > + ret = 0; > > + } > > + else > > + ret = -EINVAL; > > + break; > > + case IW_CM_STATE_LISTEN: > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = -EINVAL; > > + break; > > + case IW_CM_STATE_CLOSING: > > + /* remote peer closed first */ > > + case IW_CM_STATE_IDLE: > > + /* accept or connect returned !0 */ > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + break; > > + case IW_CM_STATE_CONN_RECV: > > + /* > > + * App called disconnect before/without calling accept after > > + * connect_request event delivered. > > + */ > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + break; > > + case IW_CM_STATE_CONN_SENT: > > + /* Can only get here if wait above fails */ > > + default: > > + BUG_ON(1); > > + } > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_disconnect); > > +static void destroy_cm_id(struct iw_cm_id *cm_id) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + unsigned long flags; > > + int ret; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + /* Wait if we're currently in a connect or accept downcall. A > > + * listening endpoint should never block here. */ > > + wait_event(cm_id_priv->connect_wait, > > + !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); > > Same question/comment as above. > > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_LISTEN: > > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + /* destroy the listening endpoint */ > > + ret = cm_id->device->iwcm->destroy_listen(cm_id); > > + break; > > + case IW_CM_STATE_ESTABLISHED: > > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + /* Abrupt close of the connection */ > > + (void)iwcm_modify_qp_err(cm_id_priv->qp); > > + break; > > + case IW_CM_STATE_IDLE: > > + case IW_CM_STATE_CLOSING: > > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + break; > > + case IW_CM_STATE_CONN_RECV: > > + /* > > + * App called destroy before/without calling accept after > > + * receiving connection request event notification. > > + */ > > + cm_id_priv->state = IW_CM_STATE_DESTROYING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + break; > > + case IW_CM_STATE_CONN_SENT: > > + case IW_CM_STATE_DESTROYING: > > + default: > > + BUG_ON(1); > > + break; > > + } > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > As an alternative, you could hold the lock from above, an let the LISTEN / > ESTABLISHED state checks release and reacquire. > > > + if (cm_id_priv->qp) { > > + cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); > > + cm_id_priv->qp = NULL; > > + } > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + > > + (void)iwcm_deref_id(cm_id_priv); > > +} > > + > > +/* > > + * This function is only called by the application thread and cannot > > + * be called by the event thread. The function will wait for all > > + * references to be released on the cm_id and then kfree the cm_id > > + * object. > > + */ > > +void iw_destroy_cm_id(struct iw_cm_id *cm_id) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)); > > + > > + destroy_cm_id(cm_id); > > + > > + wait_event(cm_id_priv->destroy_wait, > > + !atomic_read(&cm_id_priv->refcount)); > > + > > + kfree(cm_id_priv); > > +} > > +EXPORT_SYMBOL(iw_destroy_cm_id); > > + > > +/* > > + * CM_ID <-- LISTEN > > + * > > + * Start listening for connect requests. Generates one CONNECT_REQUEST > > + * event for each inbound connect request. > > + */ > > +int iw_cm_listen(struct iw_cm_id *cm_id, int backlog) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + unsigned long flags; > > + int ret = 0; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_IDLE: > > + cm_id_priv->state = IW_CM_STATE_LISTEN; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = cm_id->device->iwcm->create_listen(cm_id, backlog); > > + if (ret) > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + break; > > + default: > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = -EINVAL; > > + } > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_listen); > > + > > +/* > > + * CM_ID <-- IDLE > > + * > > + * Rejects an inbound connection request. No events are generated. > > + */ > > +int iw_cm_reject(struct iw_cm_id *cm_id, > > + const void *private_data, > > + u8 private_data_len) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + unsigned long flags; > > + int ret; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + return -EINVAL; > > + } > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + > > + ret = cm_id->device->iwcm->reject(cm_id, private_data, > > + private_data_len); > > + > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_reject); > > + > > +/* > > + * CM_ID <-- ESTABLISHED > > + * > > + * Accepts an inbound connection request and generates an ESTABLISHED > > + * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block > > + * until the ESTABLISHED event is received from the provider. > > + */ > > This makes it sound like we're just waiting for an event. > > > +int iw_cm_accept(struct iw_cm_id *cm_id, > > + struct iw_cm_conn_param *iw_param) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + struct ib_qp *qp; > > + unsigned long flags; > > + int ret; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + return -EINVAL; > > + } > > + /* Get the ib_qp given the QPN */ > > + qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); > > + if (!qp) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + return -EINVAL; > > + } > > + cm_id->device->iwcm->add_ref(qp); > > + cm_id_priv->qp = qp; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + > > + ret = cm_id->device->iwcm->accept(cm_id, iw_param); > > + if (ret) { > > + /* An error on accept precludes provider events */ > > + BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV); > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->qp) { > > + cm_id->device->iwcm->rem_ref(qp); > > + cm_id_priv->qp = NULL; > > + } > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + printk("Accept failed, ret=%d\n", ret); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + } > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_accept); > > + > > +/* > > + * Active Side: CM_ID <-- CONN_SENT > > + * > > + * If successful, results in the generation of a CONNECT_REPLY > > + * event. iw_cm_disconnect and iw_cm_destroy will block until the > > + * CONNECT_REPLY event is received from the provider. > > + */ > > +int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) > > +{ > > + struct iwcm_id_private *cm_id_priv; > > + int ret = 0; > > + unsigned long flags; > > + struct ib_qp *qp; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->state != IW_CM_STATE_IDLE) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + return -EINVAL; > > + } > > + > > + /* Get the ib_qp given the QPN */ > > + qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); > > + if (!qp) { > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + return -EINVAL; > > + } > > + cm_id->device->iwcm->add_ref(qp); > > + cm_id_priv->qp = qp; > > + cm_id_priv->state = IW_CM_STATE_CONN_SENT; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + > > + ret = cm_id->device->iwcm->connect(cm_id, iw_param); > > + if (ret) { > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->qp) { > > + cm_id->device->iwcm->rem_ref(qp); > > + cm_id_priv->qp = NULL; > > + } > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT); > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + printk("Connect failed, ret=%d\n", ret); > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + wake_up_all(&cm_id_priv->connect_wait); > > + } > > + > > + return ret; > > +} > > +EXPORT_SYMBOL(iw_cm_connect); > > + > > +/* > > + * Passive Side: new CM_ID <-- CONN_RECV > > + * > > + * Handles an inbound connect request. The function creates a new > > + * iw_cm_id to represent the new connection and inherits the client > > + * callback function and other attributes from the listening parent. > > + * > > + * The work item contains a pointer to the listen_cm_id and the event. The > > + * listen_cm_id contains the client cm_handler, context and > > + * device. These are copied when the device is cloned. The event > > + * contains the new four tuple. > > + * > > + * An error on the child should not affect the parent, so this > > + * function does not return a value. > > + */ > > +static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + struct iw_cm_id *cm_id; > > + struct iwcm_id_private *cm_id_priv; > > + int ret; > > + > > + /* The provider should never generate a connection request > > + * event with a bad status. > > + */ > > + BUG_ON(iw_event->status); > > + > > + /* We could be destroying the listening id. If so, ignore this > > + * upcall. */ > > + spin_lock_irqsave(&listen_id_priv->lock, flags); > > + if (listen_id_priv->state != IW_CM_STATE_LISTEN) { > > + spin_unlock_irqrestore(&listen_id_priv->lock, flags); > > + return; > > + } > > + spin_unlock_irqrestore(&listen_id_priv->lock, flags); > > + > > + cm_id = iw_create_cm_id(listen_id_priv->id.device, > > + listen_id_priv->id.cm_handler, > > + listen_id_priv->id.context); > > + /* If the cm_id could not be created, ignore the request */ > > + if (IS_ERR(cm_id)) > > + return; > > + > > + cm_id->provider_data = iw_event->provider_data; > > + cm_id->local_addr = iw_event->local_addr; > > + cm_id->remote_addr = iw_event->remote_addr; > > + > > + cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); > > + cm_id_priv->state = IW_CM_STATE_CONN_RECV; > > + > > + /* Call the client CM handler */ > > + ret = cm_id->cm_handler(cm_id, iw_event); > > + if (ret) { > > + printk("destroying child id %p, ret=%d\n", > > + cm_id, ret); > > We probably don't always want to print a message here. > > > + set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); > > + destroy_cm_id(cm_id); > > + if (atomic_read(&cm_id_priv->refcount)==0) > > + kfree(cm_id); > > + } > > +} > > + > > +/* > > + * Passive Side: CM_ID <-- ESTABLISHED > > + * > > + * The provider generated an ESTABLISHED event which means that > > + * the MPA negotion has completed successfully and we are now in MPA > > + * FPDU mode. > > + * > > + * This event can only be received in the CONN_RECV state. If the > > + * remote peer closed, the ESTABLISHED event would be received followed > > + * by the CLOSE event. If the app closes, it will block until we wake > > + * it up after processing this event. > > + */ > > +static int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + int ret = 0; > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + > > + /* We clear the CONNECT_WAIT bit here to allow the callback > > + * function to call iw_cm_disconnect. Calling iw_destroy_cm_id > > + * from a callback handler is not allowed */ > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_CONN_RECV: > > + cm_id_priv->state = IW_CM_STATE_ESTABLISHED; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); > > + break; > > + default: > > + BUG_ON(1); > > Can just BUG_ON the state and avoid the switch. Same comment applies below. > > > + } > > + wake_up_all(&cm_id_priv->connect_wait); > > + > > + return ret; > > +} > > + > > +/* > > + * Active Side: CM_ID <-- ESTABLISHED > > + * > > + * The app has called connect and is waiting for the established event to > > + * post it's requests to the server. This event will wake up anyone > > + * blocked in iw_cm_disconnect or iw_destroy_id. > > + */ > > +static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + int ret = 0; > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + /* Clear the connect wait bit so a callback function calling > > + * iw_cm_disconnect will not wait and deadlock this thread */ > > + clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); > > + switch (cm_id_priv->state) { > > + case IW_CM_STATE_CONN_SENT: > > + if (iw_event->status == IW_CM_EVENT_STATUS_ACCEPTED) { > > + cm_id_priv->id.local_addr = iw_event->local_addr; > > + cm_id_priv->id.remote_addr = iw_event->remote_addr; > > + cm_id_priv->state = IW_CM_STATE_ESTABLISHED; > > + } else { > > + /* REJECTED or RESET */ > > + cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); > > + cm_id_priv->qp = NULL; > > + cm_id_priv->state = IW_CM_STATE_IDLE; > > + } > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > + ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); > > + break; > > + default: > > + BUG_ON(1); > > + } > > + /* Wake up waiters on connect complete */ > > + wake_up_all(&cm_id_priv->connect_wait); > > + > > + return ret; > > +} > > + > > +/* > > + * CM_ID <-- CLOSING > > + * > > + * If in the ESTABLISHED state, move to CLOSING. > > + */ > > +static void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + > > + spin_lock_irqsave(&cm_id_priv->lock, flags); > > + if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED) > > + cm_id_priv->state = IW_CM_STATE_CLOSING; > > + spin_unlock_irqrestore(&cm_id_priv->lock, flags); > > +} > > + > > +/* > > + * CM_ID <-- IDLE > > + * > > + * If in the ESTBLISHED or CLOSING states, the QP will have have been > > + * moved by the provider to the ERR state. Disassociate the CM_ID from > > + * the QP, move to IDLE, and remove the 'connected' reference. > > + * > > + * If in some other state, the cm_id was destroyed asynchronously. > > + * This is the last reference that will result in waking up > > + * the app thread blocked in iw_destroy_cm_id. > > + */ > > +static int cm_close_handler(struct iwcm_id_private *cm_id_priv, > > + struct iw_cm_event *iw_event) > > +{ > > + unsigned long flags; > > + int ret = 0; > > + /* TT */printk("%s:%d cm_id_priv=%p, state=%d\n", > > + __FUNCTION__, __LINE__, > > + cm_id_priv,cm_id_priv->state); > > Will want to remove this. > > - Sean > _______________________________________________ > openib-general mailing list > openib-general@openib.org > http://openib.org/mailman/listinfo/openib-general > > To unsubscribe, please visit http://openib.org/mailman/listinfo/openib-general ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2006-06-02 13:57 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2006-06-01 22:28 [openib-general] Re: [PATCH 1/2] iWARP Connection Manager Caitlin Bestler 2006-06-02 13:57 ` Steve Wise -- strict thread matches above, loose matches on Subject: below -- 2006-05-31 18:26 [PATCH 0/2][RFC] iWARP Core Support Steve Wise 2006-05-31 18:26 ` [PATCH 1/2] iWARP Connection Manager Steve Wise 2006-05-31 22:22 ` Sean Hefty 2006-06-01 17:00 ` Steve Wise 2006-06-01 21:09 ` Sean Hefty 2006-06-01 22:21 ` [openib-general] " Tom Tucker 2006-06-01 17:11 ` Tom Tucker
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox