From: Ben Pfaff Date: Tue, 1 Apr 2014 23:26:47 +0000 (-0700) Subject: jsonrpc-server: Track monitor updates separately from sending them. X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;ds=sidebyside;h=03ad470a15ce348d56b8bba5cd8e9aa1b447d633;p=sliver-openvswitch.git jsonrpc-server: Track monitor updates separately from sending them. This will make combining monitor updates easier in the next commit. Signed-off-by: Ben Pfaff Acked-by: Andy Zhou --- diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c index a81648bf2..3e4e71ec7 100644 --- a/ovsdb/jsonrpc-server.c +++ b/ovsdb/jsonrpc-server.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. +/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -1023,6 +1023,14 @@ struct ovsdb_jsonrpc_monitor_column { enum ovsdb_jsonrpc_monitor_selection select; }; +/* A row that has changed in a monitored table. */ +struct ovsdb_jsonrpc_monitor_row { + struct hmap_node hmap_node; /* In ovsdb_jsonrpc_monitor_table.changes. */ + struct uuid uuid; /* UUID of row that changed. */ + struct ovsdb_datum *old; /* Old data, NULL for an inserted row. */ + struct ovsdb_datum *new; /* New data, NULL for a deleted row. */ +}; + /* A particular table being monitored. */ struct ovsdb_jsonrpc_monitor_table { const struct ovsdb_table *table; @@ -1034,6 +1042,10 @@ struct ovsdb_jsonrpc_monitor_table { /* Columns being monitored. */ struct ovsdb_jsonrpc_monitor_column *columns; size_t n_columns; + + /* Contains 'struct ovsdb_jsonrpc_monitor_row's for rows that have been + * updated but not yet flushed to the jsonrpc connection. */ + struct hmap changes; }; /* A collection of tables being monitored. */ @@ -1243,6 +1255,7 @@ ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, struct ovsdb *db, mt = xzalloc(sizeof *mt); mt->table = table; + hmap_init(&mt->changes); shash_add(&m->tables, table->schema->name, mt); /* Parse columns. */ @@ -1331,143 +1344,307 @@ ovsdb_jsonrpc_monitor_cast(struct ovsdb_replica *replica) } struct ovsdb_jsonrpc_monitor_aux { - bool initial; /* Sending initial contents of table? */ const struct ovsdb_jsonrpc_monitor *monitor; - struct json *json; /* JSON for the whole transaction. */ - - /* Current table. */ struct ovsdb_jsonrpc_monitor_table *mt; - struct json *table_json; /* JSON for table's transaction. */ }; -static bool -any_reportable_change(const struct ovsdb_jsonrpc_monitor_table *mt, - const unsigned long int *changed) +/* Finds and returns the ovsdb_jsonrpc_monitor_row in 'mt->changes' for the + * given 'uuid', or NULL if there is no such row. */ +static struct ovsdb_jsonrpc_monitor_row * +ovsdb_jsonrpc_monitor_row_find(const struct ovsdb_jsonrpc_monitor_table *mt, + const struct uuid *uuid) +{ + struct ovsdb_jsonrpc_monitor_row *row; + + HMAP_FOR_EACH_WITH_HASH (row, hmap_node, uuid_hash(uuid), &mt->changes) { + if (uuid_equals(uuid, &row->uuid)) { + return row; + } + } + return NULL; +} + +/* Allocates an array of 'mt->n_columns' ovsdb_datums and initializes them as + * copies of the data in 'row' drawn from the columns represented by + * mt->columns[]. Returns the array. + * + * If 'row' is NULL, returns NULL. */ +static struct ovsdb_datum * +clone_monitor_row_data(const struct ovsdb_jsonrpc_monitor_table *mt, + const struct ovsdb_row *row) { + struct ovsdb_datum *data; size_t i; + if (!row) { + return NULL; + } + + data = xmalloc(mt->n_columns * sizeof *data); for (i = 0; i < mt->n_columns; i++) { - const struct ovsdb_jsonrpc_monitor_column *c = &mt->columns[i]; - unsigned int idx = c->column->index; + const struct ovsdb_column *c = mt->columns[i].column; + const struct ovsdb_datum *src = &row->fields[c->index]; + struct ovsdb_datum *dst = &data[i]; + const struct ovsdb_type *type = &c->type; + + ovsdb_datum_clone(dst, src, type); + } + return data; +} + +/* Replaces the mt->n_columns ovsdb_datums in row[] by copies of the data from + * in 'row' drawn from the columns represented by mt->columns[]. */ +static void +update_monitor_row_data(const struct ovsdb_jsonrpc_monitor_table *mt, + const struct ovsdb_row *row, + struct ovsdb_datum *data) +{ + size_t i; + + for (i = 0; i < mt->n_columns; i++) { + const struct ovsdb_column *c = mt->columns[i].column; + const struct ovsdb_datum *src = &row->fields[c->index]; + struct ovsdb_datum *dst = &data[i]; + const struct ovsdb_type *type = &c->type; + + if (!ovsdb_datum_equals(src, dst, type)) { + ovsdb_datum_destroy(dst, type); + ovsdb_datum_clone(dst, src, type); + } + } +} + +/* Frees all of the mt->n_columns ovsdb_datums in data[], using the types taken + * from mt->columns[], plus 'data' itself. */ +static void +free_monitor_row_data(const struct ovsdb_jsonrpc_monitor_table *mt, + struct ovsdb_datum *data) +{ + if (data) { + size_t i; - if (c->select & OJMS_MODIFY && bitmap_is_set(changed, idx)) { - return true; + for (i = 0; i < mt->n_columns; i++) { + const struct ovsdb_column *c = mt->columns[i].column; + + ovsdb_datum_destroy(&data[i], &c->type); } + free(data); } +} - return false; +/* Frees 'row', which must have been created from 'mt'. */ +static void +ovsdb_jsonrpc_monitor_row_destroy(const struct ovsdb_jsonrpc_monitor_table *mt, + struct ovsdb_jsonrpc_monitor_row *row) +{ + if (row) { + free_monitor_row_data(mt, row->old); + free_monitor_row_data(mt, row->new); + free(row); + } } static bool ovsdb_jsonrpc_monitor_change_cb(const struct ovsdb_row *old, const struct ovsdb_row *new, - const unsigned long int *changed, + const unsigned long int *changed OVS_UNUSED, void *aux_) { struct ovsdb_jsonrpc_monitor_aux *aux = aux_; const struct ovsdb_jsonrpc_monitor *m = aux->monitor; struct ovsdb_table *table = new ? new->table : old->table; - enum ovsdb_jsonrpc_monitor_selection type; - struct json *old_json, *new_json; - struct json *row_json; - char uuid[UUID_LEN + 1]; - size_t i; + const struct uuid *uuid = ovsdb_row_get_uuid(new ? new : old); + struct ovsdb_jsonrpc_monitor_row *change; + struct ovsdb_jsonrpc_monitor_table *mt; if (!aux->mt || table != aux->mt->table) { aux->mt = shash_find_data(&m->tables, table->schema->name); - aux->table_json = NULL; if (!aux->mt) { /* We don't care about rows in this table at all. Tell the caller * to skip it. */ return false; } } + mt = aux->mt; + + change = ovsdb_jsonrpc_monitor_row_find(mt, uuid); + if (!change) { + change = xmalloc(sizeof *change); + hmap_insert(&mt->changes, &change->hmap_node, uuid_hash(uuid)); + change->uuid = *uuid; + change->old = clone_monitor_row_data(mt, old); + change->new = clone_monitor_row_data(mt, new); + } else { + if (new) { + update_monitor_row_data(mt, new, change->new); + } else { + free_monitor_row_data(mt, change->new); + change->new = NULL; + + if (!change->old) { + /* This row was added then deleted. Forget about it. */ + hmap_remove(&mt->changes, &change->hmap_node); + free(change); + } + } + } + return true; +} - type = (aux->initial ? OJMS_INITIAL - : !old ? OJMS_INSERT - : !new ? OJMS_DELETE +/* Returns JSON for a (as described in ovsdb/SPECS) for 'row' + * within 'mt', or NULL if no row update should be sent. + * + * The caller should specify 'initial' as true if the returned JSON is going to + * be used as part of the initial reply to a "monitor" request, false if it is + * going to be used as part of an "update" notification. + * + * 'changed' must be a scratch buffer for internal use that is at least + * bitmap_n_bytes(mt->n_columns) bytes long. */ +static struct json * +ovsdb_jsonrpc_monitor_compose_row_update( + const struct ovsdb_jsonrpc_monitor_table *mt, + const struct ovsdb_jsonrpc_monitor_row *row, + bool initial, unsigned long int *changed) +{ + enum ovsdb_jsonrpc_monitor_selection type; + struct json *old_json, *new_json; + struct json *row_json; + size_t i; + + type = (initial ? OJMS_INITIAL + : !row->old ? OJMS_INSERT + : !row->new ? OJMS_DELETE : OJMS_MODIFY); - if (!(aux->mt->select & type)) { - /* We don't care about this type of change (but do want to be called - * back for changes to other rows in the same table). */ - return true; + if (!(mt->select & type)) { + return NULL; } - if (type == OJMS_MODIFY && !any_reportable_change(aux->mt, changed)) { - /* Nothing of interest changed. */ - return true; + if (type == OJMS_MODIFY) { + size_t n_changes; + + n_changes = 0; + memset(changed, 0, bitmap_n_bytes(mt->n_columns)); + for (i = 0; i < mt->n_columns; i++) { + const struct ovsdb_column *c = mt->columns[i].column; + if (!ovsdb_datum_equals(&row->old[i], &row->new[i], &c->type)) { + bitmap_set1(changed, i); + n_changes++; + } + } + if (!n_changes) { + /* No actual changes: presumably a row changed and then + * changed back later. */ + return NULL; + } } + row_json = json_object_create(); old_json = new_json = NULL; if (type & (OJMS_DELETE | OJMS_MODIFY)) { old_json = json_object_create(); + json_object_put(row_json, "old", old_json); } if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { new_json = json_object_create(); + json_object_put(row_json, "new", new_json); } - for (i = 0; i < aux->mt->n_columns; i++) { - const struct ovsdb_jsonrpc_monitor_column *c = &aux->mt->columns[i]; - const struct ovsdb_column *column = c->column; - unsigned int idx = c->column->index; + for (i = 0; i < mt->n_columns; i++) { + const struct ovsdb_jsonrpc_monitor_column *c = &mt->columns[i]; if (!(type & c->select)) { - /* We don't care about this type of change for this particular - * column (but we will care about it for some other column). */ + /* We don't care about this type of change for this + * particular column (but we will care about it for some + * other column). */ continue; } - if ((type == OJMS_MODIFY && bitmap_is_set(changed, idx)) + if ((type == OJMS_MODIFY && bitmap_is_set(changed, i)) || type == OJMS_DELETE) { - json_object_put(old_json, column->name, - ovsdb_datum_to_json(&old->fields[idx], - &column->type)); + json_object_put(old_json, c->column->name, + ovsdb_datum_to_json(&row->old[i], + &c->column->type)); } if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { - json_object_put(new_json, column->name, - ovsdb_datum_to_json(&new->fields[idx], - &column->type)); + json_object_put(new_json, c->column->name, + ovsdb_datum_to_json(&row->new[i], + &c->column->type)); } } - /* Create JSON object for transaction overall. */ - if (!aux->json) { - aux->json = json_object_create(); - } + return row_json; +} - /* Create JSON object for transaction on this table. */ - if (!aux->table_json) { - aux->table_json = json_object_create(); - json_object_put(aux->json, aux->mt->table->schema->name, - aux->table_json); - } +/* Constructs and returns JSON for a object (as described in + * ovsdb/SPECS) for all the outstanding changes within 'monitor', and deletes + * all the outstanding changes from 'monitor'. Returns NULL if no update needs + * to be sent. + * + * The caller should specify 'initial' as true if the returned JSON is going to + * be used as part of the initial reply to a "monitor" request, false if it is + * going to be used as part of an "update" notification. */ +static struct json * +ovsdb_jsonrpc_monitor_compose_table_update( + const struct ovsdb_jsonrpc_monitor *monitor, bool initial) +{ + struct shash_node *node; + unsigned long int *changed; + struct json *json; + size_t max_columns; - /* Create JSON object for transaction on this row. */ - row_json = json_object_create(); - if (old_json) { - json_object_put(row_json, "old", old_json); + max_columns = 0; + SHASH_FOR_EACH (node, &monitor->tables) { + struct ovsdb_jsonrpc_monitor_table *mt = node->data; + + max_columns = MAX(max_columns, mt->n_columns); } - if (new_json) { - json_object_put(row_json, "new", new_json); + changed = xmalloc(bitmap_n_bytes(max_columns)); + + json = NULL; + SHASH_FOR_EACH (node, &monitor->tables) { + struct ovsdb_jsonrpc_monitor_table *mt = node->data; + struct ovsdb_jsonrpc_monitor_row *row, *next; + struct json *table_json = NULL; + + HMAP_FOR_EACH_SAFE (row, next, hmap_node, &mt->changes) { + struct json *row_json; + + row_json = ovsdb_jsonrpc_monitor_compose_row_update( + mt, row, initial, changed); + if (row_json) { + char uuid[UUID_LEN + 1]; + + /* Create JSON object for transaction overall. */ + if (!json) { + json = json_object_create(); + } + + /* Create JSON object for transaction on this table. */ + if (!table_json) { + table_json = json_object_create(); + json_object_put(json, mt->table->schema->name, table_json); + } + + /* Add JSON row to JSON table. */ + snprintf(uuid, sizeof uuid, UUID_FMT, UUID_ARGS(&row->uuid)); + json_object_put(table_json, uuid, row_json); + } + + hmap_remove(&mt->changes, &row->hmap_node); + ovsdb_jsonrpc_monitor_row_destroy(mt, row); + } } - /* Add JSON row to JSON table. */ - snprintf(uuid, sizeof uuid, - UUID_FMT, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old))); - json_object_put(aux->table_json, uuid, row_json); + free(changed); - return true; + return json; } static void ovsdb_jsonrpc_monitor_init_aux(struct ovsdb_jsonrpc_monitor_aux *aux, - const struct ovsdb_jsonrpc_monitor *m, - bool initial) + const struct ovsdb_jsonrpc_monitor *m) { - aux->initial = initial; aux->monitor = m; - aux->json = NULL; aux->mt = NULL; - aux->table_json = NULL; } static struct ovsdb_error * @@ -1477,15 +1654,17 @@ ovsdb_jsonrpc_monitor_commit(struct ovsdb_replica *replica, { struct ovsdb_jsonrpc_monitor *m = ovsdb_jsonrpc_monitor_cast(replica); struct ovsdb_jsonrpc_monitor_aux aux; + struct json *json; - ovsdb_jsonrpc_monitor_init_aux(&aux, m, false); + ovsdb_jsonrpc_monitor_init_aux(&aux, m); ovsdb_txn_for_each_change(txn, ovsdb_jsonrpc_monitor_change_cb, &aux); - if (aux.json) { + json = ovsdb_jsonrpc_monitor_compose_table_update(m, false); + if (json) { struct jsonrpc_msg *msg; struct json *params; params = json_array_create_2(json_clone(aux.monitor->monitor_id), - aux.json); + json); msg = jsonrpc_create_notify("update", params); jsonrpc_session_send(aux.monitor->session->js, msg); } @@ -1498,8 +1677,9 @@ ovsdb_jsonrpc_monitor_get_initial(const struct ovsdb_jsonrpc_monitor *m) { struct ovsdb_jsonrpc_monitor_aux aux; struct shash_node *node; + struct json *json; - ovsdb_jsonrpc_monitor_init_aux(&aux, m, true); + ovsdb_jsonrpc_monitor_init_aux(&aux, m); SHASH_FOR_EACH (node, &m->tables) { struct ovsdb_jsonrpc_monitor_table *mt = node->data; @@ -1511,7 +1691,8 @@ ovsdb_jsonrpc_monitor_get_initial(const struct ovsdb_jsonrpc_monitor *m) } } } - return aux.json ? aux.json : json_object_create(); + json = ovsdb_jsonrpc_monitor_compose_table_update(m, true); + return json ? json : json_object_create(); } static void @@ -1523,6 +1704,14 @@ ovsdb_jsonrpc_monitor_destroy(struct ovsdb_replica *replica) json_destroy(m->monitor_id); SHASH_FOR_EACH (node, &m->tables) { struct ovsdb_jsonrpc_monitor_table *mt = node->data; + struct ovsdb_jsonrpc_monitor_row *row, *next; + + HMAP_FOR_EACH_SAFE (row, next, hmap_node, &mt->changes) { + hmap_remove(&mt->changes, &row->hmap_node); + ovsdb_jsonrpc_monitor_row_destroy(mt, row); + } + hmap_destroy(&mt->changes); + free(mt->columns); free(mt); }