Branch data Line data Source code
1 : : /* Copyright (c) 2011, 2012 Nicira, Inc.
2 : : *
3 : : * Licensed under the Apache License, Version 2.0 (the "License");
4 : : * you may not use this file except in compliance with the License.
5 : : * You may obtain a copy of the License at:
6 : : *
7 : : * http://www.apache.org/licenses/LICENSE-2.0
8 : : *
9 : : * Unless required by applicable law or agreed to in writing, software
10 : : * distributed under the License is distributed on an "AS IS" BASIS,
11 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : * See the License for the specific language governing permissions and
13 : : * limitations under the License.
14 : : */
15 : :
16 : : #include <config.h>
17 : :
18 : : #include "server.h"
19 : :
20 : : #include "hash.h"
21 : : #include "ovsdb.h"
22 : :
23 : : /* Initializes 'session' as a session within 'server'. */
24 : : void
25 : 8192 : ovsdb_session_init(struct ovsdb_session *session, struct ovsdb_server *server)
26 : : {
27 : 8192 : session->server = server;
28 : 8192 : ovs_list_init(&session->completions);
29 : 8192 : hmap_init(&session->waiters);
30 : 8192 : }
31 : :
32 : : /* Destroys 'session'. */
33 : : void
34 : 8187 : ovsdb_session_destroy(struct ovsdb_session *session)
35 : : {
36 [ - + ]: 8187 : ovs_assert(hmap_is_empty(&session->waiters));
37 : 8187 : hmap_destroy(&session->waiters);
38 : 8187 : }
39 : :
40 : : /* Searches 'session' for an ovsdb_lock_waiter named 'lock_name' and returns
41 : : * it if it finds one, otherwise NULL. */
42 : : struct ovsdb_lock_waiter *
43 : 6134 : ovsdb_session_get_lock_waiter(const struct ovsdb_session *session,
44 : : const char *lock_name)
45 : : {
46 : : struct ovsdb_lock_waiter *waiter;
47 : :
48 [ + + ][ - + ]: 6134 : HMAP_FOR_EACH_WITH_HASH (waiter, session_node, hash_string(lock_name, 0),
49 : : &session->waiters) {
50 [ + - ]: 5498 : if (!strcmp(lock_name, waiter->lock_name)) {
51 : 5498 : return waiter;
52 : : }
53 : : }
54 : 636 : return NULL;
55 : : }
56 : :
57 : : /* Returns the waiter that owns 'lock'.
58 : : *
59 : : * A lock always has an owner, so this function will never return NULL. */
60 : : struct ovsdb_lock_waiter *
61 : 6136 : ovsdb_lock_get_owner(const struct ovsdb_lock *lock)
62 : : {
63 : 6136 : return CONTAINER_OF(ovs_list_front(&lock->waiters),
64 : : struct ovsdb_lock_waiter, lock_node);
65 : : }
66 : :
67 : : /* Removes 'waiter' from its lock's list. This means that, if 'waiter' was
68 : : * formerly the owner of its lock, then it no longer owns it.
69 : : *
70 : : * Returns the session that now owns 'waiter'. This is NULL if 'waiter' was
71 : : * the lock's owner and no other sessions were waiting for the lock. In this
72 : : * case, the lock has been destroyed, so the caller must be sure not to refer
73 : : * to it again. A nonnull return value reflects a change in the lock's
74 : : * ownership if and only if 'waiter' formerly owned the lock. */
75 : : struct ovsdb_session *
76 : 636 : ovsdb_lock_waiter_remove(struct ovsdb_lock_waiter *waiter)
77 : : {
78 : 636 : struct ovsdb_lock *lock = waiter->lock;
79 : :
80 : 636 : ovs_list_remove(&waiter->lock_node);
81 : 636 : waiter->lock = NULL;
82 : :
83 [ + + ]: 636 : if (ovs_list_is_empty(&lock->waiters)) {
84 : 633 : hmap_remove(&lock->server->locks, &lock->hmap_node);
85 : 633 : free(lock->name);
86 : 633 : free(lock);
87 : 633 : return NULL;
88 : : }
89 : :
90 : 3 : return ovsdb_lock_get_owner(lock)->session;
91 : : }
92 : :
93 : : /* Destroys 'waiter', which must have already been removed from its lock's
94 : : * waiting list with ovsdb_lock_waiter_remove().
95 : : *
96 : : * Removing and destroying locks are decoupled because a lock initially created
97 : : * by the "steal" request, that is later stolen by another client, remains in
98 : : * the database session until the database client sends an "unlock" request. */
99 : : void
100 : 636 : ovsdb_lock_waiter_destroy(struct ovsdb_lock_waiter *waiter)
101 : : {
102 [ - + ]: 636 : ovs_assert(!waiter->lock);
103 : 636 : hmap_remove(&waiter->session->waiters, &waiter->session_node);
104 : 636 : free(waiter->lock_name);
105 : 636 : free(waiter);
106 : 636 : }
107 : :
108 : : /* Returns true if 'waiter' owns its associated lock. */
109 : : bool
110 : 6132 : ovsdb_lock_waiter_is_owner(const struct ovsdb_lock_waiter *waiter)
111 : : {
112 [ + - ][ + + ]: 6132 : return waiter->lock && waiter == ovsdb_lock_get_owner(waiter->lock);
113 : : }
114 : :
115 : : /* Initializes 'server'.
116 : : *
117 : : * The caller must call ovsdb_server_add_db() for each database to which
118 : : * 'server' should provide access. */
119 : : void
120 : 1265 : ovsdb_server_init(struct ovsdb_server *server)
121 : : {
122 : 1265 : shash_init(&server->dbs);
123 : 1265 : hmap_init(&server->locks);
124 : 1265 : }
125 : :
126 : : /* Adds 'db' to the set of databases served out by 'server'. Returns true if
127 : : * successful, false if 'db''s name is the same as some database already in
128 : : * 'server'. */
129 : : bool
130 : 1276 : ovsdb_server_add_db(struct ovsdb_server *server, struct ovsdb *db)
131 : : {
132 : 1276 : return shash_add_once(&server->dbs, db->schema->name, db);
133 : : }
134 : :
135 : : /* Removes 'db' from the set of databases served out by 'server'. Returns
136 : : * true if successful, false if there is no db associated with
137 : : * db->schema->name. */
138 : : bool
139 : 2 : ovsdb_server_remove_db(struct ovsdb_server *server, struct ovsdb *db)
140 : : {
141 : 2 : void *data = shash_find_and_delete(&server->dbs, db->schema->name);
142 [ + - ]: 2 : if (data) {
143 : 2 : return true;
144 : : }
145 : 0 : return false;
146 : : }
147 : :
148 : : /* Destroys 'server'. */
149 : : void
150 : 1263 : ovsdb_server_destroy(struct ovsdb_server *server)
151 : : {
152 : 1263 : shash_destroy(&server->dbs);
153 : 1263 : hmap_destroy(&server->locks);
154 : 1263 : }
155 : :
156 : : static struct ovsdb_lock *
157 : 636 : ovsdb_server_create_lock__(struct ovsdb_server *server, const char *lock_name,
158 : : uint32_t hash)
159 : : {
160 : : struct ovsdb_lock *lock;
161 : :
162 [ + + ][ - + ]: 636 : HMAP_FOR_EACH_WITH_HASH (lock, hmap_node, hash, &server->locks) {
163 [ + - ]: 3 : if (!strcmp(lock->name, lock_name)) {
164 : 3 : return lock;
165 : : }
166 : : }
167 : :
168 : 633 : lock = xzalloc(sizeof *lock);
169 : 633 : lock->server = server;
170 : 633 : lock->name = xstrdup(lock_name);
171 : 633 : hmap_insert(&server->locks, &lock->hmap_node, hash);
172 : 633 : ovs_list_init(&lock->waiters);
173 : :
174 : 633 : return lock;
175 : : }
176 : :
177 : : /* Attempts to acquire the lock named 'lock_name' for 'session' within
178 : : * 'server'. Returns the new lock waiter.
179 : : *
180 : : * If 'mode' is OVSDB_LOCK_STEAL, then the new lock waiter is always the owner
181 : : * of the lock. '*victimp' receives the session of the previous owner or NULL
182 : : * if the lock was previously unowned. (If the victim itself originally
183 : : * obtained the lock through a "steal" operation, then this function also
184 : : * removes the victim from the lock's waiting list.)
185 : : *
186 : : * If 'mode' is OVSDB_LOCK_WAIT, then the new lock waiter is the owner of the
187 : : * lock only if this lock had no existing owner. '*victimp' is set to NULL. */
188 : : struct ovsdb_lock_waiter *
189 : 636 : ovsdb_server_lock(struct ovsdb_server *server,
190 : : struct ovsdb_session *session,
191 : : const char *lock_name,
192 : : enum ovsdb_lock_mode mode,
193 : : struct ovsdb_session **victimp)
194 : : {
195 : 636 : uint32_t hash = hash_string(lock_name, 0);
196 : : struct ovsdb_lock_waiter *waiter, *victim;
197 : : struct ovsdb_lock *lock;
198 : :
199 : 636 : lock = ovsdb_server_create_lock__(server, lock_name, hash);
200 [ + - ]: 1 : victim = (mode == OVSDB_LOCK_STEAL && !ovs_list_is_empty(&lock->waiters)
201 : : ? ovsdb_lock_get_owner(lock)
202 [ + + ]: 637 : : NULL);
203 : :
204 : 636 : waiter = xmalloc(sizeof *waiter);
205 : 636 : waiter->mode = mode;
206 : 636 : waiter->lock_name = xstrdup(lock_name);
207 : 636 : waiter->lock = lock;
208 [ + + ]: 636 : if (mode == OVSDB_LOCK_STEAL) {
209 : 1 : ovs_list_push_front(&lock->waiters, &waiter->lock_node);
210 : : } else {
211 : 635 : ovs_list_push_back(&lock->waiters, &waiter->lock_node);
212 : : }
213 : 636 : waiter->session = session;
214 : 636 : hmap_insert(&waiter->session->waiters, &waiter->session_node, hash);
215 : :
216 [ + + ][ - + ]: 636 : if (victim && victim->mode == OVSDB_LOCK_STEAL) {
217 : 0 : ovsdb_lock_waiter_remove(victim);
218 : : }
219 [ + + ]: 636 : *victimp = victim ? victim->session : NULL;
220 : :
221 : 636 : return waiter;
222 : : }
|