[openib-general][PATCH][kdapl]: evd upcall policy implementation
James Lentini
jlentini at netapp.com
Thu Aug 11 09:59:58 PDT 2005
Hi Guy,
Comments/questions below:
On Thu, 11 Aug 2005, Guy German wrote:
> Sorry for resending it - the former mail did not have a subject
>
> This patch allows the dapl consumer to control the evd upcall policy.
> Some consumers (e.g. ISER) receives one upcall, disable
> the upcall policy, and retrieve the rest of the events from a
> kernel_thread, via dat_evd_dequeue.
> This fashion of work improves performance by saving the context
> switching that is involved in many upcalls.
> If the consumer does not behave that way and leaves the upcall policy
> enabled at all times (e.g. kdapltest), the behavior will stay the same and
> the consumer will get an upcall for each event.
>
> Signed-off-by: Guy German <guyg at voltaire.com>
>
> Index: dapl_evd.c
> ===================================================================
> --- dapl_evd.c (revision 3056)
> +++ dapl_evd.c (working copy)
> @@ -38,28 +38,39 @@
> #include "dapl_ring_buffer_util.h"
>
> /*
> - * DAPL Internal routine to trigger the specified CNO.
> - * Called by the callback of some EVD associated with the CNO.
Thanks for catch these CNO references. I thought I had removed them
all.
> + * DAPL Internal routine to trigger the callback of the EVD
> */
> static void dapl_evd_upcall_trigger(struct dapl_evd *evd)
> {
> int status = 0;
> struct dat_event event;
> + unsigned long flags;
For flags, we use flags member in the dapl_common structure. Take a
look at the call to spin_lock_irqsave() in dapl_evd_get_event() for an
example. We use the flags in the structure because the EVD code takes
the spin lock in one function and releases it in another.
>
> - /* Only process events if there is an enabled callback function. */
> - if ((evd->upcall.upcall_func == (DAT_UPCALL_FUNC) NULL) ||
> - (evd->upcall_policy == DAT_UPCALL_DISABLE)) {
> +
> + /* The function is not re-entrant (change when implementing DAT_UPCALL_MANY)*/
Why is this function not re-entrant? For reference, here is how I
would define re-entrant:
http://en.wikipedia.org/wiki/Reentrant
http://foldoc.doc.ic.ac.uk/foldoc/foldoc.cgi?re-entrant
> + if (evd->is_triggered)
> return;
> - }
Why check the value here? Is it only for the efficiency of not taking
the spin lock when is_triggered is 1?
>
> - for (;;) {
> + spin_lock_irqsave (&evd->common.lock, flags);
> + if (evd->is_triggered) {
> + spin_unlock_irqrestore (&evd->common.lock, flags);
> + return;
> + }
> + evd->is_triggered = 1;
> + spin_unlock_irqrestore (&evd->common.lock, flags);
> + /* Only process events if there is an enabled callback function */
> + while ((evd->upcall.upcall_func != (DAT_UPCALL_FUNC)NULL) &&
> + (evd->upcall_policy != DAT_UPCALL_TEARDOWN) &&
> + (evd->upcall_policy != DAT_UPCALL_DISABLE)) {
> status = dapl_evd_dequeue((struct dat_evd *)evd, &event);
> - if (0 != status)
> - return;
> -
> + if (status)
> + break;
> evd->upcall.upcall_func(evd->upcall.instance_data, &event,
> FALSE);
> }
> + evd->is_triggered = 0;
> +
> + return;
> }
>
> static void dapl_evd_eh_print_wc(struct ib_wc *wc)
> @@ -820,24 +831,19 @@ static void dapl_evd_dto_callback(struct
> * This function does not dequeue from the CQ; only the consumer
> * can do that. Instead, it wakes up waiters if any exist.
> * It rearms the completion only if completions should always occur
> - * (specifically if a CNO is associated with the EVD and the
> - * EVD is enabled).
> */
> -
> - if (state == DAPL_EVD_STATE_OPEN &&
> - evd->upcall_policy != DAT_UPCALL_DISABLE) {
> - /*
> - * Re-enable callback, *then* trigger.
> - * This guarantees we won't miss any events.
> - */
> - status = ib_req_notify_cq(evd->cq, IB_CQ_NEXT_COMP);
> - if (0 != status)
> - (void)dapl_evd_post_async_error_event(
> - evd->common.owner_ia->async_error_evd,
> - DAT_ASYNC_ERROR_PROVIDER_INTERNAL_ERROR,
> - evd->common.owner_ia);
> -
> +
> + if (state == DAPL_EVD_STATE_OPEN) {
> dapl_evd_upcall_trigger(evd);
> + if ((evd->upcall_policy != DAT_UPCALL_TEARDOWN) &&
> + (evd->upcall_policy != DAT_UPCALL_DISABLE)) {
> + status = ib_req_notify_cq(evd->cq, IB_CQ_NEXT_COMP);
> + if (0 != status)
> + (void)dapl_evd_post_async_error_event(
> + evd->common.owner_ia->async_error_evd,
> + DAT_ASYNC_ERROR_PROVIDER_INTERNAL_ERROR,
> + evd->common.owner_ia);
> + }
You changed the order in which the CQ upcall is enabled and the kDAPL
upcall is made. It used to be:
enable CQ upcall
call kDAPL upcall
you are proposing
call kDAPL upcall
enable CQ upcall
I think your proposed order contains a race condition. Specifically if
a work completion occurs after dapl_evd_upcall_trigger()
returns but before the CQ upcall is re-enabled with
ib_req_notify_cq(), no upcall will occur for the completion.
Do you agree?
> }
> dapl_dbg_log(DAPL_DBG_TYPE_RTN, "%s() returns\n", __func__);
> }
> @@ -890,7 +896,7 @@ int dapl_evd_internal_create(struct dapl
>
> /* reset the qlen in the attributes, it may have changed */
> evd->qlen = evd->cq->cqe;
> -
> + evd->is_triggered = 0;
This should be done in dapl_evd_alloc.
> status = ib_req_notify_cq(evd->cq, IB_CQ_NEXT_COMP);
>
> if (status != 0)
> @@ -1035,15 +1041,41 @@ int dapl_evd_modify_upcall(struct dat_ev
> const struct dat_upcall_object *upcall)
> {
> struct dapl_evd *evd;
> -
> - dapl_dbg_log(DAPL_DBG_TYPE_API, "dapl_modify_upcall(%p)\n", evd_handle);
> + int status = 0;
> + int pending_events;
> + unsigned long flags;
See my comment above about he flags.
>
> evd = (struct dapl_evd *)evd_handle;
> + dapl_dbg_log (DAPL_DBG_TYPE_API, "%s: (evd=%p) set to %d\n",
> + __func__, evd_handle, upcall_policy);
The idea was to make the DAPL_DBG_TYPE_API prints look like a
debugger stack trace. The following would be keeping with the other
print statements:
+ dapl_dbg_log (DAPL_DBG_TYPE_API, "%s(%p, %d, %p)\n",
+ __func__, evd_handle, upcall_policy, upcall);
>
> + spin_lock_irqsave(&evd->common.lock, flags);
> + if ((upcall_policy != DAT_UPCALL_TEARDOWN) &&
> + (upcall_policy != DAT_UPCALL_DISABLE)) {
Why not let the consumer setup the upcall when it disabled? That seems
like the only safe time to modify it.
> + pending_events = dapl_rbuf_count(&evd->pending_event_queue);
I don't understand this restriction either. Please explain.
> + if (pending_events) {
> + dapl_dbg_log (DAPL_DBG_TYPE_WARN,
> + "%s: (evd %p) there are still %d pending "
> + "events in the queue - policy stays disabled\n",
> + __func__, evd_handle, pending_events);
> + status = -EBUSY;
> + goto bail;
> + }
> + if (evd->evd_flags & DAT_EVD_DTO_FLAG) {
> + status = ib_req_notify_cq(evd->cq, IB_CQ_NEXT_COMP);
Why do we need to re-enable the CQ upcall?
> + if (status) {
> + printk(KERN_ERR "%s: dapls_ib_completion_notify"
> + " failed (status=0x%x) \n",__func__,
> + status);
Let's use dapl_dbg_log instead of printk. We can also update the text
of the message to
"%s: ib_req_notify_cq failed: %X\n"
> + goto bail;
> + }
> + }
> + }
> evd->upcall_policy = upcall_policy;
> evd->upcall = *upcall;
> -
> - return 0;
> +bail:
> + spin_unlock_irqrestore(&evd->common.lock, flags);
> + return status;
> }
>
> int dapl_evd_post_se(struct dat_evd *evd_handle, const struct dat_event *event)
> @@ -1076,7 +1108,7 @@ int dapl_evd_post_se(struct dat_evd *evd
> event->event_data.
> software_event_data.pointer);
>
> - bail:
> +bail:
> return status;
> }
>
> @@ -1124,7 +1156,7 @@ int dapl_evd_dequeue(struct dat_evd *evd
> }
>
> spin_unlock_irqrestore(&evd->common.lock, evd->common.flags);
> - bail:
> +bail:
> dapl_dbg_log(DAPL_DBG_TYPE_RTN,
> "dapl_evd_dequeue () returns 0x%x\n", status);
>
More information about the general
mailing list