summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ovsdb/SPECS124
-rw-r--r--ovsdb/execution.c33
-rw-r--r--ovsdb/jsonrpc-server.c178
-rw-r--r--ovsdb/jsonrpc-server.h5
-rw-r--r--ovsdb/ovsdb-server.c19
-rw-r--r--ovsdb/ovsdb-tool.c2
-rw-r--r--ovsdb/ovsdb.h4
-rw-r--r--ovsdb/server.c156
-rw-r--r--ovsdb/server.h46
-rw-r--r--ovsdb/trigger.c4
-rw-r--r--tests/test-ovsdb.c2
-rw-r--r--vswitchd/vswitch.ovsschema4
-rw-r--r--vswitchd/vswitch.xml11
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>