On Mon, Nov 30, 2020 at 9:18 PM Han Zhou <[email protected]> wrote:
>
>
>
> On Mon, Nov 30, 2020 at 11:56 AM Ilya Maximets <[email protected]> wrote:
> >
> > On 11/30/20 8:45 PM, Han Zhou wrote:
> > >
> > >
> > > On Mon, Nov 30, 2020 at 8:41 AM Dumitru Ceara <[email protected]
> > > <mailto:[email protected]>> wrote:
> > >>
> > >> It's possible that the IDL client processes multiple jsonrpc updates
> > >> in a single ovsdb_idl_run().
> > >>
> > >> Considering the following updates processed in a single IDL run:
> > >> 1. Update row R1 from table A while R1 is also referenced by row R2 from
> > >> table B:
> > >> - this adds R1 to table A's track_list.
> > >> 2. Delete row R1 from table A while R1 is also referenced by row R2 from
> > >> table B:
> > >> - because row R2 still refers to row R1, this will create an orphan
> > >> R1.
> > >> - at this point R1 is still in table A's hmap.
> > >>
> > >> When the IDL client calls ovsdb_idl_track_clear() after it has finished
> > >> processing the tracked changes, row R1 gets freed leaving a dangling
> > >> pointer in table A's hmap.
> > >>
> > >> To fix this we don't free rows in ovsdb_idl_track_clear() if they are
> > >> orphan and still referenced by other rows, i.e., the row's 'dst_arcs'
> > >> list is not empty. Later, when all arc sources (e.g., R2) are
> > >> deleted, the orphan R1 will be cleaned up as well.
> > >>
> > >> The only exception is when the whole contents of the IDL are flushed,
> > >> in ovsdb_idl_db_clear(), in which case it's safe to free all rows.
> > >>
> > >> Reported-by: Ilya Maximets <[email protected]
> > >> <mailto:[email protected]>>
> > >> Fixes: 932104f483ef ("ovsdb-idl: Add support for change tracking.")
> > >> Signed-off-by: Dumitru Ceara <[email protected]
> > >> <mailto:[email protected]>>
> > >> ---
> > >> lib/ovsdb-idl.c | 21 ++++++++++++++++-----
> > >> 1 file changed, 16 insertions(+), 5 deletions(-)
> > >>
> > >> diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
> > >> index e0c9833..0841a2e 100644
> > >> --- a/lib/ovsdb-idl.c
> > >> +++ b/lib/ovsdb-idl.c
> > >> @@ -221,7 +221,7 @@ struct ovsdb_idl_db {
> > >> struct uuid last_id;
> > >> };
> > >>
> > >> -static void ovsdb_idl_db_track_clear(struct ovsdb_idl_db *);
> > >> +static void ovsdb_idl_db_track_clear(struct ovsdb_idl_db *, bool
> > >> flush_all);
> > >> static void ovsdb_idl_db_add_column(struct ovsdb_idl_db *,
> > >> const struct ovsdb_idl_column *);
> > >> static void ovsdb_idl_db_omit(struct ovsdb_idl_db *,
> > >> @@ -660,7 +660,7 @@ ovsdb_idl_db_clear(struct ovsdb_idl_db *db)
> > >> ovsdb_idl_row_destroy_postprocess(db);
> > >>
> > >> db->cond_seqno = 0;
> > >> - ovsdb_idl_db_track_clear(db);
> > >> + ovsdb_idl_db_track_clear(db, true);
> > >>
> > >> if (changed) {
> > >> db->change_seqno++;
> > >> @@ -1955,7 +1955,7 @@ ovsdb_idl_track_is_updated(const struct
> > >> ovsdb_idl_row *row,
> > >> * loop when it is ready to do ovsdb_idl_run() again.
> > >> */
> > >> static void
> > >> -ovsdb_idl_db_track_clear(struct ovsdb_idl_db *db)
> > >> +ovsdb_idl_db_track_clear(struct ovsdb_idl_db *db, bool flush_all)
> > >> {
> > >> size_t i;
> > >>
> > >> @@ -1989,7 +1989,18 @@ ovsdb_idl_db_track_clear(struct ovsdb_idl_db *db)
> > >> free(row->tracked_old_datum);
> > >> row->tracked_old_datum = NULL;
> > >> }
> > >> - free(row);
> > >> +
> > >> + /* Rows that were reused as orphan after being
> > >> processed
> > >> + * for deletion are still in the table hmap and
> > >> will be
> > >> + * cleaned up when their src arcs are removed.
> > >> + *
> > >> + * The exception is when 'destroy' is explicitly
> > >> set to
> > >> + * 'true' which usually happens when the complete
> > >> IDL
> > >> + * contents are being flushed.
> > >> + */
> > >> + if (flush_all || ovs_list_is_empty(&row->dst_arcs))
> > >> {
> > >> + free(row);
> > >> + }
> > >
> > > Here is a problem. The row R1 is now unparsed and datum freed. However,
> > > when R2 is deleted, R1 will be pushed to the tracked change list again
> > > and when the user examines it in tracked changes and accesses the old
> > > data, it will crash.
> >
> > It is an ophan row and, also, tracked_old_datum already freed,
> > so it should not be iterable. See:
> > f0d23f67954c ("ovsdb-idl: Fix iteration over tracked rows with no actual
> > data.")
> >
> Thanks Ilya for the explanation. This makes sense. So the row becomes a "pure
> orphaned row" after being handled here in ovsdb_idl_db_track_clear(). I
> wonder if some comments should be added to the code to clarify these tricky
> scenarios.
>
Hi Han, Ilya,
Thanks for the reviews!
Would the following incremental work for you to better document the behavior?
Thanks,
Dumitru
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
index 0841a2e..50d790f 100644
--- a/lib/ovsdb-idl.c
+++ b/lib/ovsdb-idl.c
@@ -1992,7 +1992,9 @@ ovsdb_idl_db_track_clear(struct ovsdb_idl_db
*db, bool flush_all)
/* Rows that were reused as orphan after being processed
* for deletion are still in the table hmap and will be
- * cleaned up when their src arcs are removed.
+ * cleaned up when their src arcs are removed. These rows
+ * will not be reported anymore as "deleted" to IDL
+ * clients.
*
* The exception is when 'destroy' is explicitly set to
* 'true' which usually happens when the complete IDL
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev