X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=ovsdb%2Fjsonrpc-server.c;h=4cba4d9ebe62fb550edbb9a0c49fb99c8135317f;hb=b0fb94a346e52f36aeef238dd5f9bef9a10c14ef;hp=279ea97e6cbb8d952d902e86adb55a5d9f753f65;hpb=b4e8d17057ff29183c026a7bbfc751520e1629f5;p=sliver-openvswitch.git diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c index 279ea97e6..4cba4d9eb 100644 --- a/ovsdb/jsonrpc-server.c +++ b/ovsdb/jsonrpc-server.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc. +/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ #include "jsonrpc-server.h" -#include #include #include "bitmap.h" @@ -83,6 +82,8 @@ static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel( struct json_array *params, const struct json *request_id); static void ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *); +static size_t ovsdb_jsonrpc_monitor_json_length_all( + struct ovsdb_jsonrpc_session *); /* JSON-RPC database server. */ @@ -99,6 +100,7 @@ struct ovsdb_jsonrpc_remote { struct ovsdb_jsonrpc_server *server; struct pstream *listener; /* Listener, if passive. */ struct list sessions; /* List of "struct ovsdb_jsonrpc_session"s. */ + uint8_t dscp; }; static struct ovsdb_jsonrpc_remote *ovsdb_jsonrpc_server_add_remote( @@ -127,9 +129,36 @@ ovsdb_jsonrpc_server_create(void) bool ovsdb_jsonrpc_server_add_db(struct ovsdb_jsonrpc_server *svr, struct ovsdb *db) { + /* The OVSDB protocol doesn't have a way to notify a client that a + * database has been added. If some client tried to use the database + * that we're adding and failed, then forcing it to reconnect seems like + * a reasonable way to make it try again. + * + * If this is too big of a hammer in practice, we could be more selective, + * e.g. disconnect only connections that actually tried to use a database + * with 'db''s name. */ + ovsdb_jsonrpc_server_reconnect(svr); + return ovsdb_server_add_db(&svr->up, db); } +/* Removes 'db' from the set of databases served out by 'svr'. Returns + * true if successful, false if there is no database associated with 'db'. */ +bool +ovsdb_jsonrpc_server_remove_db(struct ovsdb_jsonrpc_server *svr, + struct ovsdb *db) +{ + /* There might be pointers to 'db' from 'svr', such as monitors or + * outstanding transactions. Disconnect all JSON-RPC connections to avoid + * accesses to freed memory. + * + * If this is too big of a hammer in practice, we could be more selective, + * e.g. disconnect only connections that actually reference 'db'. */ + ovsdb_jsonrpc_server_reconnect(svr); + + return ovsdb_server_remove_db(&svr->up, db); +} + void ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr) { @@ -198,7 +227,7 @@ ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr, error = jsonrpc_pstream_open(name, &listener, options->dscp); if (error && error != EAFNOSUPPORT) { - VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, strerror(error)); + VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, ovs_strerror(error)); return NULL; } @@ -206,10 +235,11 @@ ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr, remote->server = svr; remote->listener = listener; list_init(&remote->sessions); + remote->dscp = options->dscp; shash_add(&svr->remotes, name, remote); if (!listener) { - ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name)); + ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name, true)); } return remote; } @@ -282,12 +312,13 @@ ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr) error = pstream_accept(remote->listener, &stream); if (!error) { struct jsonrpc_session *js; - js = jsonrpc_session_open_unreliably(jsonrpc_open(stream)); + js = jsonrpc_session_open_unreliably(jsonrpc_open(stream), + remote->dscp); ovsdb_jsonrpc_session_create(remote, js); } else if (error != EAGAIN) { VLOG_WARN_RL(&rl, "%s: accept failed: %s", pstream_get_name(remote->listener), - strerror(error)); + ovs_strerror(error)); } } @@ -333,6 +364,8 @@ struct ovsdb_jsonrpc_session { struct list node; /* Element in remote's sessions list. */ struct ovsdb_session up; struct ovsdb_jsonrpc_remote *remote; + size_t backlog_threshold; /* See ovsdb_jsonrpc_session_run(). */ + size_t reply_backlog; /* Triggers. */ struct hmap triggers; /* Hmap of "struct ovsdb_jsonrpc_trigger"s. */ @@ -369,6 +402,8 @@ ovsdb_jsonrpc_session_create(struct ovsdb_jsonrpc_remote *remote, list_push_back(&remote->sessions, &s->node); hmap_init(&s->triggers); hmap_init(&s->monitors); + s->reply_backlog = 0; + s->backlog_threshold = 1024 * 1024; s->js = js; s->js_seqno = jsonrpc_session_get_seqno(js); @@ -382,9 +417,13 @@ ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s) { ovsdb_jsonrpc_monitor_remove_all(s); ovsdb_jsonrpc_session_unlock_all(s); + ovsdb_jsonrpc_trigger_complete_all(s); + + hmap_destroy(&s->monitors); + hmap_destroy(&s->triggers); + jsonrpc_session_close(s->js); list_remove(&s->node); - ovsdb_session_destroy(&s->up); s->remote->server->n_sessions--; ovsdb_session_destroy(&s->up); free(s); @@ -393,6 +432,8 @@ ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s) static int ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s) { + size_t backlog; + jsonrpc_session_run(s->js); if (s->js_seqno != jsonrpc_session_get_seqno(s->js)) { s->js_seqno = jsonrpc_session_get_seqno(s->js); @@ -403,7 +444,8 @@ ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s) ovsdb_jsonrpc_trigger_complete_done(s); - if (!jsonrpc_session_get_backlog(s->js)) { + backlog = jsonrpc_session_get_backlog(s->js); + if (!backlog) { struct jsonrpc_msg *msg = jsonrpc_session_recv(s->js); if (msg) { if (msg->type == JSONRPC_REQUEST) { @@ -418,6 +460,39 @@ ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s) jsonrpc_msg_destroy(msg); } } + s->reply_backlog = jsonrpc_session_get_backlog(s->js); + } else if (backlog > s->reply_backlog + s->backlog_threshold) { + /* We have a lot of data queued to send to the client. The data is + * likely to be mostly monitor updates. It is unlikely that the + * monitor updates are due to transactions by 's', because we will not + * let 's' make any more transactions until it drains its backlog to 0 + * (see previous 'if' case). So the monitor updates are probably due + * to transactions made by database clients other than 's'. We can't + * fix that by preventing 's' from executing more transactions. We + * could fix it by preventing every client from executing transactions, + * but then one slow or hung client could prevent other clients from + * doing useful work. + * + * Our solution is to cap the maximum backlog to O(1) in the amount of + * data in the database. If the backlog exceeds that amount, then we + * disconnect the client. When it reconnects, it can fetch the entire + * contents of the database using less data than was previously + * backlogged. */ + size_t monitor_length; + + monitor_length = ovsdb_jsonrpc_monitor_json_length_all(s); + if (backlog > s->reply_backlog + monitor_length * 2) { + VLOG_INFO("%s: %zu bytes backlogged but a complete replica " + "would only take %zu bytes, disconnecting", + jsonrpc_session_get_name(s->js), + backlog - s->reply_backlog, monitor_length); + jsonrpc_session_force_reconnect(s->js); + } else { + /* The backlog is not unreasonably big. Only check again after it + * becomes much bigger. */ + s->backlog_threshold = 2 * MAX(s->backlog_threshold * 2, + monitor_length); + } } return jsonrpc_session_is_alive(s->js) ? 0 : ETIMEDOUT; } @@ -518,6 +593,22 @@ ovsdb_jsonrpc_session_set_all_options( { struct ovsdb_jsonrpc_session *s; + if (remote->listener) { + int error; + + error = pstream_set_dscp(remote->listener, options->dscp); + if (error) { + VLOG_ERR("%s: set_dscp failed %s", + pstream_get_name(remote->listener), ovs_strerror(error)); + } else { + remote->dscp = options->dscp; + } + /* + * XXX race window between setting dscp to listening socket + * and accepting socket. Accepted socket may have old dscp value. + * Ignore this race window for now. + */ + } LIST_FOR_EACH (s, node, &remote->sessions) { ovsdb_jsonrpc_session_set_options(s, options); } @@ -533,6 +624,10 @@ ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote, struct reconnect_stats rstats; struct ds locks_held, locks_waiting, locks_lost; + status->bound_port = (remote->listener + ? pstream_get_bound_port(remote->listener) + : htons(0)); + if (list_is_empty(&remote->sessions)) { return false; } @@ -1001,6 +1096,8 @@ struct ovsdb_jsonrpc_monitor *ovsdb_jsonrpc_monitor_find( static void ovsdb_jsonrpc_monitor_destroy(struct ovsdb_replica *); static struct json *ovsdb_jsonrpc_monitor_get_initial( const struct ovsdb_jsonrpc_monitor *); +static size_t ovsdb_jsonrpc_monitor_json_length( + const struct ovsdb_jsonrpc_monitor *); static bool parse_bool(struct ovsdb_parser *parser, const char *name, bool default_value) @@ -1270,10 +1367,26 @@ ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s) } } +/* Returns an overestimate of the number of bytes of JSON data required to + * report the current contents of the database over all the monitors currently + * configured in 's'. */ +static size_t +ovsdb_jsonrpc_monitor_json_length_all(struct ovsdb_jsonrpc_session *s) +{ + struct ovsdb_jsonrpc_monitor *m; + size_t length; + + length = 0; + HMAP_FOR_EACH (m, node, &s->monitors) { + length += ovsdb_jsonrpc_monitor_json_length(m); + } + return length; +} + static struct ovsdb_jsonrpc_monitor * ovsdb_jsonrpc_monitor_cast(struct ovsdb_replica *replica) { - assert(replica->class == &ovsdb_jsonrpc_replica_class); + ovs_assert(replica->class == &ovsdb_jsonrpc_replica_class); return CONTAINER_OF(replica, struct ovsdb_jsonrpc_monitor, replica); } @@ -1405,6 +1518,52 @@ ovsdb_jsonrpc_monitor_change_cb(const struct ovsdb_row *old, return true; } +/* Returns an overestimate of the number of bytes of JSON data required to + * report the current contents of the database over monitor 'm'. */ +static size_t +ovsdb_jsonrpc_monitor_json_length(const struct ovsdb_jsonrpc_monitor *m) +{ + const struct shash_node *node; + size_t length; + + /* Top-level overhead of monitor JSON. */ + length = 256; + + SHASH_FOR_EACH (node, &m->tables) { + const struct ovsdb_jsonrpc_monitor_table *mt = node->data; + const struct ovsdb_table *table = mt->table; + const struct ovsdb_row *row; + size_t i; + + /* Per-table JSON overhead: "":{...}. */ + length += strlen(table->schema->name) + 32; + + /* Per-row JSON overhead: ,"":{"old":{...},"new":{...}} */ + length += hmap_count(&table->rows) * (UUID_LEN + 32); + + /* Per-row, per-column JSON overhead: ,"": */ + for (i = 0; i < mt->n_columns; i++) { + const struct ovsdb_jsonrpc_monitor_column *c = &mt->columns[i]; + const struct ovsdb_column *column = c->column; + + length += hmap_count(&table->rows) * (8 + strlen(column->name)); + } + + /* Data. */ + HMAP_FOR_EACH (row, hmap_node, &table->rows) { + for (i = 0; i < mt->n_columns; i++) { + const struct ovsdb_jsonrpc_monitor_column *c = &mt->columns[i]; + const struct ovsdb_column *column = c->column; + + length += ovsdb_datum_json_length(&row->fields[column->index], + &column->type); + } + } + } + + return length; +} + static void ovsdb_jsonrpc_monitor_init_aux(struct ovsdb_jsonrpc_monitor_aux *aux, const struct ovsdb_jsonrpc_monitor *m,