1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
|
/* Copyright (c) 2009 Nicira Networks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <config.h>
#include "transaction.h"
#include <assert.h>
#include "hash.h"
#include "hmap.h"
#include "json.h"
#include "ovsdb-error.h"
#include "ovsdb.h"
#include "row.h"
#include "table.h"
#include "uuid.h"
struct ovsdb_txn {
struct ovsdb *db;
struct hmap txn_tables; /* Contains "struct ovsdb_txn_table"s. */
};
/* A table modified by a transaction. */
struct ovsdb_txn_table {
struct hmap_node hmap_node; /* Element in ovsdb_txn's txn_tables hmap. */
struct ovsdb_table *table;
struct hmap txn_rows; /* Contains "struct ovsdb_txn_row"s. */
};
/* A row modified by the transaction:
*
* - A row added by a transaction will have null 'old' and non-null 'new'.
*
* - A row deleted by a transaction will have non-null 'old' and null
* 'new'.
*
* - A row modified by a transaction will have non-null 'old' and 'new'.
*
* - 'old' and 'new' both null is invalid. It would indicate that a row
* was added then deleted within a single transaction, but we instead
* handle that case by deleting the txn_row entirely.
*/
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_txn *
ovsdb_txn_create(struct ovsdb *db)
{
struct ovsdb_txn *txn = xmalloc(sizeof *txn);
txn->db = db;
hmap_init(&txn->txn_tables);
return txn;
}
static void
ovsdb_txn_destroy(struct ovsdb_txn *txn, void (*cb)(struct ovsdb_txn_row *))
{
struct ovsdb_txn_table *txn_table, *next_txn_table;
HMAP_FOR_EACH_SAFE (txn_table, next_txn_table,
struct ovsdb_txn_table, hmap_node, &txn->txn_tables)
{
struct ovsdb_txn_row *txn_row, *next_txn_row;
HMAP_FOR_EACH_SAFE (txn_row, next_txn_row,
struct ovsdb_txn_row, hmap_node,
&txn_table->txn_rows)
{
if (txn_row->old) {
txn_row->old->txn_row = NULL;
}
if (txn_row->new) {
txn_row->new->txn_row = NULL;
}
cb(txn_row);
free(txn_row);
}
hmap_destroy(&txn_table->txn_rows);
free(txn_table);
}
hmap_destroy(&txn->txn_tables);
free(txn);
}
static void
ovsdb_txn_row_abort(struct ovsdb_txn_row *txn_row)
{
struct ovsdb_row *old = txn_row->old;
struct ovsdb_row *new = txn_row->new;
if (!old) {
hmap_remove(&new->table->rows, &new->hmap_node);
} else if (!new) {
hmap_insert(&old->table->rows, &old->hmap_node, ovsdb_row_hash(old));
} else {
hmap_replace(&new->table->rows, &new->hmap_node, &old->hmap_node);
}
ovsdb_row_destroy(new);
}
void
ovsdb_txn_abort(struct ovsdb_txn *txn)
{
ovsdb_txn_destroy(txn, ovsdb_txn_row_abort);
}
static void
ovsdb_txn_row_commit(struct ovsdb_txn_row *txn_row)
{
ovsdb_row_destroy(txn_row->old);
}
struct ovsdb_error *
ovsdb_txn_commit(struct ovsdb_txn *txn, bool durable)
{
struct ovsdb_replica *replica;
struct ovsdb_error *error;
LIST_FOR_EACH (replica, struct ovsdb_replica, node, &txn->db->replicas) {
error = (replica->class->commit)(replica, txn, durable);
if (error) {
/* We don't support two-phase commit so only the first replica is
* allowed to report an error. */
assert(&replica->node == txn->db->replicas.next);
ovsdb_txn_abort(txn);
return error;
}
}
txn->db->run_triggers = true;
ovsdb_txn_destroy(txn, ovsdb_txn_row_commit);
return NULL;
}
void
ovsdb_txn_for_each_change(const struct ovsdb_txn *txn,
ovsdb_txn_row_cb_func *cb, void *aux)
{
struct ovsdb_txn_table *t;
struct ovsdb_txn_row *r;
HMAP_FOR_EACH (t, struct ovsdb_txn_table, hmap_node, &txn->txn_tables) {
HMAP_FOR_EACH (r, struct ovsdb_txn_row, hmap_node, &t->txn_rows) {
if (!cb(r->old, r->new, aux)) {
break;
}
}
}
}
static struct ovsdb_txn_table *
ovsdb_txn_get_txn_table__(struct ovsdb_txn *txn,
const struct ovsdb_table *table,
uint32_t hash)
{
struct ovsdb_txn_table *txn_table;
HMAP_FOR_EACH_IN_BUCKET (txn_table, struct ovsdb_txn_table, hmap_node,
hash, &txn->txn_tables) {
if (txn_table->table == table) {
return txn_table;
}
}
return NULL;
}
static struct ovsdb_txn_table *
ovsdb_txn_get_txn_table(struct ovsdb_txn *txn, const struct ovsdb_table *table)
{
return ovsdb_txn_get_txn_table__(txn, table, hash_pointer(table, 0));
}
static struct ovsdb_txn_table *
ovsdb_txn_create_txn_table(struct ovsdb_txn *txn,
struct ovsdb_table *table)
{
uint32_t hash = hash_pointer(table, 0);
struct ovsdb_txn_table *txn_table;
txn_table = ovsdb_txn_get_txn_table__(txn, table, hash);
if (!txn_table) {
txn_table = xmalloc(sizeof *txn_table);
txn_table->table = table;
hmap_init(&txn_table->txn_rows);
hmap_insert(&txn->txn_tables, &txn_table->hmap_node, hash);
}
return txn_table;
}
static struct ovsdb_txn_row *
ovsdb_txn_row_create(struct ovsdb_txn_table *txn_table,
const struct ovsdb_row *old, struct ovsdb_row *new)
{
uint32_t hash = ovsdb_row_hash(old ? old : new);
struct ovsdb_txn_row *txn_row;
txn_row = xmalloc(sizeof *txn_row);
txn_row->old = (struct ovsdb_row *) old;
txn_row->new = new;
hmap_insert(&txn_table->txn_rows, &txn_row->hmap_node, hash);
return txn_row;
}
struct ovsdb_row *
ovsdb_txn_row_modify(struct ovsdb_txn *txn, const struct ovsdb_row *ro_row_)
{
struct ovsdb_row *ro_row = (struct ovsdb_row *) ro_row_;
if (ro_row->txn_row) {
assert(ro_row == ro_row->txn_row->new);
return ro_row;
} else {
struct ovsdb_table *table = ro_row->table;
struct ovsdb_txn_table *txn_table;
struct ovsdb_row *rw_row;
txn_table = ovsdb_txn_create_txn_table(txn, table);
rw_row = ovsdb_row_clone(ro_row);
uuid_generate(ovsdb_row_get_version_rw(rw_row));
rw_row->txn_row = 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;
}
}
void
ovsdb_txn_row_insert(struct ovsdb_txn *txn, struct ovsdb_row *row)
{
uint32_t hash = ovsdb_row_hash(row);
struct ovsdb_table *table = row->table;
struct ovsdb_txn_table *txn_table;
uuid_generate(ovsdb_row_get_version_rw(row));
txn_table = ovsdb_txn_create_txn_table(txn, table);
row->txn_row = ovsdb_txn_row_create(txn_table, NULL, row);
hmap_insert(&table->rows, &row->hmap_node, hash);
}
/* 'row' must be assumed destroyed upon return; the caller must not reference
* it again. */
void
ovsdb_txn_row_delete(struct ovsdb_txn *txn, const struct ovsdb_row *row_)
{
struct ovsdb_row *row = (struct ovsdb_row *) row_;
struct ovsdb_table *table = row->table;
struct ovsdb_txn_row *txn_row = row->txn_row;
struct ovsdb_txn_table *txn_table;
hmap_remove(&table->rows, &row->hmap_node);
if (!txn_row) {
txn_table = ovsdb_txn_create_txn_table(txn, table);
row->txn_row = ovsdb_txn_row_create(txn_table, row, NULL);
} else {
assert(txn_row->new == row);
if (txn_row->old) {
txn_row->new = NULL;
} else {
txn_table = ovsdb_txn_get_txn_table(txn, table);
hmap_remove(&txn_table->txn_rows, &txn_row->hmap_node);
}
ovsdb_row_destroy(row);
}
}
|