LCOV - code coverage report
Current view: top level - ovsdb - transaction.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 459 487 94.3 %
Date: 2016-09-14 01:02:56 Functions: 41 41 100.0 %
Branches: 249 290 85.9 %

           Branch data     Line data    Source code
       1                 :            : /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
       2                 :            :  *
       3                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       4                 :            :  * you may not use this file except in compliance with the License.
       5                 :            :  * You may obtain a copy of the License at:
       6                 :            :  *
       7                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       8                 :            :  *
       9                 :            :  * Unless required by applicable law or agreed to in writing, software
      10                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      11                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12                 :            :  * See the License for the specific language governing permissions and
      13                 :            :  * limitations under the License.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include <config.h>
      17                 :            : 
      18                 :            : #include "transaction.h"
      19                 :            : 
      20                 :            : #include "bitmap.h"
      21                 :            : #include "openvswitch/dynamic-string.h"
      22                 :            : #include "hash.h"
      23                 :            : #include "openvswitch/hmap.h"
      24                 :            : #include "openvswitch/json.h"
      25                 :            : #include "openvswitch/list.h"
      26                 :            : #include "ovsdb-error.h"
      27                 :            : #include "ovsdb.h"
      28                 :            : #include "row.h"
      29                 :            : #include "table.h"
      30                 :            : #include "perf-counter.h"
      31                 :            : #include "uuid.h"
      32                 :            : 
      33                 :            : struct ovsdb_txn {
      34                 :            :     struct ovsdb *db;
      35                 :            :     struct ovs_list txn_tables; /* Contains "struct ovsdb_txn_table"s. */
      36                 :            :     struct ds comment;
      37                 :            : };
      38                 :            : 
      39                 :            : /* A table modified by a transaction. */
      40                 :            : struct ovsdb_txn_table {
      41                 :            :     struct ovs_list node;       /* Element in ovsdb_txn's txn_tables list. */
      42                 :            :     struct ovsdb_table *table;
      43                 :            :     struct hmap txn_rows;       /* Contains "struct ovsdb_txn_row"s. */
      44                 :            : 
      45                 :            :     /* This has the same form as the 'indexes' member of struct ovsdb_table,
      46                 :            :      * but it is only used or updated at transaction commit time, from
      47                 :            :      * check_index_uniqueness(). */
      48                 :            :     struct hmap *txn_indexes;
      49                 :            : 
      50                 :            :     /* Used by for_each_txn_row(). */
      51                 :            :     unsigned int serial;        /* Serial number of in-progress iteration. */
      52                 :            :     unsigned int n_processed;   /* Number of rows processed. */
      53                 :            : };
      54                 :            : 
      55                 :            : /* A row modified by the transaction:
      56                 :            :  *
      57                 :            :  *      - A row added by a transaction will have null 'old' and non-null 'new'.
      58                 :            :  *
      59                 :            :  *      - A row deleted by a transaction will have non-null 'old' and null
      60                 :            :  *        'new'.
      61                 :            :  *
      62                 :            :  *      - A row modified by a transaction will have non-null 'old' and 'new'.
      63                 :            :  *
      64                 :            :  *      - 'old' and 'new' both null indicates that a row was added then deleted
      65                 :            :  *        within a single transaction.  Most of the time we instead delete the
      66                 :            :  *        ovsdb_txn_row entirely, but inside a for_each_txn_row() callback
      67                 :            :  *        there are restrictions that sometimes mean we have to leave the
      68                 :            :  *        ovsdb_txn_row in place.
      69                 :            :  */
      70                 :            : struct ovsdb_txn_row {
      71                 :            :     struct hmap_node hmap_node; /* In ovsdb_txn_table's txn_rows hmap. */
      72                 :            :     struct ovsdb_row *old;      /* The old row. */
      73                 :            :     struct ovsdb_row *new;      /* The new row. */
      74                 :            :     size_t n_refs;              /* Number of remaining references. */
      75                 :            : 
      76                 :            :     /* These members are the same as the corresponding members of 'old' or
      77                 :            :      * 'new'.  They are present here for convenience and because occasionally
      78                 :            :      * there can be an ovsdb_txn_row where both 'old' and 'new' are NULL. */
      79                 :            :     struct uuid uuid;
      80                 :            :     struct ovsdb_table *table;
      81                 :            : 
      82                 :            :     /* Used by for_each_txn_row(). */
      83                 :            :     unsigned int serial;        /* Serial number of in-progress commit. */
      84                 :            : 
      85                 :            :     unsigned long changed[];    /* Bits set to 1 for columns that changed. */
      86                 :            : };
      87                 :            : 
      88                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
      89                 :            : delete_garbage_row(struct ovsdb_txn *txn, struct ovsdb_txn_row *r);
      90                 :            : static void ovsdb_txn_row_prefree(struct ovsdb_txn_row *);
      91                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
      92                 :            : for_each_txn_row(struct ovsdb_txn *txn,
      93                 :            :                       struct ovsdb_error *(*)(struct ovsdb_txn *,
      94                 :            :                                               struct ovsdb_txn_row *));
      95                 :            : 
      96                 :            : /* Used by for_each_txn_row() to track tables and rows that have been
      97                 :            :  * processed.  */
      98                 :            : static unsigned int serial;
      99                 :            : 
     100                 :            : struct ovsdb_txn *
     101                 :      16090 : ovsdb_txn_create(struct ovsdb *db)
     102                 :            : {
     103                 :      16090 :     struct ovsdb_txn *txn = xmalloc(sizeof *txn);
     104                 :      16090 :     txn->db = db;
     105                 :      16090 :     ovs_list_init(&txn->txn_tables);
     106                 :      16090 :     ds_init(&txn->comment);
     107                 :      16090 :     return txn;
     108                 :            : }
     109                 :            : 
     110                 :            : static void
     111                 :      16090 : ovsdb_txn_free(struct ovsdb_txn *txn)
     112                 :            : {
     113         [ -  + ]:      16090 :     ovs_assert(ovs_list_is_empty(&txn->txn_tables));
     114                 :      16090 :     ds_destroy(&txn->comment);
     115                 :      16090 :     free(txn);
     116                 :      16090 : }
     117                 :            : 
     118                 :            : static struct ovsdb_error *
     119                 :        319 : ovsdb_txn_row_abort(struct ovsdb_txn *txn OVS_UNUSED,
     120                 :            :                     struct ovsdb_txn_row *txn_row)
     121                 :            : {
     122                 :        319 :     struct ovsdb_row *old = txn_row->old;
     123                 :        319 :     struct ovsdb_row *new = txn_row->new;
     124                 :            : 
     125                 :        319 :     ovsdb_txn_row_prefree(txn_row);
     126         [ +  + ]:        319 :     if (!old) {
     127         [ +  - ]:        101 :         if (new) {
     128                 :        101 :             hmap_remove(&new->table->rows, &new->hmap_node);
     129                 :            :         }
     130         [ +  + ]:        218 :     } else if (!new) {
     131                 :         85 :         hmap_insert(&old->table->rows, &old->hmap_node, ovsdb_row_hash(old));
     132                 :            :     } else {
     133                 :        133 :         hmap_replace(&new->table->rows, &new->hmap_node, &old->hmap_node);
     134                 :            :     }
     135                 :        319 :     ovsdb_row_destroy(new);
     136                 :        319 :     free(txn_row);
     137                 :            : 
     138                 :        319 :     return NULL;
     139                 :            : }
     140                 :            : 
     141                 :            : /* Returns the offset in bytes from the start of an ovsdb_row for 'table' to
     142                 :            :  * the hmap_node for the index numbered 'i'. */
     143                 :            : static size_t
     144                 :     174012 : ovsdb_row_index_offset__(const struct ovsdb_table *table, size_t i)
     145                 :            : {
     146                 :     174012 :     size_t n_fields = shash_count(&table->schema->columns);
     147                 :     174012 :     return (offsetof(struct ovsdb_row, fields)
     148                 :     174012 :             + n_fields * sizeof(struct ovsdb_datum)
     149                 :     174012 :             + i * sizeof(struct hmap_node));
     150                 :            : }
     151                 :            : 
     152                 :            : /* Returns the hmap_node in 'row' for the index numbered 'i'. */
     153                 :            : static struct hmap_node *
     154                 :     136664 : ovsdb_row_get_index_node(struct ovsdb_row *row, size_t i)
     155                 :            : {
     156                 :     136664 :     return (void *) ((char *) row + ovsdb_row_index_offset__(row->table, i));
     157                 :            : }
     158                 :            : 
     159                 :            : /* Returns the ovsdb_row given 'index_node', which is a pointer to that row's
     160                 :            :  * hmap_node for the index numbered 'i' within 'table'. */
     161                 :            : static struct ovsdb_row *
     162                 :      37348 : ovsdb_row_from_index_node(struct hmap_node *index_node,
     163                 :            :                           const struct ovsdb_table *table, size_t i)
     164                 :            : {
     165                 :      37348 :     return (void *) ((char *) index_node - ovsdb_row_index_offset__(table, i));
     166                 :            : }
     167                 :            : 
     168                 :            : void
     169                 :       4371 : ovsdb_txn_abort(struct ovsdb_txn *txn)
     170                 :            : {
     171                 :       4371 :     ovsdb_error_assert(for_each_txn_row(txn, ovsdb_txn_row_abort));
     172                 :       4371 :     ovsdb_txn_free(txn);
     173                 :       4371 : }
     174                 :            : 
     175                 :            : static struct ovsdb_txn_row *
     176                 :      66438 : find_txn_row(const struct ovsdb_table *table, const struct uuid *uuid)
     177                 :            : {
     178                 :            :     struct ovsdb_txn_row *txn_row;
     179                 :            : 
     180         [ +  + ]:      66438 :     if (!table->txn_table) {
     181                 :       1392 :         return NULL;
     182                 :            :     }
     183                 :            : 
     184 [ +  + ][ -  + ]:      65046 :     HMAP_FOR_EACH_WITH_HASH (txn_row, hmap_node,
     185                 :            :                              uuid_hash(uuid), &table->txn_table->txn_rows) {
     186         [ +  - ]:      38262 :         if (uuid_equals(uuid, &txn_row->uuid)) {
     187                 :      38262 :             return txn_row;
     188                 :            :         }
     189                 :            :     }
     190                 :            : 
     191                 :      26784 :     return NULL;
     192                 :            : }
     193                 :            : 
     194                 :            : static struct ovsdb_txn_row *
     195                 :      66438 : find_or_make_txn_row(struct ovsdb_txn *txn, const struct ovsdb_table *table,
     196                 :            :                      const struct uuid *uuid)
     197                 :            : {
     198                 :      66438 :     struct ovsdb_txn_row *txn_row = find_txn_row(table, uuid);
     199         [ +  + ]:      66438 :     if (!txn_row) {
     200                 :      28176 :         const struct ovsdb_row *row = ovsdb_table_get_row(table, uuid);
     201         [ +  + ]:      28176 :         if (row) {
     202                 :      28142 :             txn_row = ovsdb_txn_row_modify(txn, row)->txn_row;
     203                 :            :         }
     204                 :            :     }
     205                 :      66438 :     return txn_row;
     206                 :            : }
     207                 :            : 
     208                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     209                 :     807170 : ovsdb_txn_adjust_atom_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r,
     210                 :            :                            const struct ovsdb_column *c,
     211                 :            :                            const struct ovsdb_base_type *base,
     212                 :            :                            const union ovsdb_atom *atoms, unsigned int n,
     213                 :            :                            int delta)
     214                 :            : {
     215                 :            :     const struct ovsdb_table *table;
     216                 :            :     unsigned int i;
     217                 :            : 
     218         [ +  + ]:     807170 :     if (!ovsdb_base_type_is_strong_ref(base)) {
     219                 :     777564 :         return NULL;
     220                 :            :     }
     221                 :            : 
     222                 :      29606 :     table = base->u.uuid.refTable;
     223         [ +  + ]:      93430 :     for (i = 0; i < n; i++) {
     224                 :      63858 :         const struct uuid *uuid = &atoms[i].uuid;
     225                 :            :         struct ovsdb_txn_row *txn_row;
     226                 :            : 
     227         [ +  + ]:      63858 :         if (uuid_equals(uuid, ovsdb_row_get_uuid(r))) {
     228                 :            :             /* Self-references don't count. */
     229                 :        355 :             continue;
     230                 :            :         }
     231                 :            : 
     232                 :      63503 :         txn_row = find_or_make_txn_row(txn, table, uuid);
     233         [ +  + ]:      63503 :         if (!txn_row) {
     234                 :         34 :             return ovsdb_error("referential integrity violation",
     235                 :            :                                "Table %s column %s row "UUID_FMT" "
     236                 :            :                                "references nonexistent row "UUID_FMT" in "
     237                 :            :                                "table %s.",
     238                 :         34 :                                r->table->schema->name, c->name,
     239                 :         34 :                                UUID_ARGS(ovsdb_row_get_uuid(r)),
     240                 :        136 :                                UUID_ARGS(uuid), table->schema->name);
     241                 :            :         }
     242                 :      63469 :         txn_row->n_refs += delta;
     243                 :            :     }
     244                 :            : 
     245                 :      29572 :     return NULL;
     246                 :            : }
     247                 :            : 
     248                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     249                 :     403602 : ovsdb_txn_adjust_row_refs(struct ovsdb_txn *txn, const struct ovsdb_row *r,
     250                 :            :                           const struct ovsdb_column *column, int delta)
     251                 :            : {
     252                 :     403602 :     const struct ovsdb_datum *field = &r->fields[column->index];
     253                 :            :     struct ovsdb_error *error;
     254                 :            : 
     255                 :     403602 :     error = ovsdb_txn_adjust_atom_refs(txn, r, column, &column->type.key,
     256                 :     403602 :                                        field->keys, field->n, delta);
     257         [ +  + ]:     403602 :     if (!error) {
     258                 :     403568 :         error = ovsdb_txn_adjust_atom_refs(txn, r, column, &column->type.value,
     259                 :     403568 :                                            field->values, field->n, delta);
     260                 :            :     }
     261                 :     403602 :     return error;
     262                 :            : }
     263                 :            : 
     264                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     265                 :      61972 : update_row_ref_count(struct ovsdb_txn *txn, struct ovsdb_txn_row *r)
     266                 :            : {
     267                 :      61972 :     struct ovsdb_table *table = r->table;
     268                 :            :     struct shash_node *node;
     269                 :            : 
     270 [ +  + ][ -  + ]:    1362527 :     SHASH_FOR_EACH (node, &table->schema->columns) {
     271                 :    1300589 :         const struct ovsdb_column *column = node->data;
     272                 :            :         struct ovsdb_error *error;
     273                 :            : 
     274         [ +  + ]:    1300589 :         if (bitmap_is_set(r->changed, column->index)) {
     275         [ +  + ]:     365983 :             if (r->old) {
     276                 :      45284 :                 error = ovsdb_txn_adjust_row_refs(txn, r->old, column, -1);
     277         [ -  + ]:      45284 :                 if (error) {
     278                 :          0 :                     return OVSDB_WRAP_BUG("error decreasing refcount", error);
     279                 :            :                 }
     280                 :            :             }
     281         [ +  + ]:     365983 :             if (r->new) {
     282                 :     358318 :                 error = ovsdb_txn_adjust_row_refs(txn, r->new, column, 1);
     283         [ +  + ]:     358318 :                 if (error) {
     284                 :         34 :                     return error;
     285                 :            :                 }
     286                 :            :             }
     287                 :            :         }
     288                 :            :     }
     289                 :            : 
     290                 :      61938 :     return NULL;
     291                 :            : }
     292                 :            : 
     293                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     294                 :      61910 : check_ref_count(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *r)
     295                 :            : {
     296 [ +  + ][ +  + ]:      61910 :     if (r->new || !r->n_refs) {
     297                 :      61852 :         return NULL;
     298                 :            :     } else {
     299                 :         58 :         return ovsdb_error("referential integrity violation",
     300                 :            :                            "cannot delete %s row "UUID_FMT" because "
     301                 :            :                            "of %"PRIuSIZE" remaining reference(s)",
     302                 :        174 :                            r->table->schema->name, UUID_ARGS(&r->uuid),
     303                 :            :                            r->n_refs);
     304                 :            :     }
     305                 :            : }
     306                 :            : 
     307                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     308                 :     174790 : delete_row_refs(struct ovsdb_txn *txn, const struct ovsdb_row *row,
     309                 :            :                 const struct ovsdb_base_type *base,
     310                 :            :                 const union ovsdb_atom *atoms, unsigned int n)
     311                 :            : {
     312                 :            :     const struct ovsdb_table *table;
     313                 :            :     unsigned int i;
     314                 :            : 
     315         [ +  + ]:     174790 :     if (!ovsdb_base_type_is_strong_ref(base)) {
     316                 :     171304 :         return NULL;
     317                 :            :     }
     318                 :            : 
     319                 :       3486 :     table = base->u.uuid.refTable;
     320         [ +  + ]:       6428 :     for (i = 0; i < n; i++) {
     321                 :       2942 :         const struct uuid *uuid = &atoms[i].uuid;
     322                 :            :         struct ovsdb_txn_row *txn_row;
     323                 :            : 
     324         [ +  + ]:       2942 :         if (uuid_equals(uuid, ovsdb_row_get_uuid(row))) {
     325                 :            :             /* Self-references don't count. */
     326                 :          7 :             continue;
     327                 :            :         }
     328                 :            : 
     329                 :       2935 :         txn_row = find_or_make_txn_row(txn, table, uuid);
     330         [ -  + ]:       2935 :         if (!txn_row) {
     331                 :          0 :             return OVSDB_BUG("strong ref target missing");
     332         [ -  + ]:       2935 :         } else if (!txn_row->n_refs) {
     333                 :          0 :             return OVSDB_BUG("strong ref target has zero n_refs");
     334         [ -  + ]:       2935 :         } else if (!txn_row->new) {
     335                 :          0 :             return OVSDB_BUG("deleted strong ref target");
     336                 :            :         }
     337                 :            : 
     338         [ +  + ]:       2935 :         if (--txn_row->n_refs == 0) {
     339                 :       2895 :             struct ovsdb_error *error = delete_garbage_row(txn, txn_row);
     340         [ -  + ]:       2895 :             if (error) {
     341                 :          0 :                 return error;
     342                 :            :             }
     343                 :            :         }
     344                 :            :     }
     345                 :            : 
     346                 :       3486 :     return NULL;
     347                 :            : }
     348                 :            : 
     349                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     350                 :      17747 : delete_garbage_row(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
     351                 :            : {
     352                 :            :     struct shash_node *node;
     353                 :            :     struct ovsdb_row *row;
     354                 :            : 
     355         [ +  + ]:      17747 :     if (txn_row->table->schema->is_root) {
     356                 :      14473 :         return NULL;
     357                 :            :     }
     358                 :            : 
     359                 :       3274 :     row = txn_row->new;
     360                 :       3274 :     txn_row->new = NULL;
     361                 :       3274 :     hmap_remove(&txn_row->table->rows, &row->hmap_node);
     362 [ +  + ][ -  + ]:      90669 :     SHASH_FOR_EACH (node, &txn_row->table->schema->columns) {
     363                 :      87395 :         const struct ovsdb_column *column = node->data;
     364                 :      87395 :         const struct ovsdb_datum *field = &row->fields[column->index];
     365                 :            :         struct ovsdb_error *error;
     366                 :            : 
     367                 :      87395 :         error = delete_row_refs(txn, row,
     368                 :      87395 :                                 &column->type.key, field->keys, field->n);
     369         [ -  + ]:      87395 :         if (error) {
     370                 :          0 :             return error;
     371                 :            :         }
     372                 :            : 
     373                 :      87395 :         error = delete_row_refs(txn, row,
     374                 :      87395 :                                 &column->type.value, field->values, field->n);
     375         [ -  + ]:      87395 :         if (error) {
     376                 :          0 :             return error;
     377                 :            :         }
     378                 :            :     }
     379                 :       3274 :     ovsdb_row_destroy(row);
     380                 :            : 
     381                 :       3274 :     return NULL;
     382                 :            : }
     383                 :            : 
     384                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     385                 :      64727 : collect_garbage(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
     386                 :            : {
     387 [ +  + ][ +  + ]:      64727 :     if (txn_row->new && !txn_row->n_refs) {
     388                 :      14852 :         return delete_garbage_row(txn, txn_row);
     389                 :            :     }
     390                 :      49875 :     return NULL;
     391                 :            : }
     392                 :            : 
     393                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     394                 :      11860 : update_ref_counts(struct ovsdb_txn *txn)
     395                 :            : {
     396                 :            :     struct ovsdb_error *error;
     397                 :            : 
     398                 :      11860 :     error = for_each_txn_row(txn, update_row_ref_count);
     399         [ +  + ]:      11860 :     if (error) {
     400                 :         34 :         return error;
     401                 :            :     }
     402                 :            : 
     403                 :      11826 :     return for_each_txn_row(txn, check_ref_count);
     404                 :            : }
     405                 :            : 
     406                 :            : static struct ovsdb_error *
     407                 :      64679 : ovsdb_txn_row_commit(struct ovsdb_txn *txn OVS_UNUSED,
     408                 :            :                      struct ovsdb_txn_row *txn_row)
     409                 :            : {
     410                 :      64679 :     size_t n_indexes = txn_row->table->schema->n_indexes;
     411                 :            : 
     412         [ +  + ]:      64679 :     if (txn_row->old) {
     413                 :            :         size_t i;
     414                 :            : 
     415         [ +  + ]:      87330 :         for (i = 0; i < n_indexes; i++) {
     416                 :      40556 :             struct hmap_node *node = ovsdb_row_get_index_node(txn_row->old, i);
     417                 :      40556 :             hmap_remove(&txn_row->table->indexes[i], node);
     418                 :            :         }
     419                 :            :     }
     420         [ +  + ]:      64679 :     if (txn_row->new) {
     421                 :            :         size_t i;
     422                 :            : 
     423         [ +  + ]:     108486 :         for (i = 0; i < n_indexes; i++) {
     424                 :      48047 :             struct hmap_node *node = ovsdb_row_get_index_node(txn_row->new, i);
     425                 :      48047 :             hmap_insert(&txn_row->table->indexes[i], node, node->hash);
     426                 :            :         }
     427                 :            :     }
     428                 :            : 
     429                 :      64679 :     ovsdb_txn_row_prefree(txn_row);
     430         [ +  + ]:      64679 :     if (txn_row->new) {
     431                 :      60439 :         txn_row->new->n_refs = txn_row->n_refs;
     432                 :            :     }
     433                 :      64679 :     ovsdb_row_destroy(txn_row->old);
     434                 :      64679 :     free(txn_row);
     435                 :            : 
     436                 :      64679 :     return NULL;
     437                 :            : }
     438                 :            : 
     439                 :            : static struct ovsdb_error *
     440                 :      64679 : ovsdb_txn_update_weak_refs(struct ovsdb_txn *txn OVS_UNUSED,
     441                 :            :                            struct ovsdb_txn_row *txn_row)
     442                 :            : {
     443                 :            :     struct ovsdb_weak_ref *weak, *next;
     444                 :            : 
     445                 :            :     /* Remove the weak references originating in the old version of the row. */
     446         [ +  + ]:      64679 :     if (txn_row->old) {
     447 [ +  + ][ +  + ]:      47745 :         LIST_FOR_EACH_SAFE (weak, next, src_node, &txn_row->old->src_refs) {
     448                 :        971 :             ovs_list_remove(&weak->src_node);
     449                 :        971 :             ovs_list_remove(&weak->dst_node);
     450                 :        971 :             free(weak);
     451                 :            :         }
     452                 :            :     }
     453                 :            : 
     454                 :            :     /* Although the originating rows have the responsibility of updating the
     455                 :            :      * weak references in the dst, it is possible that some source rows aren't
     456                 :            :      * part of the transaction.  In that situation this row needs to move the
     457                 :            :      * list of incoming weak references from the old row into the new one.
     458                 :            :      */
     459 [ +  + ][ +  + ]:      64679 :     if (txn_row->old && txn_row->new) {
     460                 :            :         /* Move the incoming weak references from old to new. */
     461                 :      42594 :         ovs_list_push_back_all(&txn_row->new->dst_refs,
     462                 :      42594 :                                &txn_row->old->dst_refs);
     463                 :            :     }
     464                 :            : 
     465                 :            :     /* Insert the weak references originating in the new version of the row. */
     466                 :            :     struct ovsdb_row *dst_row;
     467         [ +  + ]:      64679 :     if (txn_row->new) {
     468         [ +  + ]:      61830 :         LIST_FOR_EACH (weak, src_node, &txn_row->new->src_refs) {
     469                 :            :             /* dst_row MUST exist. */
     470                 :       1391 :             dst_row = CONST_CAST(struct ovsdb_row *,
     471                 :            :                     ovsdb_table_get_row(weak->dst_table, &weak->dst));
     472                 :       1391 :             ovs_list_insert(&dst_row->dst_refs, &weak->dst_node);
     473                 :            :         }
     474                 :            :     }
     475                 :            : 
     476                 :      64679 :     return NULL;
     477                 :            : }
     478                 :            : 
     479                 :            : static void
     480                 :       1607 : add_weak_ref(const struct ovsdb_row *src_, const struct ovsdb_row *dst_)
     481                 :            : {
     482                 :       1607 :     struct ovsdb_row *src = CONST_CAST(struct ovsdb_row *, src_);
     483                 :       1607 :     struct ovsdb_row *dst = CONST_CAST(struct ovsdb_row *, dst_);
     484                 :            :     struct ovsdb_weak_ref *weak;
     485                 :            : 
     486         [ +  + ]:       1607 :     if (src == dst) {
     487                 :        202 :         return;
     488                 :            :     }
     489                 :            : 
     490         [ +  + ]:       1405 :     if (!ovs_list_is_empty(&dst->dst_refs)) {
     491                 :            :         /* Omit duplicates. */
     492                 :        725 :         weak = CONTAINER_OF(ovs_list_back(&dst->dst_refs),
     493                 :            :                             struct ovsdb_weak_ref, dst_node);
     494         [ -  + ]:        725 :         if (weak->src == src) {
     495                 :          0 :             return;
     496                 :            :         }
     497                 :            :     }
     498                 :            : 
     499                 :       1405 :     weak = xmalloc(sizeof *weak);
     500                 :       1405 :     weak->src = src;
     501                 :       1405 :     weak->dst_table = dst->table;
     502                 :       1405 :     weak->dst = *ovsdb_row_get_uuid(dst);
     503                 :            :     /* The dst_refs list is updated at commit time. */
     504                 :       1405 :     ovs_list_init(&weak->dst_node);
     505                 :       1405 :     ovs_list_push_back(&src->src_refs, &weak->src_node);
     506                 :            : }
     507                 :            : 
     508                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     509                 :      64761 : assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
     510                 :            : {
     511                 :            :     struct ovsdb_table *table;
     512                 :            :     struct shash_node *node;
     513                 :            : 
     514 [ +  + ][ +  + ]:      64761 :     if (txn_row->old && !txn_row->new) {
     515                 :            :         /* Mark rows that have weak references to 'txn_row' as modified, so
     516                 :            :          * that their weak references will get reassessed. */
     517                 :            :         struct ovsdb_weak_ref *weak, *next;
     518                 :            : 
     519 [ +  + ][ +  + ]:       4420 :         LIST_FOR_EACH_SAFE (weak, next, dst_node, &txn_row->old->dst_refs) {
     520         [ +  + ]:        219 :             if (!weak->src->txn_row) {
     521                 :         50 :                 ovsdb_txn_row_modify(txn, weak->src);
     522                 :            :             }
     523                 :            :         }
     524                 :            :     }
     525                 :            : 
     526         [ +  + ]:      64761 :     if (!txn_row->new) {
     527                 :            :         /* We don't have to do anything about references that originate at
     528                 :            :          * 'txn_row', because ovsdb_row_destroy() will remove those weak
     529                 :            :          * references. */
     530                 :       4261 :         return NULL;
     531                 :            :     }
     532                 :            : 
     533                 :      60500 :     table = txn_row->table;
     534 [ +  + ][ -  + ]:    1347894 :     SHASH_FOR_EACH (node, &table->schema->columns) {
     535                 :    1287415 :         const struct ovsdb_column *column = node->data;
     536                 :    1287415 :         struct ovsdb_datum *datum = &txn_row->new->fields[column->index];
     537                 :            :         unsigned int orig_n, i;
     538                 :    1287415 :         bool zero = false;
     539                 :            : 
     540                 :    1287415 :         orig_n = datum->n;
     541                 :            : 
     542         [ +  + ]:    1287415 :         if (ovsdb_base_type_is_weak_ref(&column->type.key)) {
     543         [ +  + ]:       6122 :             for (i = 0; i < datum->n; ) {
     544                 :            :                 const struct ovsdb_row *row;
     545                 :            : 
     546                 :       1663 :                 row = ovsdb_table_get_row(column->type.key.u.uuid.refTable,
     547                 :       1663 :                                           &datum->keys[i].uuid);
     548         [ +  + ]:       1663 :                 if (row) {
     549                 :       1607 :                     add_weak_ref(txn_row->new, row);
     550                 :       1607 :                     i++;
     551                 :            :                 } else {
     552         [ +  + ]:         56 :                     if (uuid_is_zero(&datum->keys[i].uuid)) {
     553                 :          7 :                         zero = true;
     554                 :            :                     }
     555                 :         56 :                     ovsdb_datum_remove_unsafe(datum, i, &column->type);
     556                 :            :                 }
     557                 :            :             }
     558                 :            :         }
     559                 :            : 
     560         [ -  + ]:    1287415 :         if (ovsdb_base_type_is_weak_ref(&column->type.value)) {
     561         [ #  # ]:          0 :             for (i = 0; i < datum->n; ) {
     562                 :            :                 const struct ovsdb_row *row;
     563                 :            : 
     564                 :          0 :                 row = ovsdb_table_get_row(column->type.value.u.uuid.refTable,
     565                 :          0 :                                           &datum->values[i].uuid);
     566         [ #  # ]:          0 :                 if (row) {
     567                 :          0 :                     add_weak_ref(txn_row->new, row);
     568                 :          0 :                     i++;
     569                 :            :                 } else {
     570         [ #  # ]:          0 :                     if (uuid_is_zero(&datum->values[i].uuid)) {
     571                 :          0 :                         zero = true;
     572                 :            :                     }
     573                 :          0 :                     ovsdb_datum_remove_unsafe(datum, i, &column->type);
     574                 :            :                 }
     575                 :            :             }
     576                 :            :         }
     577                 :            : 
     578         [ +  + ]:    1287415 :         if (datum->n != orig_n) {
     579                 :         56 :             bitmap_set1(txn_row->changed, column->index);
     580                 :         56 :             ovsdb_datum_sort_assert(datum, column->type.key.type);
     581         [ +  + ]:         56 :             if (datum->n < column->type.n_min) {
     582                 :         21 :                 const struct uuid *row_uuid = ovsdb_row_get_uuid(txn_row->new);
     583 [ +  + ][ +  - ]:         21 :                 if (zero && !txn_row->old) {
     584                 :          7 :                     return ovsdb_error(
     585                 :            :                         "constraint violation",
     586                 :            :                         "Weak reference column \"%s\" in \"%s\" row "UUID_FMT
     587                 :            :                         " (inserted within this transaction) contained "
     588                 :            :                         "all-zeros UUID (probably as the default value for "
     589                 :            :                         "this column) but deleting this value caused a "
     590                 :            :                         "constraint volation because this column is not "
     591                 :            :                         "allowed to be empty.", column->name,
     592                 :         21 :                         table->schema->name, UUID_ARGS(row_uuid));
     593                 :            :                 } else {
     594         [ +  - ]:         14 :                     return ovsdb_error(
     595                 :            :                         "constraint violation",
     596                 :            :                         "Deletion of %u weak reference(s) to deleted (or "
     597                 :            :                         "never-existing) rows from column \"%s\" in \"%s\" "
     598                 :            :                         "row "UUID_FMT" %scaused this column to become empty, "
     599                 :            :                         "but constraints on this column disallow an "
     600                 :            :                         "empty column.",
     601                 :         28 :                         orig_n - datum->n, column->name, table->schema->name,
     602                 :         42 :                         UUID_ARGS(row_uuid),
     603                 :         14 :                         (txn_row->old
     604                 :            :                          ? ""
     605                 :            :                          : "(inserted within this transaction) "));
     606                 :            :                 }
     607                 :            :             }
     608                 :            :         }
     609                 :            :     }
     610                 :            : 
     611                 :      60479 :     return NULL;
     612                 :            : }
     613                 :            : 
     614                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     615                 :      36741 : determine_changes(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
     616                 :            : {
     617                 :      36741 :     struct ovsdb_table *table = txn_row->table;
     618                 :            : 
     619 [ +  + ][ +  + ]:      54481 :     if (txn_row->old && txn_row->new) {
     620                 :            :         struct shash_node *node;
     621                 :      17740 :         bool changed = false;
     622                 :            : 
     623 [ +  + ][ -  + ]:     463099 :         SHASH_FOR_EACH (node, &table->schema->columns) {
     624                 :     445359 :             const struct ovsdb_column *column = node->data;
     625                 :     445359 :             const struct ovsdb_type *type = &column->type;
     626                 :     445359 :             unsigned int idx = column->index;
     627                 :            : 
     628         [ +  + ]:     445359 :             if (!ovsdb_datum_equals(&txn_row->old->fields[idx],
     629                 :     445359 :                                     &txn_row->new->fields[idx],
     630                 :            :                                     type)) {
     631                 :      37628 :                 bitmap_set1(txn_row->changed, idx);
     632                 :      37628 :                 changed = true;
     633                 :            :             }
     634                 :            :         }
     635                 :            : 
     636         [ -  + ]:      17740 :         if (!changed) {
     637                 :            :             /* Nothing actually changed in this row, so drop it. */
     638                 :          0 :             ovsdb_txn_row_abort(txn, txn_row);
     639                 :            :         }
     640                 :            :     } else {
     641                 :      19001 :         bitmap_set_multiple(txn_row->changed, 0,
     642                 :      19001 :                             shash_count(&table->schema->columns), 1);
     643                 :            :     }
     644                 :            : 
     645                 :      36741 :     return NULL;
     646                 :            : }
     647                 :            : 
     648                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     649                 :      11768 : check_max_rows(struct ovsdb_txn *txn)
     650                 :            : {
     651                 :            :     struct ovsdb_txn_table *t;
     652                 :            : 
     653         [ +  + ]:      35305 :     LIST_FOR_EACH (t, node, &txn->txn_tables) {
     654                 :      23544 :         size_t n_rows = hmap_count(&t->table->rows);
     655                 :      23544 :         unsigned int max_rows = t->table->schema->max_rows;
     656                 :            : 
     657         [ +  + ]:      23544 :         if (n_rows > max_rows) {
     658                 :          7 :             return ovsdb_error("constraint violation",
     659                 :            :                                "transaction causes \"%s\" table to contain "
     660                 :            :                                "%"PRIuSIZE" rows, greater than the schema-defined "
     661                 :            :                                "limit of %u row(s)",
     662                 :          7 :                                t->table->schema->name, n_rows, max_rows);
     663                 :            :         }
     664                 :            :     }
     665                 :            : 
     666                 :      11761 :     return NULL;
     667                 :            : }
     668                 :            : 
     669                 :            : static struct ovsdb_row *
     670                 :      96150 : ovsdb_index_search(struct hmap *index, struct ovsdb_row *row, size_t i,
     671                 :            :                    uint32_t hash)
     672                 :            : {
     673                 :      96150 :     const struct ovsdb_table *table = row->table;
     674                 :      96150 :     const struct ovsdb_column_set *columns = &table->schema->indexes[i];
     675                 :            :     struct hmap_node *node;
     676                 :            : 
     677         [ +  + ]:      96150 :     for (node = hmap_first_with_hash(index, hash); node;
     678                 :          0 :          node = hmap_next_with_hash(node)) {
     679                 :      37348 :         struct ovsdb_row *irow = ovsdb_row_from_index_node(node, table, i);
     680         [ +  - ]:      37348 :         if (ovsdb_row_equal_columns(row, irow, columns)) {
     681                 :      37348 :             return irow;
     682                 :            :         }
     683                 :            :     }
     684                 :            : 
     685                 :      58802 :     return NULL;
     686                 :            : }
     687                 :            : 
     688                 :            : static void
     689                 :         42 : duplicate_index_row__(const struct ovsdb_column_set *index,
     690                 :            :                       const struct ovsdb_row *row,
     691                 :            :                       const char *title,
     692                 :            :                       struct ds *out)
     693                 :            : {
     694                 :         42 :     size_t n_columns = shash_count(&row->table->schema->columns);
     695                 :            : 
     696                 :         42 :     ds_put_format(out, "%s row, with UUID "UUID_FMT", ",
     697                 :         42 :                   title, UUID_ARGS(ovsdb_row_get_uuid(row)));
     698         [ +  + ]:         42 :     if (!row->txn_row
     699         [ -  + ]:         35 :         || bitmap_scan(row->txn_row->changed, 1, 0, n_columns) == n_columns) {
     700                 :          7 :         ds_put_cstr(out, "existed in the database before this "
     701                 :            :                     "transaction and was not modified by the transaction.");
     702         [ +  + ]:         35 :     } else if (!row->txn_row->old) {
     703                 :         21 :         ds_put_cstr(out, "was inserted by this transaction.");
     704         [ -  + ]:         14 :     } else if (ovsdb_row_equal_columns(row->txn_row->old,
     705                 :         14 :                                        row->txn_row->new, index)) {
     706                 :          0 :         ds_put_cstr(out, "existed in the database before this "
     707                 :            :                     "transaction, which modified some of the row's columns "
     708                 :            :                     "but not any columns in this index.");
     709                 :            :     } else {
     710                 :         14 :         ds_put_cstr(out, "had the following index values before the "
     711                 :            :                     "transaction: ");
     712                 :         14 :         ovsdb_row_columns_to_string(row->txn_row->old, index, out);
     713                 :         14 :         ds_put_char(out, '.');
     714                 :            :     }
     715                 :         42 : }
     716                 :            : 
     717                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     718                 :         21 : duplicate_index_row(const struct ovsdb_column_set *index,
     719                 :            :                     const struct ovsdb_row *a,
     720                 :            :                     const struct ovsdb_row *b)
     721                 :            : {
     722                 :            :     struct ovsdb_column_set all_columns;
     723                 :            :     struct ovsdb_error *error;
     724                 :            :     char *index_s;
     725                 :            :     struct ds s;
     726                 :            : 
     727                 :            :     /* Put 'a' and 'b' in a predictable order to make error messages
     728                 :            :      * reproducible for testing. */
     729                 :         21 :     ovsdb_column_set_init(&all_columns);
     730                 :         21 :     ovsdb_column_set_add_all(&all_columns, a->table);
     731         [ +  + ]:         21 :     if (ovsdb_row_compare_columns_3way(a, b, &all_columns) < 0) {
     732                 :          5 :         const struct ovsdb_row *tmp = a;
     733                 :          5 :         a = b;
     734                 :          5 :         b = tmp;
     735                 :            :     }
     736                 :         21 :     ovsdb_column_set_destroy(&all_columns);
     737                 :            : 
     738                 :         21 :     index_s = ovsdb_column_set_to_string(index);
     739                 :            : 
     740                 :         21 :     ds_init(&s);
     741                 :         21 :     ds_put_format(&s, "Transaction causes multiple rows in \"%s\" table to "
     742                 :         21 :                   "have identical values (", a->table->schema->name);
     743                 :         21 :     ovsdb_row_columns_to_string(a, index, &s);
     744                 :         21 :     ds_put_format(&s, ") for index on %s.  ", index_s);
     745                 :         21 :     duplicate_index_row__(index, a, "First", &s);
     746                 :         21 :     ds_put_cstr(&s, "  ");
     747                 :         21 :     duplicate_index_row__(index, b, "Second", &s);
     748                 :            : 
     749                 :         21 :     free(index_s);
     750                 :            : 
     751                 :         21 :     error = ovsdb_error("constraint violation", "%s", ds_cstr(&s));
     752                 :         21 :     ds_destroy(&s);
     753                 :         21 :     return error;
     754                 :            : }
     755                 :            : 
     756                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     757                 :      64718 : check_index_uniqueness(struct ovsdb_txn *txn OVS_UNUSED,
     758                 :            :                        struct ovsdb_txn_row *txn_row)
     759                 :            : {
     760                 :      64718 :     struct ovsdb_txn_table *txn_table = txn_row->table->txn_table;
     761                 :      64718 :     struct ovsdb_table *table = txn_row->table;
     762                 :      64718 :     struct ovsdb_row *row = txn_row->new;
     763                 :            :     size_t i;
     764                 :            : 
     765         [ +  + ]:      64718 :     if (!row) {
     766                 :       4244 :         return NULL;
     767                 :            :     }
     768                 :            : 
     769         [ +  + ]:     108535 :     for (i = 0; i < table->schema->n_indexes; i++) {
     770                 :      48082 :         const struct ovsdb_column_set *index = &table->schema->indexes[i];
     771                 :            :         struct ovsdb_row *irow;
     772                 :            :         uint32_t hash;
     773                 :            : 
     774                 :      48082 :         hash = ovsdb_row_hash_columns(row, index, 0);
     775                 :      48082 :         irow = ovsdb_index_search(&txn_table->txn_indexes[i], row, i, hash);
     776         [ +  + ]:      48082 :         if (irow) {
     777                 :         14 :             return duplicate_index_row(index, irow, row);
     778                 :            :         }
     779                 :            : 
     780                 :      48068 :         irow = ovsdb_index_search(&table->indexes[i], row, i, hash);
     781 [ +  + ][ +  + ]:      48068 :         if (irow && !irow->txn_row) {
     782                 :          7 :             return duplicate_index_row(index, irow, row);
     783                 :            :         }
     784                 :            : 
     785                 :      48061 :         hmap_insert(&txn_table->txn_indexes[i],
     786                 :            :                     ovsdb_row_get_index_node(row, i), hash);
     787                 :            :     }
     788                 :            : 
     789                 :      60453 :     return NULL;
     790                 :            : }
     791                 :            : 
     792                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
     793                 :      64679 : update_version(struct ovsdb_txn *txn OVS_UNUSED, struct ovsdb_txn_row *txn_row)
     794                 :            : {
     795                 :      64679 :     struct ovsdb_table *table = txn_row->table;
     796                 :      64679 :     size_t n_columns = shash_count(&table->schema->columns);
     797                 :            : 
     798 [ +  + ][ +  + ]:      64679 :     if (txn_row->old && txn_row->new
     799         [ +  + ]:      42594 :         && !bitmap_is_all_zeros(txn_row->changed, n_columns)) {
     800                 :      17707 :         bitmap_set1(txn_row->changed, OVSDB_COL_VERSION);
     801                 :      17707 :         uuid_generate(ovsdb_row_get_version_rw(txn_row->new));
     802                 :            :     }
     803                 :            : 
     804                 :      64679 :     return NULL;
     805                 :            : }
     806                 :            : 
     807                 :            : static struct ovsdb_error *
     808                 :      15966 : ovsdb_txn_commit_(struct ovsdb_txn *txn, bool durable)
     809                 :            : {
     810                 :            :     struct ovsdb_replica *replica;
     811                 :            :     struct ovsdb_error *error;
     812                 :            : 
     813                 :            :     /* Figure out what actually changed, and abort early if the transaction
     814                 :            :      * was really a no-op. */
     815                 :      15966 :     error = for_each_txn_row(txn, determine_changes);
     816         [ -  + ]:      15966 :     if (error) {
     817                 :          0 :         return OVSDB_WRAP_BUG("can't happen", error);
     818                 :            :     }
     819         [ +  + ]:      15966 :     if (ovs_list_is_empty(&txn->txn_tables)) {
     820                 :       4106 :         ovsdb_txn_abort(txn);
     821                 :       4106 :         return NULL;
     822                 :            :     }
     823                 :            : 
     824                 :            :     /* Update reference counts and check referential integrity. */
     825                 :      11860 :     error = update_ref_counts(txn);
     826         [ +  + ]:      11860 :     if (error) {
     827                 :         92 :         ovsdb_txn_abort(txn);
     828                 :         92 :         return error;
     829                 :            :     }
     830                 :            : 
     831                 :            :     /* Delete unreferenced, non-root rows. */
     832                 :      11768 :     error = for_each_txn_row(txn, collect_garbage);
     833         [ -  + ]:      11768 :     if (error) {
     834                 :          0 :         ovsdb_txn_abort(txn);
     835                 :          0 :         return OVSDB_WRAP_BUG("can't happen", error);
     836                 :            :     }
     837                 :            : 
     838                 :            :     /* Check maximum rows table constraints. */
     839                 :      11768 :     error = check_max_rows(txn);
     840         [ +  + ]:      11768 :     if (error) {
     841                 :          7 :         ovsdb_txn_abort(txn);
     842                 :          7 :         return error;
     843                 :            :     }
     844                 :            : 
     845                 :            :     /* Check reference counts and remove bad references for "weak" referential
     846                 :            :      * integrity. */
     847                 :      11761 :     error = for_each_txn_row(txn, assess_weak_refs);
     848         [ +  + ]:      11761 :     if (error) {
     849                 :         21 :         ovsdb_txn_abort(txn);
     850                 :         21 :         return error;
     851                 :            :     }
     852                 :            : 
     853                 :            :     /* Verify that the indexes will still be unique post-transaction. */
     854                 :      11740 :     error = for_each_txn_row(txn, check_index_uniqueness);
     855         [ +  + ]:      11740 :     if (error) {
     856                 :         21 :         ovsdb_txn_abort(txn);
     857                 :         21 :         return error;
     858                 :            :     }
     859                 :            : 
     860                 :            :     /* Update _version for rows that changed.  */
     861                 :      11719 :     error = for_each_txn_row(txn, update_version);
     862         [ -  + ]:      11719 :     if (error) {
     863                 :          0 :         return OVSDB_WRAP_BUG("can't happen", error);
     864                 :            :     }
     865                 :            : 
     866                 :            :     /* Send the commit to each replica. */
     867         [ +  + ]:      39795 :     LIST_FOR_EACH (replica, node, &txn->db->replicas) {
     868                 :      28076 :         error = (replica->class->commit)(replica, txn, durable);
     869         [ -  + ]:      28076 :         if (error) {
     870                 :            :             /* We don't support two-phase commit so only the first replica is
     871                 :            :              * allowed to report an error. */
     872         [ #  # ]:          0 :             ovs_assert(&replica->node == txn->db->replicas.next);
     873                 :            : 
     874                 :          0 :             ovsdb_txn_abort(txn);
     875                 :          0 :             return error;
     876                 :            :         }
     877                 :            :     }
     878                 :            : 
     879                 :            :     /* Finalize commit. */
     880                 :      11719 :     txn->db->run_triggers = true;
     881                 :      11719 :     ovsdb_error_assert(for_each_txn_row(txn, ovsdb_txn_update_weak_refs));
     882                 :      11719 :     ovsdb_error_assert(for_each_txn_row(txn, ovsdb_txn_row_commit));
     883                 :      11719 :     ovsdb_txn_free(txn);
     884                 :            : 
     885                 :      11719 :     return NULL;
     886                 :            : }
     887                 :            : 
     888                 :            : struct ovsdb_error *
     889                 :      15966 : ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable)
     890                 :            : {
     891                 :            :    struct ovsdb_error *err;
     892                 :            : 
     893                 :      15966 :    PERF(__func__, err = ovsdb_txn_commit_(txn, durable));
     894                 :      15966 :    return err;
     895                 :            : }
     896                 :            : 
     897                 :            : void
     898                 :      28076 : ovsdb_txn_for_each_change(const struct ovsdb_txn *txn,
     899                 :            :                           ovsdb_txn_row_cb_func *cb, void *aux)
     900                 :            : {
     901                 :            :     struct ovsdb_txn_table *t;
     902                 :            :     struct ovsdb_txn_row *r;
     903                 :            : 
     904         [ +  + ]:      89837 :     LIST_FOR_EACH (t, node, &txn->txn_tables) {
     905 [ +  + ][ -  + ]:     241203 :         HMAP_FOR_EACH (r, hmap_node, &t->txn_rows) {
     906 [ +  + ][ +  + ]:     179600 :             if ((r->old || r->new) && !cb(r->old, r->new, r->changed, aux)) {
                 [ +  + ]
     907                 :        158 :                 break;
     908                 :            :             }
     909                 :            :         }
     910                 :            :    }
     911                 :      28076 : }
     912                 :            : 
     913                 :            : static struct ovsdb_txn_table *
     914                 :      65025 : ovsdb_txn_create_txn_table(struct ovsdb_txn *txn, struct ovsdb_table *table)
     915                 :            : {
     916         [ +  + ]:      65025 :     if (!table->txn_table) {
     917                 :            :         struct ovsdb_txn_table *txn_table;
     918                 :            :         size_t i;
     919                 :            : 
     920                 :      23740 :         table->txn_table = txn_table = xmalloc(sizeof *table->txn_table);
     921                 :      23740 :         txn_table->table = table;
     922                 :      23740 :         hmap_init(&txn_table->txn_rows);
     923                 :      23740 :         txn_table->serial = serial - 1;
     924                 :      23740 :         txn_table->txn_indexes = xmalloc(table->schema->n_indexes
     925                 :            :                                          * sizeof *txn_table->txn_indexes);
     926         [ +  + ]:      39449 :         for (i = 0; i < table->schema->n_indexes; i++) {
     927                 :      15709 :             hmap_init(&txn_table->txn_indexes[i]);
     928                 :            :         }
     929                 :      23740 :         ovs_list_push_back(&txn->txn_tables, &txn_table->node);
     930                 :            :     }
     931                 :      65025 :     return table->txn_table;
     932                 :            : }
     933                 :            : 
     934                 :            : static struct ovsdb_txn_row *
     935                 :      65025 : ovsdb_txn_row_create(struct ovsdb_txn *txn, struct ovsdb_table *table,
     936                 :            :                      const struct ovsdb_row *old_, struct ovsdb_row *new)
     937                 :            : {
     938         [ +  + ]:      65025 :     const struct ovsdb_row *row = old_ ? old_ : new;
     939                 :      65025 :     struct ovsdb_row *old = CONST_CAST(struct ovsdb_row *, old_);
     940                 :      65025 :     size_t n_columns = shash_count(&table->schema->columns);
     941                 :            :     struct ovsdb_txn_table *txn_table;
     942                 :            :     struct ovsdb_txn_row *txn_row;
     943                 :            : 
     944                 :      65025 :     txn_row = xzalloc(offsetof(struct ovsdb_txn_row, changed)
     945                 :      65025 :                       + bitmap_n_bytes(n_columns));
     946                 :      65025 :     txn_row->uuid = *ovsdb_row_get_uuid(row);
     947                 :      65025 :     txn_row->table = row->table;
     948                 :      65025 :     txn_row->old = old;
     949                 :      65025 :     txn_row->new = new;
     950         [ +  + ]:      65025 :     txn_row->n_refs = old ? old->n_refs : 0;
     951                 :      65025 :     txn_row->serial = serial - 1;
     952                 :            : 
     953         [ +  + ]:      65025 :     if (old) {
     954                 :      46992 :         old->txn_row = txn_row;
     955                 :            :     }
     956         [ +  + ]:      65025 :     if (new) {
     957                 :      63976 :         new->txn_row = txn_row;
     958                 :            :     }
     959                 :            : 
     960                 :      65025 :     txn_table = ovsdb_txn_create_txn_table(txn, table);
     961         [ +  + ]:      65025 :     hmap_insert(&txn_table->txn_rows, &txn_row->hmap_node,
     962                 :            :                 ovsdb_row_hash(old ? old : new));
     963                 :            : 
     964                 :      65025 :     return txn_row;
     965                 :            : }
     966                 :            : 
     967                 :            : struct ovsdb_row *
     968                 :      46710 : ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_)
     969                 :            : {
     970                 :      46710 :     struct ovsdb_row *ro_row = CONST_CAST(struct ovsdb_row *, ro_row_);
     971                 :            : 
     972         [ +  + ]:      46710 :     if (ro_row->txn_row) {
     973         [ -  + ]:        767 :         ovs_assert(ro_row == ro_row->txn_row->new);
     974                 :        767 :         return ro_row;
     975                 :            :     } else {
     976                 :      45943 :         struct ovsdb_table *table = ro_row->table;
     977                 :            :         struct ovsdb_row *rw_row;
     978                 :            : 
     979                 :      45943 :         rw_row = ovsdb_row_clone(ro_row);
     980                 :      45943 :         rw_row->n_refs = ro_row->n_refs;
     981                 :      45943 :         ovsdb_txn_row_create(txn, table, ro_row, rw_row);
     982                 :      45943 :         hmap_replace(&table->rows, &ro_row->hmap_node, &rw_row->hmap_node);
     983                 :            : 
     984                 :      45943 :         return rw_row;
     985                 :            :     }
     986                 :            : }
     987                 :            : 
     988                 :            : void
     989                 :      18033 : ovsdb_txn_row_insert(struct ovsdb_txn *txn, struct ovsdb_row *row)
     990                 :            : {
     991                 :      18033 :     uint32_t hash = ovsdb_row_hash(row);
     992                 :      18033 :     struct ovsdb_table *table = row->table;
     993                 :            : 
     994                 :      18033 :     uuid_generate(ovsdb_row_get_version_rw(row));
     995                 :            : 
     996                 :      18033 :     ovsdb_txn_row_create(txn, table, NULL, row);
     997                 :      18033 :     hmap_insert(&table->rows, &row->hmap_node, hash);
     998                 :      18033 : }
     999                 :            : 
    1000                 :            : /* 'row' must be assumed destroyed upon return; the caller must not reference
    1001                 :            :  * it again. */
    1002                 :            : void
    1003                 :       1078 : ovsdb_txn_row_delete(struct ovsdb_txn *txn, const struct ovsdb_row *row_)
    1004                 :            : {
    1005                 :       1078 :     struct ovsdb_row *row = CONST_CAST(struct ovsdb_row *, row_);
    1006                 :       1078 :     struct ovsdb_table *table = row->table;
    1007                 :       1078 :     struct ovsdb_txn_row *txn_row = row->txn_row;
    1008                 :            : 
    1009                 :       1078 :     hmap_remove(&table->rows, &row->hmap_node);
    1010                 :            : 
    1011         [ +  + ]:       1078 :     if (!txn_row) {
    1012                 :       1049 :         ovsdb_txn_row_create(txn, table, row, NULL);
    1013                 :            :     } else {
    1014         [ -  + ]:         29 :         ovs_assert(txn_row->new == row);
    1015         [ +  + ]:         29 :         if (txn_row->old) {
    1016                 :          2 :             txn_row->new = NULL;
    1017                 :            :         } else {
    1018                 :         27 :             hmap_remove(&table->txn_table->txn_rows, &txn_row->hmap_node);
    1019                 :         27 :             free(txn_row);
    1020                 :            :         }
    1021                 :         29 :         ovsdb_row_destroy(row);
    1022                 :            :     }
    1023                 :       1078 : }
    1024                 :            : 
    1025                 :            : void
    1026                 :       4131 : ovsdb_txn_add_comment(struct ovsdb_txn *txn, const char *s)
    1027                 :            : {
    1028         [ -  + ]:       4131 :     if (txn->comment.length) {
    1029                 :          0 :         ds_put_char(&txn->comment, '\n');
    1030                 :            :     }
    1031                 :       4131 :     ds_put_cstr(&txn->comment, s);
    1032                 :       4131 : }
    1033                 :            : 
    1034                 :            : const char *
    1035                 :      10407 : ovsdb_txn_get_comment(const struct ovsdb_txn *txn)
    1036                 :            : {
    1037         [ +  + ]:      10407 :     return txn->comment.length ? ds_cstr_ro(&txn->comment) : NULL;
    1038                 :            : }
    1039                 :            : 
    1040                 :            : static void
    1041                 :      64998 : ovsdb_txn_row_prefree(struct ovsdb_txn_row *txn_row)
    1042                 :            : {
    1043                 :      64998 :     struct ovsdb_txn_table *txn_table = txn_row->table->txn_table;
    1044                 :            : 
    1045                 :      64998 :     txn_table->n_processed--;
    1046                 :      64998 :     hmap_remove(&txn_table->txn_rows, &txn_row->hmap_node);
    1047                 :            : 
    1048         [ +  + ]:      64998 :     if (txn_row->old) {
    1049                 :      46992 :         txn_row->old->txn_row = NULL;
    1050                 :            :     }
    1051         [ +  + ]:      64998 :     if (txn_row->new) {
    1052                 :      60673 :         txn_row->new->txn_row = NULL;
    1053                 :            :     }
    1054                 :      64998 : }
    1055                 :            : 
    1056                 :            : static void
    1057                 :      23740 : ovsdb_txn_table_destroy(struct ovsdb_txn_table *txn_table)
    1058                 :            : {
    1059                 :            :     size_t i;
    1060                 :            : 
    1061         [ -  + ]:      23740 :     ovs_assert(hmap_is_empty(&txn_table->txn_rows));
    1062                 :            : 
    1063         [ +  + ]:      39449 :     for (i = 0; i < txn_table->table->schema->n_indexes; i++) {
    1064                 :      15709 :         hmap_destroy(&txn_table->txn_indexes[i]);
    1065                 :            :     }
    1066                 :      23740 :     free(txn_table->txn_indexes);
    1067                 :            : 
    1068                 :      23740 :     txn_table->table->txn_table = NULL;
    1069                 :      23740 :     hmap_destroy(&txn_table->txn_rows);
    1070                 :      23740 :     ovs_list_remove(&txn_table->node);
    1071                 :      23740 :     free(txn_table);
    1072                 :      23740 : }
    1073                 :            : 
    1074                 :            : /* Calls 'cb' for every txn_row within 'txn'.  If 'cb' returns nonnull, this
    1075                 :            :  * aborts the iteration and for_each_txn_row() passes the error up.  Otherwise,
    1076                 :            :  * returns a null pointer after iteration is complete.
    1077                 :            :  *
    1078                 :            :  * 'cb' may insert new txn_rows and new txn_tables into 'txn'.  It may delete
    1079                 :            :  * the txn_row that it is passed in, or txn_rows in txn_tables other than the
    1080                 :            :  * one passed to 'cb'.  It may *not* delete txn_rows other than the one passed
    1081                 :            :  * in within the same txn_table.  It may *not* delete any txn_tables.  As long
    1082                 :            :  * as these rules are followed, 'cb' will be called exactly once for each
    1083                 :            :  * txn_row in 'txn', even those added by 'cb'.
    1084                 :            :  *
    1085                 :            :  * (Even though 'cb' is not allowed to delete some txn_rows, it can still
    1086                 :            :  * delete any actual row by clearing a txn_row's 'new' member.)
    1087                 :            :  */
    1088                 :            : static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
    1089                 :     114449 : for_each_txn_row(struct ovsdb_txn *txn,
    1090                 :            :                  struct ovsdb_error *(*cb)(struct ovsdb_txn *,
    1091                 :            :                                            struct ovsdb_txn_row *))
    1092                 :            : {
    1093                 :            :     bool any_work;
    1094                 :            : 
    1095                 :     114449 :     serial++;
    1096                 :            : 
    1097                 :            :     do {
    1098                 :            :         struct ovsdb_txn_table *t, *next_txn_table;
    1099                 :            : 
    1100                 :     222281 :         any_work = false;
    1101 [ +  + ][ +  + ]:     623876 :         LIST_FOR_EACH_SAFE (t, next_txn_table, node, &txn->txn_tables) {
    1102         [ +  + ]:     401729 :             if (t->serial != serial) {
    1103                 :     210603 :                 t->serial = serial;
    1104                 :     210603 :                 t->n_processed = 0;
    1105                 :            :             }
    1106                 :            : 
    1107         [ +  + ]:     613152 :             while (t->n_processed < hmap_count(&t->txn_rows)) {
    1108                 :            :                 struct ovsdb_txn_row *r, *next_txn_row;
    1109                 :            : 
    1110 [ +  + ][ -  + ]:     761973 :                 HMAP_FOR_EACH_SAFE (r, next_txn_row, hmap_node, &t->txn_rows) {
                 [ +  + ]
    1111         [ +  + ]:     550550 :                     if (r->serial != serial) {
    1112                 :            :                         struct ovsdb_error *error;
    1113                 :            : 
    1114                 :     549185 :                         r->serial = serial;
    1115                 :     549185 :                         t->n_processed++;
    1116                 :     549185 :                         any_work = true;
    1117                 :            : 
    1118                 :     549185 :                         error = cb(txn, r);
    1119         [ +  + ]:     549185 :                         if (error) {
    1120                 :        134 :                             return error;
    1121                 :            :                         }
    1122                 :            :                     }
    1123                 :            :                 }
    1124                 :            :             }
    1125         [ +  + ]:     401595 :             if (hmap_is_empty(&t->txn_rows)) {
    1126                 :            :                 /* Table is empty.  Drop it. */
    1127                 :      23740 :                 ovsdb_txn_table_destroy(t);
    1128                 :            :             }
    1129                 :            :         }
    1130         [ +  + ]:     222147 :     } while (any_work);
    1131                 :            : 
    1132                 :     114315 :     return NULL;
    1133                 :            : }

Generated by: LCOV version 1.12