diff options
-rw-r--r-- | ovsdb/SPECS | 124 | ||||
-rw-r--r-- | ovsdb/execution.c | 33 | ||||
-rw-r--r-- | ovsdb/jsonrpc-server.c | 178 | ||||
-rw-r--r-- | ovsdb/jsonrpc-server.h | 5 | ||||
-rw-r--r-- | ovsdb/ovsdb-server.c | 19 | ||||
-rw-r--r-- | ovsdb/ovsdb-tool.c | 2 | ||||
-rw-r--r-- | ovsdb/ovsdb.h | 4 | ||||
-rw-r--r-- | ovsdb/server.c | 156 | ||||
-rw-r--r-- | ovsdb/server.h | 46 | ||||
-rw-r--r-- | ovsdb/trigger.c | 4 | ||||
-rw-r--r-- | tests/test-ovsdb.c | 2 | ||||
-rw-r--r-- | vswitchd/vswitch.ovsschema | 4 | ||||
-rw-r--r-- | vswitchd/vswitch.xml | 11 |
13 files changed, 576 insertions, 12 deletions
diff --git a/ovsdb/SPECS b/ovsdb/SPECS index 951c0976e..fdad2e377 100644 --- a/ovsdb/SPECS +++ b/ovsdb/SPECS @@ -627,6 +627,107 @@ Cancels the ongoing table monitor request, identified by the ongoing "monitor" request. No more "update" messages will be sent for this table monitor. +lock operations +............... + +Request object members: + + "method": "lock", "steal", or "unlock" required + "params": [<id>] required + "id": <nonnull-json-value> required + +Response object members: + + "result": {"locked": <boolean>} for "lock" + "result": {"locked": true} for "steal" + "result": {} for "unlock" + "error": null + "id": same "id" as request + +Performs an operation on a "lock" object. The database server +supports an arbitrary number of locks, each of which is identified by +a client-defined id (given in "params"). At any given time, each lock +may have at most one owner. + +The locking operation depends on "method": + + - "lock": The database will assign this client ownership of the + lock as soon as it becomes available. When multiple clients + request the same lock, they will receive it in first-come, first + served order. + + - "steal": The database immediately assigns this client ownership + of the lock. If there is an existing owner, it loses ownership. + + - "unlock": If the client owns the lock, releases it. If the + client is waiting to obtain the lock, cancels the request and + stops waiting. + + (Closing or otherwise disconnecting a database client connection + unlocks all of its locks.) + +For any given lock, the client must alternate "lock" or "steal" +operations with "unlock" operations. That is, if the previous +operation on a lock was "lock" or "steal", it must be followed by an +"unlock" operation, and vice versa. + +For a "lock" operation, the "locked" member in the response object is +true if the lock has already been acquired, false if another client +holds the lock and the client's request for it was queued. In the +latter case, the client will be notified later with a "locked" message +when acquisition succeeds. + +These requests complete and send a response quickly, without waiting. +The "locked" and "stolen" notifications (see below) report +asynchronous changes to ownership. + +The scope of a lock is a database server, not a database hosted by +that server. A naming convention, such as "<db-name>:<lock-name>", +can effectively limit the scope of a lock to a particular database. + +locked +...... + +Notification object members: + + "method": "locked" + "params": [<id>] + "id": null + +Notifies the client that a "lock" operation that it previously +requested has succeeded. The client now owns the lock named in +"params". + +The database server sends this notification after the reply to the +corresponding "lock" request (but only if the "locked" member of the +response was false), and before the reply to the client's subsequent +"unlock" request. + +stolen +...... + +Notification object members: + + "method": "locked" + "params": [<id>] + "id": null + +Notifies the client that owns a lock that another database client has +stolen ownership of the lock. The client no longer owns the lock +named in "params". The client must still issue an "unlock" request +before performing any subsequent "lock" or "steal" operation on the +lock. + +If the client originally obtained the lock through a "lock" request, +then it will automatically regain the lock later after the client that +stole it releases it. (The database server will send the client a +"locked" notification at that point to let it know.) + +If the client originally obtained the lock through a "steal" request, +the database server won't automatically reassign it ownership of the +lock when it later becomes available. To regain ownership, the client +must "unlock" and then "lock" or "steal" the lock again. + echo .... @@ -1192,3 +1293,26 @@ Semantics: Provides information to a database administrator on the purpose of a transaction. The OVSDB server, for example, adds comments in transactions that modify the database to the database journal. + +assert +...... + +Request object members: + + "op": "assert" required + "lock": <string> required + +Result object members: + + none + +Semantics: + + If the client does not own the lock named <string>, aborts the + transaction. + +Errors: + + "error": "not owner" + + The client does not own the named lock. diff --git a/ovsdb/execution.c b/ovsdb/execution.c index 416016fd9..9e3a8d07b 100644 --- a/ovsdb/execution.c +++ b/ovsdb/execution.c @@ -29,12 +29,14 @@ #include "ovsdb.h" #include "query.h" #include "row.h" +#include "server.h" #include "table.h" #include "timeval.h" #include "transaction.h" struct ovsdb_execution { struct ovsdb *db; + const struct ovsdb_session *session; struct ovsdb_txn *txn; struct ovsdb_symbol_table *symtab; bool durable; @@ -57,6 +59,7 @@ static ovsdb_operation_executor ovsdb_execute_wait; static ovsdb_operation_executor ovsdb_execute_commit; static ovsdb_operation_executor ovsdb_execute_abort; static ovsdb_operation_executor ovsdb_execute_comment; +static ovsdb_operation_executor ovsdb_execute_assert; static ovsdb_operation_executor * lookup_executor(const char *name) @@ -76,6 +79,7 @@ lookup_executor(const char *name) { "commit", ovsdb_execute_commit }, { "abort", ovsdb_execute_abort }, { "comment", ovsdb_execute_comment }, + { "assert", ovsdb_execute_assert }, }; size_t i; @@ -90,7 +94,8 @@ lookup_executor(const char *name) } struct json * -ovsdb_execute(struct ovsdb *db, const struct json *params, +ovsdb_execute(struct ovsdb *db, const struct ovsdb_session *session, + const struct json *params, long long int elapsed_msec, long long int *timeout_msec) { struct ovsdb_execution x; @@ -116,6 +121,7 @@ ovsdb_execute(struct ovsdb *db, const struct json *params, } x.db = db; + x.session = session; x.txn = ovsdb_txn_create(db); x.symtab = ovsdb_symbol_table_create(); x.durable = false; @@ -706,3 +712,28 @@ ovsdb_execute_comment(struct ovsdb_execution *x, struct ovsdb_parser *parser, return NULL; } + +static struct ovsdb_error * +ovsdb_execute_assert(struct ovsdb_execution *x, struct ovsdb_parser *parser, + struct json *result OVS_UNUSED) +{ + const struct json *lock_name; + + lock_name = ovsdb_parser_member(parser, "lock", OP_STRING); + if (!lock_name) { + return NULL; + } + + if (x->session) { + const struct ovsdb_lock_waiter *waiter; + + waiter = ovsdb_session_get_lock_waiter(x->session, + json_string(lock_name)); + if (waiter && ovsdb_lock_waiter_is_owner(waiter)) { + return NULL; + } + } + + return ovsdb_error("not owner", "Asserted lock %s not held.", + json_string(lock_name)); +} diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c index 11ec439d1..147dadc7e 100644 --- a/ovsdb/jsonrpc-server.c +++ b/ovsdb/jsonrpc-server.c @@ -58,6 +58,8 @@ static void ovsdb_jsonrpc_session_set_all_options( static bool ovsdb_jsonrpc_session_get_status( const struct ovsdb_jsonrpc_remote *, struct ovsdb_jsonrpc_remote_status *); +static void ovsdb_jsonrpc_session_unlock_all(struct ovsdb_jsonrpc_session *); +static void ovsdb_jsonrpc_session_unlock__(struct ovsdb_lock_waiter *); /* Triggers. */ static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *, @@ -220,6 +222,15 @@ ovsdb_jsonrpc_server_get_remote_status( return remote && ovsdb_jsonrpc_session_get_status(remote, status); } +void +ovsdb_jsonrpc_server_free_remote_status( + struct ovsdb_jsonrpc_remote_status *status) +{ + free(status->locks_held); + free(status->locks_waiting); + free(status->locks_lost); +} + /* Forces all of the JSON-RPC sessions managed by 'svr' to disconnect and * reconnect. */ void @@ -330,8 +341,10 @@ static void ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s) { ovsdb_jsonrpc_monitor_remove_all(s); + ovsdb_jsonrpc_session_unlock_all(s); 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); @@ -345,6 +358,7 @@ ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s) s->js_seqno = jsonrpc_session_get_seqno(s->js); ovsdb_jsonrpc_trigger_complete_all(s); ovsdb_jsonrpc_monitor_remove_all(s); + ovsdb_jsonrpc_session_unlock_all(s); } ovsdb_jsonrpc_trigger_complete_done(s); @@ -453,7 +467,9 @@ ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote, { const struct ovsdb_jsonrpc_session *s; const struct jsonrpc_session *js; + struct ovsdb_lock_waiter *waiter; struct reconnect_stats rstats; + struct ds locks_held, locks_waiting, locks_lost; if (list_is_empty(&remote->sessions)) { return false; @@ -471,6 +487,24 @@ ovsdb_jsonrpc_session_get_status(const struct ovsdb_jsonrpc_remote *remote, status->sec_since_disconnect = rstats.msec_since_disconnect == UINT_MAX ? UINT_MAX : rstats.msec_since_disconnect / 1000; + ds_init(&locks_held); + ds_init(&locks_waiting); + ds_init(&locks_lost); + HMAP_FOR_EACH (waiter, session_node, &s->up.waiters) { + struct ds *string; + + string = (ovsdb_lock_waiter_is_owner(waiter) ? &locks_held + : waiter->mode == OVSDB_LOCK_WAIT ? &locks_waiting + : &locks_lost); + if (string->length) { + ds_put_char(string, ' '); + } + ds_put_cstr(string, waiter->lock_name); + } + status->locks_held = ds_steal_cstr(&locks_held); + status->locks_waiting = ds_steal_cstr(&locks_waiting); + status->locks_lost = ds_steal_cstr(&locks_lost); + status->n_connections = list_size(&remote->sessions); return true; @@ -518,6 +552,144 @@ error: return reply; } +static struct ovsdb_error * +ovsdb_jsonrpc_session_parse_lock_name(const struct jsonrpc_msg *request, + const char **lock_namep) +{ + const struct json_array *params; + + params = json_array(request->params); + if (params->n != 1 || params->elems[0]->type != JSON_STRING || + !ovsdb_parser_is_id(json_string(params->elems[0]))) { + *lock_namep = NULL; + return ovsdb_syntax_error(request->params, NULL, + "%s request params must be <id>", + request->method); + } + + *lock_namep = json_string(params->elems[0]); + return NULL; +} + +static void +ovsdb_jsonrpc_session_notify(struct ovsdb_session *session, + const char *lock_name, + const char *method) +{ + struct ovsdb_jsonrpc_session *s; + struct json *params; + + s = CONTAINER_OF(session, struct ovsdb_jsonrpc_session, up); + params = json_array_create_1(json_string_create(lock_name)); + jsonrpc_session_send(s->js, jsonrpc_create_notify(method, params)); +} + +static struct jsonrpc_msg * +ovsdb_jsonrpc_session_lock(struct ovsdb_jsonrpc_session *s, + struct jsonrpc_msg *request, + enum ovsdb_lock_mode mode) +{ + struct ovsdb_lock_waiter *waiter; + struct jsonrpc_msg *reply; + struct ovsdb_error *error; + struct ovsdb_session *victim; + const char *lock_name; + struct json *result; + + error = ovsdb_jsonrpc_session_parse_lock_name(request, &lock_name); + if (error) { + goto error; + } + + /* Report error if this session has issued a "lock" or "steal" without a + * matching "unlock" for this lock. */ + waiter = ovsdb_session_get_lock_waiter(&s->up, lock_name); + if (waiter) { + error = ovsdb_syntax_error( + request->params, NULL, + "must issue \"unlock\" before new \"%s\"", request->method); + goto error; + } + + /* Get the lock, add us as a waiter. */ + waiter = ovsdb_server_lock(&s->remote->server->up, &s->up, lock_name, mode, + &victim); + if (victim) { + ovsdb_jsonrpc_session_notify(victim, lock_name, "stolen"); + } + + result = json_object_create(); + json_object_put(result, "locked", + json_boolean_create(ovsdb_lock_waiter_is_owner(waiter))); + + return jsonrpc_create_reply(result, request->id); + +error: + reply = jsonrpc_create_reply(ovsdb_error_to_json(error), request->id); + ovsdb_error_destroy(error); + return reply; +} + +static void +ovsdb_jsonrpc_session_unlock_all(struct ovsdb_jsonrpc_session *s) +{ + struct ovsdb_lock_waiter *waiter, *next; + + HMAP_FOR_EACH_SAFE (waiter, next, session_node, &s->up.waiters) { + ovsdb_jsonrpc_session_unlock__(waiter); + } +} + +static void +ovsdb_jsonrpc_session_unlock__(struct ovsdb_lock_waiter *waiter) +{ + struct ovsdb_lock *lock = waiter->lock; + + if (lock) { + struct ovsdb_session *new_owner = ovsdb_lock_waiter_remove(waiter); + if (new_owner) { + ovsdb_jsonrpc_session_notify(new_owner, lock->name, "locked"); + } else { + /* ovsdb_server_lock() might have freed 'lock'. */ + } + } + + ovsdb_lock_waiter_destroy(waiter); +} + +static struct jsonrpc_msg * +ovsdb_jsonrpc_session_unlock(struct ovsdb_jsonrpc_session *s, + struct jsonrpc_msg *request) +{ + struct ovsdb_lock_waiter *waiter; + struct jsonrpc_msg *reply; + struct ovsdb_error *error; + const char *lock_name; + + error = ovsdb_jsonrpc_session_parse_lock_name(request, &lock_name); + if (error) { + goto error; + } + + /* Report error if this session has not issued a "lock" or "steal" for this + * lock. */ + waiter = ovsdb_session_get_lock_waiter(&s->up, lock_name); + if (!waiter) { + error = ovsdb_syntax_error( + request->params, NULL, "\"unlock\" without \"lock\" or \"steal\""); + goto error; + } + + ovsdb_jsonrpc_session_unlock__(waiter); + + return jsonrpc_create_reply(json_object_create(), request->id); + +error: + reply = jsonrpc_create_reply(ovsdb_error_to_json(error), request->id); + ovsdb_error_destroy(error); + return reply; +} + static struct jsonrpc_msg * execute_transaction(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request) @@ -560,6 +732,12 @@ ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s, reply = jsonrpc_create_reply( json_array_create_1(json_string_create(get_db_name(s))), request->id); + } else if (!strcmp(request->method, "lock")) { + reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_WAIT); + } else if (!strcmp(request->method, "steal")) { + reply = ovsdb_jsonrpc_session_lock(s, request, OVSDB_LOCK_STEAL); + } else if (!strcmp(request->method, "unlock")) { + reply = ovsdb_jsonrpc_session_unlock(s, request); } else if (!strcmp(request->method, "echo")) { reply = jsonrpc_create_reply(json_clone(request->params), request->id); } else { diff --git a/ovsdb/jsonrpc-server.h b/ovsdb/jsonrpc-server.h index 16504483d..78ddb82dc 100644 --- a/ovsdb/jsonrpc-server.h +++ b/ovsdb/jsonrpc-server.h @@ -41,11 +41,16 @@ struct ovsdb_jsonrpc_remote_status { unsigned int sec_since_connect; unsigned int sec_since_disconnect; bool is_connected; + char *locks_held; + char *locks_waiting; + char *locks_lost; int n_connections; }; bool ovsdb_jsonrpc_server_get_remote_status( const struct ovsdb_jsonrpc_server *, const char *target, struct ovsdb_jsonrpc_remote_status *); +void ovsdb_jsonrpc_server_free_remote_status( + struct ovsdb_jsonrpc_remote_status *); void ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *); diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c index 1b8d0be09..2d332fec0 100644 --- a/ovsdb/ovsdb-server.c +++ b/ovsdb/ovsdb-server.c @@ -15,8 +15,7 @@ #include <config.h> -#include "ovsdb.h" - +#include <assert.h> #include <errno.h> #include <getopt.h> #include <signal.h> @@ -27,11 +26,13 @@ #include "daemon.h" #include "dirs.h" #include "file.h" +#include "hash.h" #include "json.h" #include "jsonrpc.h" #include "jsonrpc-server.h" #include "leak-checker.h" #include "list.h" +#include "ovsdb.h" #include "ovsdb-data.h" #include "ovsdb-types.h" #include "ovsdb-error.h" @@ -499,11 +500,25 @@ update_remote_row(const struct ovsdb_row *row, struct ovsdb_txn *txn, values[n++] = xstrdup(ovs_retval_to_string(status.last_error)); } + if (status.locks_held && status.locks_held[0]) { + keys[n] = xstrdup("locks_held"); + values[n++] = xstrdup(status.locks_held); + } + if (status.locks_waiting && status.locks_waiting[0]) { + keys[n] = xstrdup("locks_waiting"); + values[n++] = xstrdup(status.locks_waiting); + } + if (status.locks_lost && status.locks_lost[0]) { + keys[n] = xstrdup("locks_lost"); + values[n++] = xstrdup(status.locks_lost); + } if (status.n_connections > 1) { keys[n] = xstrdup("n_connections"); values[n++] = xasprintf("%d", status.n_connections); } write_string_string_column(rw_row, "status", keys, values, n); + + ovsdb_jsonrpc_server_free_remote_status(&status); } static void diff --git a/ovsdb/ovsdb-tool.c b/ovsdb/ovsdb-tool.c index 822eb6b2e..74dfa5a5b 100644 --- a/ovsdb/ovsdb-tool.c +++ b/ovsdb/ovsdb-tool.c @@ -311,7 +311,7 @@ transact(bool read_only, const char *db_file_name, const char *transaction) check_ovsdb_error(ovsdb_file_open(db_file_name, read_only, &db, NULL)); request = parse_json(transaction); - result = ovsdb_execute(db, request, 0, NULL); + result = ovsdb_execute(db, NULL, request, 0, NULL); json_destroy(request); print_and_free_json(result); diff --git a/ovsdb/ovsdb.h b/ovsdb/ovsdb.h index 834ff1a99..0d15ef2db 100644 --- a/ovsdb/ovsdb.h +++ b/ovsdb/ovsdb.h @@ -23,6 +23,7 @@ struct json; struct ovsdb_log; +struct ovsdb_session; struct ovsdb_txn; struct uuid; @@ -71,7 +72,8 @@ struct json *ovsdb_to_json(const struct ovsdb *); struct ovsdb_table *ovsdb_get_table(const struct ovsdb *, const char *); -struct json *ovsdb_execute(struct ovsdb *, const struct json *params, +struct json *ovsdb_execute(struct ovsdb *, const struct ovsdb_session *, + const struct json *params, long long int elapsed_msec, long long int *timeout_msec); diff --git a/ovsdb/server.c b/ovsdb/server.c index ad9454dfb..e3ba1498e 100644 --- a/ovsdb/server.c +++ b/ovsdb/server.c @@ -17,18 +17,100 @@ #include "server.h" +#include <assert.h> + +#include "hash.h" + /* Initializes 'session' as a session that operates on 'db'. */ void ovsdb_session_init(struct ovsdb_session *session, struct ovsdb *db) { session->db = db; list_init(&session->completions); + hmap_init(&session->waiters); } /* Destroys 'session'. */ void -ovsdb_session_destroy(struct ovsdb_session *session OVS_UNUSED) +ovsdb_session_destroy(struct ovsdb_session *session) +{ + assert(hmap_is_empty(&session->waiters)); + hmap_destroy(&session->waiters); +} + +/* Searches 'session' for an ovsdb_lock_waiter named 'lock_name' and returns + * it if it finds one, otherwise NULL. */ +struct ovsdb_lock_waiter * +ovsdb_session_get_lock_waiter(const struct ovsdb_session *session, + const char *lock_name) +{ + struct ovsdb_lock_waiter *waiter; + + HMAP_FOR_EACH_WITH_HASH (waiter, session_node, hash_string(lock_name, 0), + &session->waiters) { + if (!strcmp(lock_name, waiter->lock_name)) { + return waiter; + } + } + return NULL; +} + +/* Returns the waiter that owns 'lock'. + * + * A lock always has an owner, so this function will never return NULL. */ +struct ovsdb_lock_waiter * +ovsdb_lock_get_owner(const struct ovsdb_lock *lock) +{ + return CONTAINER_OF(list_front(&lock->waiters), + struct ovsdb_lock_waiter, lock_node); +} + +/* Removes 'waiter' from its lock's list. This means that, if 'waiter' was + * formerly the owner of its lock, then it no longer owns it. + * + * Returns the session that now owns 'waiter'. This is NULL if 'waiter' was + * the lock's owner and no other sessions were waiting for the lock. In this + * case, the lock has been destroyed, so the caller must be sure not to refer + * to it again. A nonnull return value reflects a change in the lock's + * ownership if and only if 'waiter' formerly owned the lock. */ +struct ovsdb_session * +ovsdb_lock_waiter_remove(struct ovsdb_lock_waiter *waiter) +{ + struct ovsdb_lock *lock = waiter->lock; + + list_remove(&waiter->lock_node); + waiter->lock = NULL; + + if (list_is_empty(&lock->waiters)) { + hmap_remove(&lock->server->locks, &lock->hmap_node); + free(lock->name); + free(lock); + return NULL; + } + + return ovsdb_lock_get_owner(lock)->session; +} + +/* Destroys 'waiter', which must have already been removed from its lock's + * waiting list with ovsdb_lock_waiter_remove(). + * + * Removing and destroying locks are decoupled because a lock initially created + * by the "steal" request, that is later stolen by another client, remains in + * the database session until the database client sends an "unlock" request. */ +void +ovsdb_lock_waiter_destroy(struct ovsdb_lock_waiter *waiter) +{ + assert(!waiter->lock); + hmap_remove(&waiter->session->waiters, &waiter->session_node); + free(waiter->lock_name); + free(waiter); +} + +/* Returns true if 'waiter' owns its associated lock. */ +bool +ovsdb_lock_waiter_is_owner(const struct ovsdb_lock_waiter *waiter) { + return waiter->lock && waiter == ovsdb_lock_get_owner(waiter->lock); } /* Initializes 'server' as a server that operates on 'db'. */ @@ -36,10 +118,80 @@ void ovsdb_server_init(struct ovsdb_server *server, struct ovsdb *db) { server->db = db; + hmap_init(&server->locks); } /* Destroys 'server'. */ void -ovsdb_server_destroy(struct ovsdb_server *server OVS_UNUSED) +ovsdb_server_destroy(struct ovsdb_server *server) +{ + hmap_destroy(&server->locks); +} + +static struct ovsdb_lock * +ovsdb_server_create_lock__(struct ovsdb_server *server, const char *lock_name, + uint32_t hash) +{ + struct ovsdb_lock *lock; + + HMAP_FOR_EACH_WITH_HASH (lock, hmap_node, hash, &server->locks) { + if (!strcmp(lock->name, lock_name)) { + return lock; + } + } + + lock = xzalloc(sizeof *lock); + lock->server = server; + lock->name = xstrdup(lock_name); + hmap_insert(&server->locks, &lock->hmap_node, hash); + list_init(&lock->waiters); + + return lock; +} + +/* Attempts to acquire the lock named 'lock_name' for 'session' within + * 'server'. Returns the new lock waiter. + * + * If 'mode' is OVSDB_LOCK_STEAL, then the new lock waiter is always the owner + * of the lock. '*victimp' receives the session of the previous owner or NULL + * if the lock was previously unowned. (If the victim itself originally + * obtained the lock through a "steal" operation, then this function also + * removes the victim from the lock's waiting list.) + * + * If 'mode' is OVSDB_LOCK_WAIT, then the new lock waiter is the owner of the + * lock only if this lock had no existing owner. '*victimp' is set to NULL. */ +struct ovsdb_lock_waiter * +ovsdb_server_lock(struct ovsdb_server *server, + struct ovsdb_session *session, + const char *lock_name, + enum ovsdb_lock_mode mode, + struct ovsdb_session **victimp) { + uint32_t hash = hash_string(lock_name, 0); + struct ovsdb_lock_waiter *waiter, *victim; + struct ovsdb_lock *lock; + + lock = ovsdb_server_create_lock__(server, lock_name, hash); + victim = (mode == OVSDB_LOCK_STEAL && !list_is_empty(&lock->waiters) + ? ovsdb_lock_get_owner(lock) + : NULL); + + waiter = xmalloc(sizeof *waiter); + waiter->mode = mode; + waiter->lock_name = xstrdup(lock_name); + waiter->lock = lock; + if (mode == OVSDB_LOCK_STEAL) { + list_push_front(&lock->waiters, &waiter->lock_node); + } else { + list_push_back(&lock->waiters, &waiter->lock_node); + } + waiter->session = session; + hmap_insert(&waiter->session->waiters, &waiter->session_node, hash); + + if (victim && victim->mode == OVSDB_LOCK_STEAL) { + ovsdb_lock_waiter_remove(victim); + } + *victimp = victim ? victim->session : NULL; + + return waiter; } diff --git a/ovsdb/server.h b/ovsdb/server.h index ce19b8d09..a9285f79e 100644 --- a/ovsdb/server.h +++ b/ovsdb/server.h @@ -25,19 +25,65 @@ struct ovsdb_session { struct ovsdb *db; struct list completions; /* Completed triggers. */ + struct hmap waiters; /* "ovsdb_lock_waiter *"s by lock name. */ }; void ovsdb_session_init(struct ovsdb_session *, struct ovsdb *); void ovsdb_session_destroy(struct ovsdb_session *); +struct ovsdb_lock_waiter *ovsdb_session_get_lock_waiter( + const struct ovsdb_session *, const char *lock_name); + +/* A database lock. + * + * A lock always has one or more "lock waiters" kept on a list. The waiter at + * the head of the list owns the lock. */ +struct ovsdb_lock { + struct ovsdb_server *server; /* The containing server. */ + char *name; /* Unique name. */ + struct hmap_node hmap_node; /* In ovsdb_server's "locks" hmap. */ + struct list waiters; /* Contains "struct ovsdb_lock_waiter"s. */ +}; + +struct ovsdb_lock_waiter *ovsdb_lock_get_owner(const struct ovsdb_lock *); + +/* How to obtain a lock. */ +enum ovsdb_lock_mode { + OVSDB_LOCK_WAIT, /* By waiting for it to become available. */ + OVSDB_LOCK_STEAL /* By stealing it from the owner. */ +}; + +/* A session's request for a database lock. */ +struct ovsdb_lock_waiter { + enum ovsdb_lock_mode mode; + char *lock_name; + + struct ovsdb_lock *lock; /* The lock being waited for. */ + struct list lock_node; /* In ->lock->waiters's list. */ + + struct ovsdb_session *session; + struct hmap_node session_node; /* In ->session->locks's hmap. */ +}; + +struct ovsdb_session *ovsdb_lock_waiter_remove(struct ovsdb_lock_waiter *); +void ovsdb_lock_waiter_destroy(struct ovsdb_lock_waiter *); +bool ovsdb_lock_waiter_is_owner(const struct ovsdb_lock_waiter *); + /* Abstract representation of an OVSDB server not tied to any particular * network protocol. Protocol implementations (e.g. jsonrpc-server.c) embed * this in a larger data structure. */ struct ovsdb_server { struct ovsdb *db; + struct hmap locks; /* Contains "struct ovsdb_lock"s indexed by name. */ }; void ovsdb_server_init(struct ovsdb_server *, struct ovsdb *); void ovsdb_server_destroy(struct ovsdb_server *); +struct ovsdb_lock_waiter *ovsdb_server_lock(struct ovsdb_server *, + struct ovsdb_session *, + const char *lock_name, + enum ovsdb_lock_mode, + struct ovsdb_session **victimp); + #endif /* ovsdb/server.h */ diff --git a/ovsdb/trigger.c b/ovsdb/trigger.c index b2eb0116f..1322a2fd4 100644 --- a/ovsdb/trigger.c +++ b/ovsdb/trigger.c @@ -110,8 +110,8 @@ ovsdb_trigger_wait(struct ovsdb *db, long long int now) static bool ovsdb_trigger_try(struct ovsdb_trigger *t, long long int now) { - t->result = ovsdb_execute(t->session->db, t->request, - now - t->created, &t->timeout_msec); + t->result = ovsdb_execute(t->session->db, t->session, + t->request, now - t->created, &t->timeout_msec); if (t->result) { ovsdb_trigger_complete(t); return true; diff --git a/tests/test-ovsdb.c b/tests/test-ovsdb.c index e151dd8bc..1cdf69ae8 100644 --- a/tests/test-ovsdb.c +++ b/tests/test-ovsdb.c @@ -1258,7 +1258,7 @@ do_execute(int argc OVS_UNUSED, char *argv[]) char *s; params = parse_json(argv[i]); - result = ovsdb_execute(db, params, 0, NULL); + result = ovsdb_execute(db, NULL, params, 0, NULL); s = json_to_string(result, JSSF_SORT); printf("%s\n", s); free(s); diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema index 315affcb8..ca61a2cd6 100644 --- a/vswitchd/vswitch.ovsschema +++ b/vswitchd/vswitch.ovsschema @@ -1,6 +1,6 @@ {"name": "Open_vSwitch", - "version": "5.1.0", - "cksum": "154459795 14545", + "version": "5.2.0", + "cksum": "434778864 14545", "tables": { "Open_vSwitch": { "columns": { diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 5b216a556..e72401fdc 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -2192,6 +2192,17 @@ disconnected.</dd> </dl> <dl> + <dt><code>locks_held</code></dt> + <dt><code>locks_waiting</code></dt> + <dt><code>locks_lost</code></dt> + <dd> + Space-separated lists of the names of OVSDB locks that the + connection holds, is currently waiting to acquire, or has had + stolen by another OVSDB client, respectively. Key-value pairs for + lists that would be empty are omitted. + </dd> + </dl> + <dl> <dt><code>n_connections</code></dt> <dd> <p> |