On 1/10/24 14:12, Dumitru Ceara wrote:
> On 1/9/24 20:54, Ilya Maximets wrote:
>> Database file contains the column diff, but it is discarded once
>> the 'new' state of a row is constructed. Keep it in the transaction
>> row, as it can be used later by other parts of the code.
>>
>> Diffs do not live long, we keep them around only while transaction
>> is alive, so should not affect memory consumption.
>>
>> Users for this data will be added in later commits.
>>
>> Acked-by: Mike Pattrick <[email protected]>
>> Signed-off-by: Ilya Maximets <[email protected]>
>> ---
>> ovsdb/execution.c | 14 ++++++++---
>> ovsdb/file.c | 22 +++++++++++-----
>> ovsdb/ovsdb-server.c | 7 ++++--
>> ovsdb/table.c | 6 +++--
>> ovsdb/transaction.c | 60 +++++++++++++++++++++++++++++++++-----------
>> ovsdb/transaction.h | 6 +++--
>> tests/test-ovsdb.c | 2 +-
>> 7 files changed, 86 insertions(+), 31 deletions(-)
>>
>> diff --git a/ovsdb/execution.c b/ovsdb/execution.c
>> index 5587ef96f..8c20c3b54 100644
>> --- a/ovsdb/execution.c
>> +++ b/ovsdb/execution.c
>> @@ -490,9 +490,11 @@ update_row_cb(const struct ovsdb_row *row, void *ur_)
>>
>> ur->n_matches++;
>> if (!ovsdb_row_equal_columns(row, ur->row, ur->columns)) {
>> + struct ovsdb_row *rw_row;
>> +
>> + ovsdb_txn_row_modify(ur->txn, row, &rw_row, NULL);
>> ovsdb_error_assert(ovsdb_row_update_columns(
>> - ovsdb_txn_row_modify(ur->txn, row),
>> - ur->row, ur->columns, false));
>> + rw_row, ur->row, ur->columns, false));
>> }
>>
>> return true;
>> @@ -572,10 +574,14 @@ static bool
>> mutate_row_cb(const struct ovsdb_row *row, void *mr_)
>> {
>> struct mutate_row_cbdata *mr = mr_;
>> + struct ovsdb_row *rw_row;
>> +
>> + /* Not trying to track the row diff here, because user transactions
>> + * may attempt to add duplicates or remove elements that do not exist.
>> */
>> + ovsdb_txn_row_modify(mr->txn, row, &rw_row, NULL);
>>
>> mr->n_matches++;
>> - *mr->error = ovsdb_mutation_set_execute(ovsdb_txn_row_modify(mr->txn,
>> row),
>> - mr->mutations);
>> + *mr->error = ovsdb_mutation_set_execute(rw_row, mr->mutations);
>> return *mr->error == NULL;
>> }
>>
>> diff --git a/ovsdb/file.c b/ovsdb/file.c
>> index 8bd1d4af3..77a89fd1a 100644
>> --- a/ovsdb/file.c
>> +++ b/ovsdb/file.c
>> @@ -80,8 +80,8 @@ ovsdb_file_column_diff_disable(void)
>> }
>>
>> static struct ovsdb_error *
>> -ovsdb_file_update_row_from_json(struct ovsdb_row *row, bool converting,
>> - bool row_contains_diff,
>> +ovsdb_file_update_row_from_json(struct ovsdb_row *row, struct ovsdb_row
>> *diff,
>> + bool converting, bool row_contains_diff,
>> const struct json *json)
>> {
>> struct ovsdb_table_schema *schema = row->table->schema;
>> @@ -122,6 +122,12 @@ ovsdb_file_update_row_from_json(struct ovsdb_row *row,
>> bool converting,
>> error = ovsdb_datum_apply_diff_in_place(
>> &row->fields[column->index],
>> &datum, &column->type);
>> + if (!error && diff) {
>> +
>> ovs_assert(ovsdb_datum_is_default(&diff->fields[column->index],
>> + &column->type));
>> + ovsdb_datum_swap(&diff->fields[column->index], &datum);
>> + }
>> +
>> ovsdb_datum_destroy(&datum, &column->type);
>> if (error) {
>> return error;
>> @@ -150,16 +156,20 @@ ovsdb_file_txn_row_from_json(struct ovsdb_txn *txn,
>> struct ovsdb_table *table,
>> ovsdb_txn_row_delete(txn, row);
>> return NULL;
>> } else if (row) {
>> - return ovsdb_file_update_row_from_json(ovsdb_txn_row_modify(txn,
>> row),
>> - converting,
>> row_contains_diff,
>> - json);
>> + struct ovsdb_row *new, *diff = NULL;
>> +
>> + ovsdb_txn_row_modify(txn, row, &new,
>> + row_contains_diff ? &diff : NULL);
>> + return ovsdb_file_update_row_from_json(new, diff, converting,
>> + row_contains_diff, json);
>> } else {
>> struct ovsdb_error *error;
>> struct ovsdb_row *new;
>>
>> new = ovsdb_row_create(table);
>> *ovsdb_row_get_uuid_rw(new) = *row_uuid;
>> - error = ovsdb_file_update_row_from_json(new, converting, false,
>> json);
>> + error = ovsdb_file_update_row_from_json(new, NULL, converting,
>> + false, json);
>> if (error) {
>> ovsdb_row_destroy(new);
>> } else {
>> diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
>> index 4d29043f4..dbf85fe3b 100644
>> --- a/ovsdb/ovsdb-server.c
>> +++ b/ovsdb/ovsdb-server.c
>> @@ -1111,7 +1111,7 @@ update_remote_row(const struct ovsdb_row *row, struct
>> ovsdb_txn *txn,
>> /* Bad remote spec or incorrect schema. */
>> return;
>> }
>> - rw_row = ovsdb_txn_row_modify(txn, row);
>> + ovsdb_txn_row_modify(txn, row, &rw_row, NULL);
>> ovsdb_jsonrpc_server_get_remote_status(jsonrpc, target, &status);
>>
>> /* Update status information columns. */
>> @@ -1301,7 +1301,10 @@ update_server_status(struct shash *all_dbs)
>> if (!db || !db->db) {
>> ovsdb_txn_row_delete(txn, row);
>> } else {
>> - update_database_status(ovsdb_txn_row_modify(txn, row), db);
>> + struct ovsdb_row *rw_row;
>> +
>> + ovsdb_txn_row_modify(txn, row, &rw_row, NULL);
>> + update_database_status(rw_row, db);
>> }
>> }
>>
>> diff --git a/ovsdb/table.c b/ovsdb/table.c
>> index 0792e1580..3e89ddd44 100644
>> --- a/ovsdb/table.c
>> +++ b/ovsdb/table.c
>> @@ -415,8 +415,10 @@ ovsdb_table_execute_update(struct ovsdb_txn *txn, const
>> struct uuid *row_uuid,
>> NULL, &columns, xor);
>>
>> if (!error && (xor || !ovsdb_row_equal_columns(row, update, &columns)))
>> {
>> - error = ovsdb_row_update_columns(ovsdb_txn_row_modify(txn, row),
>> - update, &columns, xor);
>> + struct ovsdb_row *rw_row;
>> +
>> + ovsdb_txn_row_modify(txn, row, &rw_row, NULL);
>> + error = ovsdb_row_update_columns(rw_row, update, &columns, xor);
>> }
>>
>> ovsdb_column_set_destroy(&columns);
>> diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
>> index a482588a0..b69d03b5a 100644
>> --- a/ovsdb/transaction.c
>> +++ b/ovsdb/transaction.c
>> @@ -72,6 +72,8 @@ struct ovsdb_txn_table {
>> * 'new'.
>> *
>> * - A row modified by a transaction will have non-null 'old' and
>> 'new'.
>> + * It may have non-null 'diff' as well in this case, but it is not
>> + * necessary.
>> *
>> * - 'old' and 'new' both null indicates that a row was added then
>> deleted
>> * within a single transaction. Most of the time we instead delete
>> the
>> @@ -83,6 +85,7 @@ struct ovsdb_txn_row {
>> struct hmap_node hmap_node; /* In ovsdb_txn_table's txn_rows hmap. */
>> struct ovsdb_row *old; /* The old row. */
>> struct ovsdb_row *new; /* The new row. */
>> + struct ovsdb_row *diff; /* The difference between old and new. */
>> size_t n_refs; /* Number of remaining references. */
>>
>> /* These members are the same as the corresponding members of 'old' or
>> @@ -155,6 +158,7 @@ ovsdb_txn_row_abort(struct ovsdb_txn *txn OVS_UNUSED,
>> {
>> struct ovsdb_row *old = txn_row->old;
>> struct ovsdb_row *new = txn_row->new;
>> + struct ovsdb_row *diff = txn_row->diff;
>>
>> ovsdb_txn_row_prefree(txn_row);
>> if (!old) {
>> @@ -184,6 +188,7 @@ ovsdb_txn_row_abort(struct ovsdb_txn *txn OVS_UNUSED,
>> }
>>
>> ovsdb_row_destroy(new);
>> + ovsdb_row_destroy(diff);
>> free(txn_row);
>>
>> return NULL;
>> @@ -250,7 +255,10 @@ find_or_make_txn_row(struct ovsdb_txn *txn, const
>> struct ovsdb_table *table,
>> if (!txn_row) {
>> const struct ovsdb_row *row = ovsdb_table_get_row(table, uuid);
>> if (row) {
>> - txn_row = ovsdb_txn_row_modify(txn, row)->txn_row;
>> + struct ovsdb_row *rw_row;
>> +
>> + ovsdb_txn_row_modify(txn, row, &rw_row, NULL);
>> + txn_row = rw_row->txn_row;
>> }
>> }
>> return txn_row;
>> @@ -433,6 +441,7 @@ delete_garbage_row(struct ovsdb_txn *txn, struct
>> ovsdb_txn_row *txn_row)
>> return NULL;
>> }
>>
>> + ovsdb_row_destroy(txn_row->diff);
>
> I think in practice 'txn_row' will always be destroyed by the time we
> exit the delete_garbage_row() function. But given that we do have some
> theoretical error cases (when delete_row_refs() returns error) in which
> we don't destroy 'row' should we set txn_row->diff to NULL here?
delete_row_refs() can't actually fail, because we just checked the
referential integrity of strong references before colliecting
garbage rows. And you may see that garbage collection failure is
wrapped in OVSDB_WRAP_BUG("can't happen", error). And it actually
can't happen, otherwise we have a serious referential integrity
bug in ovsdb-server.
But I can add txn_row->diff = NULL, just to not crash somewhere
later because of this, even if we could crash due to referential
integrity issues.
What do you think?
>
> OTOH, just below we remove 'row' from the hmap so it's not reachable
> after we exit this function. Is that an (unrelated) theoretical memory
> leak?
Yeah, the 'new' row will be leaked, but it is an unrelated issue
that can't happen. We can adjust that code separately.
>
> The rest looks good to me.
>
> Thanks,
> Dumitru
>
>> row = txn_row->new;
>> txn_row->new = NULL;
>> hmap_remove(&txn_row->table->rows, &row->hmap_node);
>> @@ -544,6 +553,7 @@ ovsdb_txn_row_commit(struct ovsdb_txn *txn OVS_UNUSED,
>> txn_row->new->n_refs = txn_row->n_refs;
>> }
>> ovsdb_row_destroy(txn_row->old);
>> + ovsdb_row_destroy(txn_row->diff);
>> free(txn_row);
>>
>> return NULL;
>> @@ -1178,6 +1188,7 @@ ovsdb_txn_destroy_cloned(struct ovsdb_txn *txn)
>> if (r->new) {
>> ovsdb_row_destroy(r->new);
>> }
>> + ovs_assert(!r->diff);
>> hmap_remove(&t->txn_rows, &r->hmap_node);
>> free(r);
>> }
>> @@ -1439,7 +1450,8 @@ ovsdb_txn_create_txn_table(struct ovsdb_txn *txn,
>> struct ovsdb_table *table)
>>
>> static struct ovsdb_txn_row *
>> ovsdb_txn_row_create(struct ovsdb_txn *txn, struct ovsdb_table *table,
>> - const struct ovsdb_row *old_, struct ovsdb_row *new)
>> + const struct ovsdb_row *old_, struct ovsdb_row *new,
>> + struct ovsdb_row *diff)
>> {
>> const struct ovsdb_row *row = old_ ? old_ : new;
>> struct ovsdb_row *old = CONST_CAST(struct ovsdb_row *, old_);
>> @@ -1453,6 +1465,7 @@ ovsdb_txn_row_create(struct ovsdb_txn *txn, struct
>> ovsdb_table *table,
>> txn_row->table = row->table;
>> txn_row->old = old;
>> txn_row->new = new;
>> + txn_row->diff = diff;
>> txn_row->n_refs = old ? old->n_refs : 0;
>> txn_row->serial = serial - 1;
>>
>> @@ -1465,6 +1478,9 @@ ovsdb_txn_row_create(struct ovsdb_txn *txn, struct
>> ovsdb_table *table,
>> if (new) {
>> new->txn_row = txn_row;
>> }
>> + if (diff) {
>> + diff->txn_row = txn_row;
>> + }
>>
>> txn_table = ovsdb_txn_create_txn_table(txn, table);
>> hmap_insert(&txn_table->txn_rows, &txn_row->hmap_node,
>> @@ -1473,24 +1489,38 @@ ovsdb_txn_row_create(struct ovsdb_txn *txn, struct
>> ovsdb_table *table,
>> return txn_row;
>> }
>>
>> -struct ovsdb_row *
>> -ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_)
>> +void
>> +ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_,
>> + struct ovsdb_row **rw_row, struct ovsdb_row **diff)
>> {
>> struct ovsdb_row *ro_row = CONST_CAST(struct ovsdb_row *, ro_row_);
>>
>> + ovs_assert(ro_row);
>> + ovs_assert(rw_row);
>> +
>> if (ro_row->txn_row) {
>> ovs_assert(ro_row == ro_row->txn_row->new);
>> - return ro_row;
>> + *rw_row = ro_row;
>> + if (diff) {
>> + *diff = ro_row->txn_row->diff;
>> + } else {
>> + /* Caller will modify the row without updating the diff.
>> + * Destroy the existing diff, if any, so it will not be
>> + * used for this row anymore. Modification will always
>> + * return NULL from this point on. */
>> + ovsdb_row_destroy(ro_row->txn_row->diff);
>> + ro_row->txn_row->diff = NULL;
>> + }
>> } else {
>> struct ovsdb_table *table = ro_row->table;
>> - struct ovsdb_row *rw_row;
>>
>> - rw_row = ovsdb_row_clone(ro_row);
>> - rw_row->n_refs = ro_row->n_refs;
>> - ovsdb_txn_row_create(txn, table, ro_row, rw_row);
>> - hmap_replace(&table->rows, &ro_row->hmap_node, &rw_row->hmap_node);
>> -
>> - return rw_row;
>> + *rw_row = ovsdb_row_clone(ro_row);
>> + (*rw_row)->n_refs = ro_row->n_refs;
>> + if (diff) {
>> + *diff = ovsdb_row_create(table);
>> + }
>> + ovsdb_txn_row_create(txn, table, ro_row, *rw_row, diff ? *diff :
>> NULL);
>> + hmap_replace(&table->rows, &ro_row->hmap_node,
>> &(*rw_row)->hmap_node);
>> }
>> }
>>
>> @@ -1502,7 +1532,7 @@ ovsdb_txn_row_insert(struct ovsdb_txn *txn, struct
>> ovsdb_row *row)
>>
>> uuid_generate(ovsdb_row_get_version_rw(row));
>>
>> - ovsdb_txn_row_create(txn, table, NULL, row);
>> + ovsdb_txn_row_create(txn, table, NULL, row, NULL);
>> hmap_insert(&table->rows, &row->hmap_node, hash);
>> }
>>
>> @@ -1518,9 +1548,11 @@ ovsdb_txn_row_delete(struct ovsdb_txn *txn, const
>> struct ovsdb_row *row_)
>> hmap_remove(&table->rows, &row->hmap_node);
>>
>> if (!txn_row) {
>> - ovsdb_txn_row_create(txn, table, row, NULL);
>> + ovsdb_txn_row_create(txn, table, row, NULL, NULL);
>> } else {
>> ovs_assert(txn_row->new == row);
>> + ovsdb_row_destroy(txn_row->diff);
>> + txn_row->diff = NULL;
>> if (txn_row->old) {
>> txn_row->new = NULL;
>> } else {
>> diff --git a/ovsdb/transaction.h b/ovsdb/transaction.h
>> index 0e054eef3..f659838dc 100644
>> --- a/ovsdb/transaction.h
>> +++ b/ovsdb/transaction.h
>> @@ -21,6 +21,7 @@
>>
>> struct json;
>> struct ovsdb;
>> +struct ovsdb_row;
>> struct ovsdb_schema;
>> struct ovsdb_table;
>> struct uuid;
>> @@ -50,8 +51,9 @@ const struct ovsdb_error *ovsdb_txn_progress_get_error(
>> const struct ovsdb_txn_progress *);
>> void ovsdb_txn_progress_destroy(struct ovsdb_txn_progress *);
>>
>> -struct ovsdb_row *ovsdb_txn_row_modify(struct ovsdb_txn *,
>> - const struct ovsdb_row *);
>> +void ovsdb_txn_row_modify(struct ovsdb_txn *, const struct ovsdb_row *,
>> + struct ovsdb_row **row_new,
>> + struct ovsdb_row **row_diff);
>> void ovsdb_txn_row_insert(struct ovsdb_txn *, struct ovsdb_row *);
>> void ovsdb_txn_row_delete(struct ovsdb_txn *, const struct ovsdb_row *);
>>
>> diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c
>> index c761822e6..0394eafd2 100644
>> --- a/tests/test-ovsdb.c
>> +++ b/tests/test-ovsdb.c
>> @@ -1798,7 +1798,7 @@ do_transact_modify(struct ovs_cmdl_context *ctx)
>> struct ovsdb_row *row_rw;
>>
>> row_ro = do_transact_find_row(ctx->argv[1]);
>> - row_rw = ovsdb_txn_row_modify(do_transact_txn, row_ro);
>> + ovsdb_txn_row_modify(do_transact_txn, row_ro, &row_rw, NULL);
>> do_transact_set_i_j(row_rw, ctx->argv[2], ctx->argv[3]);
>> }
>>
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev