[ovs-dev] [PATCH V2 2/2] dpif-netdev: Purge all ukeys when reconfigure pmd.

Joe Stringer joestringer at nicira.com
Fri Aug 28 17:51:47 UTC 2015


On 28 August 2015 at 09:41, Daniele Di Proietto <diproiettod at vmware.com> wrote:
> I've tested it and it appears to correctly keep all the stats.
>
> Two comments inline, otherwise:
>
> Acked-by: Daniele Di Proietto <diproiettod at vmware.com>

That could even be... dare I say it... "Tested-by: ..." :-)


In general this patch seems fine. I'm not entirely confident what the
worst case behaviour is though - for instance if revalidators are
crazy busy (eg disable megaflows with port scan) and you reconfigure
pmd threads, does it handle this gracefully? As far as I can tell,
there is nothing stopping a revalidator from trying to delete the same
ukey+flow at the same time. Also, typical purging like this happens
independently by each revalidator thread where no thread can handle
the same ukeys as another thread. This instead goes through all cmaps
and picks out those with the current pmd id.

The safest thing is to stop the world (ie, at least stop revalidators
from processing further, if not completely disable the threads then
re-enable when it's safe again), and an approach like that seems
simpler.


> On 28/08/2015 06:25, "Alex Wang" <ee07b291 at gmail.com> wrote:
>
>>When dpdk configuration changes, all pmd threads are recreated
>>and rx queues of each port are reloaded.  After this process,
>>rx queue could be mapped to a different pmd thread other than
>>the one before reconfiguration.  However, this is totally
>>transparent to ofproto layer modules.  So, if the ofproto-dpif-upcall
>>module still holds ukeys generated before pmd thread recreation,
>>this old ukey will collide with the ukey for the new upcalls
>>from same traffic flow, causing flow installation failure.
>>
>>To fix the bug, this commit adds a new call-back function
>>in dpif layer for notifying upper layer the purging of datapath
>>(e.g. pmd thread deletion in dpif-netdev).  So, the
>>ofproto-dpif-upcall module can react properly with deleting
>>the ukeys and with collecting flows' last stats.
>>
>>Reported-by: Ilya Maximets <i.maximets at samsung.com>
>>Signed-off-by: Alex Wang <ee07b291 at gmail.com>
>>
>>---
>>PATCH->V2:
>>- PATCH does not fix the problem,
>>- call the purge_cb between pmd thread stopping and deletion.
>>  (which should be the right place)
>>---
>> lib/dpif-netdev.c             | 23 ++++++++++++++++++++---
>> lib/dpif-netlink.c            |  1 +
>> lib/dpif-provider.h           | 11 ++++++++++-
>> lib/dpif.c                    |  8 ++++++++
>> lib/dpif.h                    | 13 +++++++++++++
>> ofproto/ofproto-dpif-upcall.c | 36 ++++++++++++++++++++++++++++++++++++
>> 6 files changed, 88 insertions(+), 4 deletions(-)
>>
>>diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
>>index f841876..2d187da 100644
>>--- a/lib/dpif-netdev.c
>>+++ b/lib/dpif-netdev.c
>>@@ -204,6 +204,11 @@ struct dp_netdev {
>>     upcall_callback *upcall_cb;  /* Callback function for executing
>>upcalls. */
>>     void *upcall_aux;
>>
>>+    /* Callback function for notifying the purging of dp flows (during
>>+     * reseting pmd deletion). */
>>+    dp_purge_callback *dp_purge_cb;
>>+    void *dp_purge_aux;
>>+
>>     /* Stores all 'struct dp_netdev_pmd_thread's. */
>>     struct cmap poll_threads;
>>
>>@@ -2851,7 +2856,7 @@ dp_netdev_destroy_pmd(struct dp_netdev_pmd_thread
>>*pmd)
>> /* Stops the pmd thread, removes it from the 'dp->poll_threads',
>>  * and unrefs the struct. */
>> static void
>>-dp_netdev_del_pmd(struct dp_netdev_pmd_thread *pmd)
>>+dp_netdev_del_pmd(struct dp_netdev *dp, struct dp_netdev_pmd_thread *pmd)
>> {
>>     /* Uninit the 'flow_cache' since there is
>>      * no actual thread uninit it for NON_PMD_CORE_ID. */
>>@@ -2863,6 +2868,8 @@ dp_netdev_del_pmd(struct dp_netdev_pmd_thread *pmd)
>>         ovs_numa_unpin_core(pmd->core_id);
>>         xpthread_join(pmd->thread, NULL);
>>     }
>>+    /* Purges the 'pmd''s flows after stoping the thread. */
>>+    dp->dp_purge_cb(dp->dp_purge_aux, pmd->core_id);
>
> There's still the possibility a datapath is created through appctl
> dpctl/add-dp.
> In this case dp_purge_cb is NULL and a couple of tests fail (789, 790).
> Adding a check should be enough:
>
>     /* Purges the 'pmd''s flows after stopping the thread. */
>     if (dp->dp_purge_cb) {
>         dp->dp_purge_cb(dp->dp_purge_aux, pmd->core_id);
>     }
>
>
>
>
>>     cmap_remove(&pmd->dp->poll_threads, &pmd->node,
>>hash_int(pmd->core_id, 0));
>>     dp_netdev_pmd_unref(pmd);
>> }
>>@@ -2874,7 +2881,7 @@ dp_netdev_destroy_all_pmds(struct dp_netdev *dp)
>>     struct dp_netdev_pmd_thread *pmd;
>>
>>     CMAP_FOR_EACH (pmd, node, &dp->poll_threads) {
>>-        dp_netdev_del_pmd(pmd);
>>+        dp_netdev_del_pmd(dp, pmd);
>>     }
>> }
>>
>>@@ -2886,7 +2893,7 @@ dp_netdev_del_pmds_on_numa(struct dp_netdev *dp,
>>int numa_id)
>>
>>     CMAP_FOR_EACH (pmd, node, &dp->poll_threads) {
>>         if (pmd->numa_id == numa_id) {
>>-            dp_netdev_del_pmd(pmd);
>>+            dp_netdev_del_pmd(dp, pmd);
>>         }
>>     }
>> }
>>@@ -3391,6 +3398,15 @@ struct dp_netdev_execute_aux {
>> };
>>
>> static void
>>+dpif_netdev_register_dp_purge_cb(struct dpif *dpif, dp_purge_callback
>>*cb,
>>+                                 void *aux)
>>+{
>>+    struct dp_netdev *dp = get_dp_netdev(dpif);
>>+    dp->dp_purge_aux = aux;
>>+    dp->dp_purge_cb = cb;
>>+}
>>+
>>+static void
>> dpif_netdev_register_upcall_cb(struct dpif *dpif, upcall_callback *cb,
>>                                void *aux)
>> {
>>@@ -3637,6 +3653,7 @@ const struct dpif_class dpif_netdev_class = {
>>     NULL,                       /* recv */
>>     NULL,                       /* recv_wait */
>>     NULL,                       /* recv_purge */
>>+    dpif_netdev_register_dp_purge_cb,
>>     dpif_netdev_register_upcall_cb,
>>     dpif_netdev_enable_upcall,
>>     dpif_netdev_disable_upcall,
>>diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
>>index b8c27d8..ffeb124 100644
>>--- a/lib/dpif-netlink.c
>>+++ b/lib/dpif-netlink.c
>>@@ -2309,6 +2309,7 @@ const struct dpif_class dpif_netlink_class = {
>>     dpif_netlink_recv,
>>     dpif_netlink_recv_wait,
>>     dpif_netlink_recv_purge,
>>+    NULL,                       /* register_dp_purge_cb */
>>     NULL,                       /* register_upcall_cb */
>>     NULL,                       /* enable_upcall */
>>     NULL,                       /* disable_upcall */
>>diff --git a/lib/dpif-provider.h b/lib/dpif-provider.h
>>index 28ea86f..5415897 100644
>>--- a/lib/dpif-provider.h
>>+++ b/lib/dpif-provider.h
>>@@ -361,13 +361,22 @@ struct dpif_class {
>>      * return. */
>>     void (*recv_purge)(struct dpif *dpif);
>>
>>+    /* When 'dpif' is about to purge the datapath, the higher layer may
>>want
>>+     * to be notified so that it could try reacting accordingly (e.g.
>>grabbing
>>+     * all flow stats before they are gone).
>>+     *
>>+     * Registers an upcall callback function with 'dpif'.  This is only
>>used
>>+     * if 'dpif' needs to notify the purging of datapath.  'aux' is
>>passed to
>>+     * the callback on invocation. */
>>+    void (*register_dp_purge_cb)(struct dpif *, dp_purge_callback *,
>>void *aux);
>>+
>>     /* For datapaths that run in userspace (i.e. dpif-netdev), threads
>>polling
>>      * for incoming packets can directly call upcall functions instead of
>>      * offloading packet processing to separate handler threads.
>>Datapaths
>>      * that directly call upcall functions should use the functions
>>below to
>>      * to register an upcall function and enable / disable upcalls.
>>      *
>>-     * Registers an upcall callback function with 'dpif'. This is only
>>used if
>>+     * Registers an upcall callback function with 'dpif'. This is only
>>used
>>      * if 'dpif' directly executes upcall functions. 'aux' is passed to
>>the
>>      * callback on invocation. */
>>     void (*register_upcall_cb)(struct dpif *, upcall_callback *, void
>>*aux);
>>diff --git a/lib/dpif.c b/lib/dpif.c
>>index 6e26f30..9a67a24 100644
>>--- a/lib/dpif.c
>>+++ b/lib/dpif.c
>>@@ -1346,6 +1346,14 @@ dpif_handlers_set(struct dpif *dpif, uint32_t
>>n_handlers)
>> }
>>
>> void
>>+dpif_register_dp_purge_cb(struct dpif *dpif, dp_purge_callback *cb, void
>>*aux)
>>+{
>>+    if (dpif->dpif_class->register_dp_purge_cb) {
>>+        dpif->dpif_class->register_dp_purge_cb(dpif, cb, aux);
>>+    }
>>+}
>>+
>>+void
>> dpif_register_upcall_cb(struct dpif *dpif, upcall_callback *cb, void
>>*aux)
>> {
>>     if (dpif->dpif_class->register_upcall_cb) {
>>diff --git a/lib/dpif.h b/lib/dpif.h
>>index ea9caf8..1b8a7b9 100644
>>--- a/lib/dpif.h
>>+++ b/lib/dpif.h
>>@@ -787,6 +787,19 @@ struct dpif_upcall {
>>     struct nlattr *actions;    /* Argument to OVS_ACTION_ATTR_USERSPACE.
>>*/
>> };
>>
>>+/* A callback to notify higher layer of dpif about to be purged, so that
>>+ * higher layer could try reacting to this (e.g. grabbing all flow stats
>>+ * before they are gone).  This function is currently implemented only by
>>+ * dpif-netdev.
>>+ *
>>+ * The caller needs to provide the 'aux' pointer passed down by higher
>>+ * layer from the dpif_register_notify_cb() function and the 'pmd_id' of
>>+ * the polling thread.
>>+ */
>>+    typedef void dp_purge_callback(void *aux, unsigned pmd_id);
>>+
>>+void dpif_register_dp_purge_cb(struct dpif *, dp_purge_callback *, void
>>*aux);
>>+
>> /* A callback to process an upcall, currently implemented only by
>>dpif-netdev.
>>  *
>>  * The caller provides the 'packet' and 'flow' to process, the
>>corresponding
>>diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
>>index 419fd1a..5795c19 100644
>>--- a/ofproto/ofproto-dpif-upcall.c
>>+++ b/ofproto/ofproto-dpif-upcall.c
>>@@ -316,6 +316,7 @@ static int upcall_receive(struct upcall *, const
>>struct dpif_backer *,
>> static void upcall_uninit(struct upcall *);
>>
>> static upcall_callback upcall_cb;
>>+static dp_purge_callback dp_purge_cb;
>>
>> static atomic_bool enable_megaflows = ATOMIC_VAR_INIT(true);
>> static atomic_bool enable_ufid = ATOMIC_VAR_INIT(true);
>>@@ -368,6 +369,7 @@ udpif_create(struct dpif_backer *backer, struct dpif
>>*dpif)
>>     }
>>
>>     dpif_register_upcall_cb(dpif, upcall_cb, udpif);
>>+    dpif_register_dp_purge_cb(dpif, dp_purge_cb, udpif);
>>
>>     return udpif;
>> }
>>@@ -2195,6 +2197,40 @@ revalidator_purge(struct revalidator *revalidator)
>> {
>>     revalidator_sweep__(revalidator, true);
>> }
>>+
>>+/* In reaction to dpif purge, purges all 'ukey's with same 'pmd_id'. */
>>+static void
>>+dp_purge_cb(void *aux, unsigned pmd_id)
>>+{
>>+    struct udpif *udpif = aux;
>>+    size_t i;
>>+
>>+    for (i = 0; i < N_UMAPS; i++) {
>>+        uint64_t odp_actions_stub[1024 / 8];
>>+        struct ofpbuf odp_actions =
>>OFPBUF_STUB_INITIALIZER(odp_actions_stub);
>
> I think these variables above are unnecessary.
>
>>+        struct ukey_op ops[REVALIDATE_MAX_BATCH];
>>+        struct udpif_key *ukey;
>>+        struct umap *umap = &udpif->ukeys[i];
>>+        size_t n_ops = 0;
>>+
>>+        CMAP_FOR_EACH(ukey, cmap_node, &umap->cmap) {
>>+             if (ukey->pmd_id == pmd_id) {
>>+                delete_op_init(udpif, &ops[n_ops++], ukey);
>>+                if (n_ops == REVALIDATE_MAX_BATCH) {
>>+                    push_ukey_ops(udpif, umap, ops, n_ops);
>>+                    n_ops = 0;
>>+                }
>>+            }
>>+        }
>>+
>>+        if (n_ops) {
>>+            push_ukey_ops(udpif, umap, ops, n_ops);
>>+        }
>>+
>>+        ofpbuf_uninit(&odp_actions);
>>+        ovsrcu_quiesce();
>>+    }
>>+}
>> ?
>> static void
>> upcall_unixctl_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
>>--
>>1.9.1
>>
>



More information about the dev mailing list