xref: /freebsd/crypto/krb5/src/ccapi/server/ccs_lock_state.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* ccapi/server/ccs_lock_state.c */
2 /*
3  * Copyright 2006, 2007 Massachusetts Institute of Technology.
4  * All Rights Reserved.
5  *
6  * Export of this software from the United States of America may
7  * require a specific license from the United States Government.
8  * It is the responsibility of any person or organization contemplating
9  * export to obtain such a license before exporting.
10  *
11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12  * distribute this software and its documentation for any purpose and
13  * without fee is hereby granted, provided that the above copyright
14  * notice appear in all copies and that both that copyright notice and
15  * this permission notice appear in supporting documentation, and that
16  * the name of M.I.T. not be used in advertising or publicity pertaining
17  * to distribution of the software without specific, written prior
18  * permission.  Furthermore if you modify this software you must label
19  * your software as modified software and not distribute it in such a
20  * fashion that it might be confused with the original M.I.T. software.
21  * M.I.T. makes no representations about the suitability of
22  * this software for any purpose.  It is provided "as is" without express
23  * or implied warranty.
24  */
25 
26 #include "ccs_common.h"
27 
28 struct ccs_lock_state_d {
29     cc_int32 invalid_object_err;
30     cc_int32 pending_lock_err;
31     cc_int32 no_lock_err;
32     ccs_lock_array_t locks;
33     cc_uint64 first_pending_lock_index;
34 };
35 
36 struct ccs_lock_state_d ccs_lock_state_initializer = { 1, 1, 1, NULL, 0 };
37 
38 /* ------------------------------------------------------------------------ */
39 
ccs_lock_state_new(ccs_lock_state_t * out_lock_state,cc_int32 in_invalid_object_err,cc_int32 in_pending_lock_err,cc_int32 in_no_lock_err)40 cc_int32 ccs_lock_state_new (ccs_lock_state_t *out_lock_state,
41                              cc_int32          in_invalid_object_err,
42                              cc_int32          in_pending_lock_err,
43                              cc_int32          in_no_lock_err)
44 {
45     cc_int32 err = ccNoError;
46     ccs_lock_state_t lock_state = NULL;
47 
48     if (!out_lock_state) { err = cci_check_error (ccErrBadParam); }
49 
50     if (!err) {
51         lock_state = malloc (sizeof (*lock_state));
52         if (lock_state) {
53             *lock_state = ccs_lock_state_initializer;
54         } else {
55             err = cci_check_error (ccErrNoMem);
56         }
57     }
58 
59     if (!err) {
60         err = ccs_lock_array_new (&lock_state->locks);
61     }
62 
63     if (!err) {
64         lock_state->invalid_object_err = in_invalid_object_err;
65         lock_state->pending_lock_err = in_pending_lock_err;
66         lock_state->no_lock_err = in_no_lock_err;
67 
68         *out_lock_state = lock_state;
69         lock_state = NULL;
70     }
71 
72     ccs_lock_state_release (lock_state);
73 
74     return cci_check_error (err);
75 }
76 
77 /* ------------------------------------------------------------------------ */
78 
ccs_lock_state_release(ccs_lock_state_t io_lock_state)79 cc_int32 ccs_lock_state_release (ccs_lock_state_t io_lock_state)
80 {
81     cc_int32 err = ccNoError;
82 
83     if (!err && io_lock_state) {
84         ccs_lock_array_release (io_lock_state->locks);
85         free (io_lock_state);
86     }
87 
88     return cci_check_error (err);
89 }
90 
91 #ifdef TARGET_OS_MAC
92 #pragma mark -
93 #endif
94 
95 /* ------------------------------------------------------------------------ */
96 
ccs_lock_status_add_pending_lock(ccs_lock_state_t io_lock_state,ccs_pipe_t in_client_pipe,ccs_pipe_t in_reply_pipe,cc_uint32 in_lock_type,cc_uint64 * out_lock_index)97 static cc_int32 ccs_lock_status_add_pending_lock (ccs_lock_state_t  io_lock_state,
98                                                   ccs_pipe_t        in_client_pipe,
99                                                   ccs_pipe_t        in_reply_pipe,
100                                                   cc_uint32         in_lock_type,
101                                                   cc_uint64        *out_lock_index)
102 {
103     cc_int32 err = ccNoError;
104     ccs_lock_t lock = NULL;
105 
106     if (!io_lock_state                  ) { err = cci_check_error (ccErrBadParam); }
107     if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); }
108     if (!ccs_pipe_valid (in_reply_pipe) ) { err = cci_check_error (ccErrBadParam); }
109 
110     if (!err) {
111         err = ccs_lock_new (&lock, in_lock_type,
112                             io_lock_state->invalid_object_err,
113                             in_client_pipe, in_reply_pipe,
114                             io_lock_state);
115     }
116 
117     if (!err) {
118         err = ccs_lock_array_insert (io_lock_state->locks, lock,
119                                      ccs_lock_array_count (io_lock_state->locks));
120         if (!err) { lock = NULL; /* take ownership */ }
121     }
122 
123     if (!err) {
124         *out_lock_index = ccs_lock_array_count (io_lock_state->locks) - 1;
125     }
126 
127     ccs_lock_release (lock);
128 
129     return cci_check_error (err);
130 }
131 
132 /* ------------------------------------------------------------------------ */
133 
ccs_lock_status_remove_lock(ccs_lock_state_t io_lock_state,cc_uint64 in_lock_index)134 static cc_int32 ccs_lock_status_remove_lock (ccs_lock_state_t io_lock_state,
135                                              cc_uint64        in_lock_index)
136 {
137     cc_int32 err = ccNoError;
138 
139     if (!io_lock_state) { err = cci_check_error (ccErrBadParam); }
140 
141     if (!err) {
142         err = ccs_lock_array_remove (io_lock_state->locks, in_lock_index);
143 
144         if (!err && in_lock_index < io_lock_state->first_pending_lock_index) {
145             io_lock_state->first_pending_lock_index--;
146         }
147     }
148 
149     return cci_check_error (err);
150 }
151 
152 /* ------------------------------------------------------------------------ */
153 
ccs_lock_status_grant_lock(ccs_lock_state_t io_lock_state,cc_uint64 in_pending_lock_index)154 static cc_int32 ccs_lock_status_grant_lock (ccs_lock_state_t io_lock_state,
155                                             cc_uint64        in_pending_lock_index)
156 {
157     cc_int32 err = ccNoError;
158     ccs_lock_t pending_lock = NULL;
159     cc_uint32 type = 0;
160 
161     if (!io_lock_state) { err = cci_check_error (ccErrBadParam); }
162 
163     if (!err) {
164         pending_lock = ccs_lock_array_object_at_index (io_lock_state->locks,
165                                                        in_pending_lock_index);
166         if (!pending_lock || in_pending_lock_index < io_lock_state->first_pending_lock_index) {
167             err = cci_check_error (ccErrBadParam);
168         }
169     }
170 
171     if (!err) {
172         err = ccs_lock_type (pending_lock, &type);
173     }
174 
175     if (!err && (type == cc_lock_upgrade || type == cc_lock_downgrade)) {
176         /* lock upgrades or downgrades.  Find the old lock and remove it. */
177         ccs_pipe_t pending_client_pipe = CCS_PIPE_NULL;
178 
179         err = ccs_lock_client_pipe (pending_lock, &pending_client_pipe);
180 
181         if (!err) {
182             cc_uint64 i;
183 
184             for (i = 0; !err && i < io_lock_state->first_pending_lock_index; i++) {
185                 ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i);
186                 cc_uint32 is_lock_for_client = 0;
187 
188                 err = ccs_lock_is_for_client_pipe (lock, pending_client_pipe, &is_lock_for_client);
189 
190                 if (!err && is_lock_for_client) {
191                     cci_debug_printf ("%s: Removing old lock %p at index %d to replace with pending lock %p.",
192                                       __FUNCTION__, lock, (int) i, pending_lock);
193                     err = ccs_lock_status_remove_lock (io_lock_state, i);
194                     if (!err) { i--; in_pending_lock_index--; /* We removed one so back up an index */ }
195                     break;
196                 }
197             }
198         }
199     }
200 
201     if (!err) {
202         cc_uint64 new_lock_index = 0;
203 
204         err = ccs_lock_array_move (io_lock_state->locks,
205                                    in_pending_lock_index,
206                                    io_lock_state->first_pending_lock_index,
207                                    &new_lock_index);
208         if (!err) { io_lock_state->first_pending_lock_index++; }
209     }
210 
211     if (!err) {
212         err = ccs_lock_grant_lock (pending_lock);
213     }
214 
215     return cci_check_error (err);
216 }
217 
218 #ifdef TARGET_OS_MAC
219 #pragma mark -
220 #endif
221 
222 /* ------------------------------------------------------------------------ */
223 
ccs_lock_state_check_pending_lock(ccs_lock_state_t io_lock_state,ccs_pipe_t in_pending_lock_client_pipe,cc_uint32 in_pending_lock_type,cc_uint32 * out_grant_lock)224 static cc_int32 ccs_lock_state_check_pending_lock (ccs_lock_state_t  io_lock_state,
225                                                    ccs_pipe_t        in_pending_lock_client_pipe,
226                                                    cc_uint32         in_pending_lock_type,
227                                                    cc_uint32        *out_grant_lock)
228 {
229     cc_int32 err = ccNoError;
230     cc_uint32 is_write_locked = 0;
231     cc_uint32 client_has_lock = 0;
232     cc_uint32 other_clients_have_locks = 0;
233     cc_uint32 client_lock_type = 0;
234     cc_uint64 client_lock_index = 0;
235     cc_uint32 grant_lock = 0;
236 
237     if (!io_lock_state                               ) { err = cci_check_error (ccErrBadParam); }
238     if (!ccs_pipe_valid (in_pending_lock_client_pipe)) { err = cci_check_error (ccErrBadParam); }
239     if (!out_grant_lock                              ) { err = cci_check_error (ccErrBadParam); }
240 
241     if (!err) {
242         cc_uint64 i;
243         cc_uint64 lock_count = io_lock_state->first_pending_lock_index;
244 
245         for (i = 0; !err && i < lock_count; i++) {
246             ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i);
247             cc_uint32 lock_type = 0;
248             cc_uint32 lock_is_for_client = 0;
249 
250             err = ccs_lock_type (lock, &lock_type);
251 
252             if (!err) {
253                 err = ccs_lock_is_for_client_pipe (lock, in_pending_lock_client_pipe,
254                                                    &lock_is_for_client);
255             }
256 
257             if (!err) {
258                 if (lock_type == cc_lock_write || lock_type == cc_lock_upgrade) {
259                     is_write_locked = 1;
260                 }
261 
262                 if (!lock_is_for_client) {
263                     other_clients_have_locks = 1;
264 
265                 } else if (!client_has_lock) { /* only record type of 1st lock */
266                     client_has_lock = 1;
267                     client_lock_type = lock_type;
268                     client_lock_index = i;
269                 }
270             }
271         }
272     }
273 
274     if (!err) {
275         cc_uint64 lock_count = io_lock_state->first_pending_lock_index;
276 
277         if (in_pending_lock_type == cc_lock_write) {
278             if (client_has_lock) {
279                 err = cci_check_error (ccErrBadLockType);
280             } else {
281                 grant_lock = (lock_count == 0);
282             }
283 
284         } else if (in_pending_lock_type == cc_lock_read) {
285             if (client_has_lock) {
286                 err = cci_check_error (ccErrBadLockType);
287             } else {
288                 grant_lock = !is_write_locked;
289             }
290 
291         } else if (in_pending_lock_type == cc_lock_upgrade) {
292             if (!client_has_lock || (client_lock_type != cc_lock_read &&
293                                      client_lock_type != cc_lock_downgrade)) {
294                 err = cci_check_error (ccErrBadLockType);
295             } else {
296                 /* don't grant if other clients have read locks */
297                 grant_lock = !other_clients_have_locks;
298             }
299 
300         } else if (in_pending_lock_type == cc_lock_downgrade) {
301             if (!client_has_lock || (client_lock_type != cc_lock_write &&
302                                      client_lock_type != cc_lock_upgrade)) {
303                 err = cci_check_error (ccErrBadLockType);
304             } else {
305                 /* downgrades can never block */
306                 grant_lock = 1;
307             }
308         } else {
309             err = cci_check_error (ccErrBadLockType);
310         }
311     }
312 
313     if (!err) {
314         *out_grant_lock = grant_lock;
315     }
316 
317     return cci_check_error (err);
318 }
319 
320 /* ------------------------------------------------------------------------ */
321 
ccs_lock_status_try_to_grant_pending_locks(ccs_lock_state_t io_lock_state)322 static cc_int32 ccs_lock_status_try_to_grant_pending_locks (ccs_lock_state_t io_lock_state)
323 {
324     cc_int32 err = ccNoError;
325     cc_uint32 done = 0;
326 
327     if (!io_lock_state) { err = cci_check_error (ccErrBadParam); }
328 
329     /* Look at the pending locks and see if we can grant them.
330      * Note that downgrade locks mean we must check all pending locks each pass
331      * since a downgrade lock might be last in the list. */
332 
333     while (!err && !done) {
334         cc_uint64 i;
335         cc_uint64 count = ccs_lock_array_count (io_lock_state->locks);
336         cc_uint32 granted_lock = 0;
337 
338         for (i = io_lock_state->first_pending_lock_index; !err && i < count; i++) {
339             ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i);
340             cc_uint32 lock_type = 0;
341             ccs_pipe_t client_pipe = CCS_PIPE_NULL;
342             cc_uint32 can_grant_lock_now = 0;
343 
344             err = ccs_lock_client_pipe (lock, &client_pipe);
345 
346             if (!err) {
347                 err = ccs_lock_type (lock, &lock_type);
348             }
349 
350             if (!err) {
351                 err = ccs_lock_state_check_pending_lock (io_lock_state, client_pipe,
352                                                          lock_type, &can_grant_lock_now);
353             }
354 
355             if (!err && can_grant_lock_now) {
356                 err = ccs_lock_status_grant_lock (io_lock_state, i);
357                 if (!err) { granted_lock = 1; }
358             }
359         }
360 
361         if (!err && !granted_lock) {
362             /* we walked over all the locks and couldn't grant any of them */
363             done = 1;
364         }
365     }
366 
367     return cci_check_error (err);
368 }
369 
370 #ifdef TARGET_OS_MAC
371 #pragma mark -
372 #endif
373 
374 /* ------------------------------------------------------------------------ */
375 
ccs_lock_state_add(ccs_lock_state_t io_lock_state,ccs_pipe_t in_client_pipe,ccs_pipe_t in_reply_pipe,cc_uint32 in_lock_type,cc_uint32 in_block,cc_uint32 * out_will_send_reply)376 cc_int32 ccs_lock_state_add (ccs_lock_state_t  io_lock_state,
377                              ccs_pipe_t        in_client_pipe,
378                              ccs_pipe_t        in_reply_pipe,
379                              cc_uint32         in_lock_type,
380                              cc_uint32         in_block,
381                              cc_uint32        *out_will_send_reply)
382 {
383     cc_int32 err = ccNoError;
384     cc_uint32 can_grant_lock_now = 0;
385 
386     if (!io_lock_state                  ) { err = cci_check_error (ccErrBadParam); }
387     if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); }
388     if (!ccs_pipe_valid (in_reply_pipe) ) { err = cci_check_error (ccErrBadParam); }
389     if (!out_will_send_reply            ) { err = cci_check_error (ccErrBadParam); }
390 
391     if (!err) {
392         /* Sanity check: if there are any pending locks for this client
393          * the client must have timed out waiting for our reply.  Remove any
394          * existing pending locks for the client. */
395         cc_uint64 i;
396 
397         for (i = io_lock_state->first_pending_lock_index; !err && i < ccs_lock_array_count (io_lock_state->locks); i++) {
398             ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i);
399             cc_uint32 has_pending_lock_for_client = 0;
400 
401             err = ccs_lock_is_for_client_pipe (lock, in_client_pipe, &has_pending_lock_for_client);
402 
403             if (!err && has_pending_lock_for_client) {
404                 cci_debug_printf ("WARNING %s: Removing unexpected pending lock %p at index %d.",
405                                   __FUNCTION__, lock, (int) i);
406                 err = ccs_lock_status_remove_lock (io_lock_state, i);
407                 if (!err) { i--;  /* We removed one so back up an index */ }
408             }
409         }
410     }
411 
412     if (!err) {
413         err = ccs_lock_state_check_pending_lock (io_lock_state, in_client_pipe,
414                                                  in_lock_type, &can_grant_lock_now);
415     }
416 
417     if (!err) {
418         if (!can_grant_lock_now && (in_block == cc_lock_noblock)) {
419             err = cci_check_error (io_lock_state->pending_lock_err);
420 
421         } else {
422             cc_uint64 new_lock_index = 0;
423 
424             err = ccs_lock_status_add_pending_lock (io_lock_state,
425                                                     in_client_pipe,
426                                                     in_reply_pipe,
427                                                     in_lock_type,
428                                                     &new_lock_index);
429 
430             if (!err && can_grant_lock_now) {
431                 err = ccs_lock_status_grant_lock (io_lock_state, new_lock_index);
432 
433                 if (!err && (in_lock_type == cc_lock_downgrade)) {
434                     /* downgrades can allow us to grant other locks */
435                     err = ccs_lock_status_try_to_grant_pending_locks (io_lock_state);
436                 }
437             }
438         }
439     }
440 
441     if (!err) {
442         /* ccs_lock_state_add sends its replies via callback so caller shouldn't */
443         *out_will_send_reply = 1;
444     }
445 
446     return cci_check_error (err);
447 }
448 
449 /* ------------------------------------------------------------------------ */
450 
ccs_lock_state_remove(ccs_lock_state_t io_lock_state,ccs_pipe_t in_client_pipe)451 cc_int32 ccs_lock_state_remove (ccs_lock_state_t io_lock_state,
452                                 ccs_pipe_t       in_client_pipe)
453 {
454     cc_int32 err = ccNoError;
455     cc_uint32 found_lock = 0;
456 
457     if (!io_lock_state                  ) { err = cci_check_error (ccErrBadParam); }
458     if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); }
459 
460     if (!err) {
461         cc_uint64 i;
462 
463         /* Remove all locks for this client.
464          * There should only be one so warn if there are multiple */
465         for (i = 0; !err && i < io_lock_state->first_pending_lock_index; i++) {
466             ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i);
467             cc_uint32 is_for_client = 0;
468 
469             err = ccs_lock_is_for_client_pipe (lock, in_client_pipe, &is_for_client);
470 
471             if (!err && is_for_client) {
472                 if (found_lock) {
473                     cci_debug_printf ("WARNING %s: Found multiple locks for client.",
474                                       __FUNCTION__);
475                 }
476 
477                 found_lock = 1;
478 
479                 cci_debug_printf ("%s: Removing lock %p at index %d.", __FUNCTION__, lock, (int) i);
480                 err = ccs_lock_status_remove_lock (io_lock_state, i);
481                 if (!err) { i--;  /* We removed one so back up an index */ }
482             }
483         }
484     }
485 
486     if (!err && !found_lock) {
487         err = cci_check_error (io_lock_state->no_lock_err);
488     }
489 
490     if (!err) {
491         err = ccs_lock_status_try_to_grant_pending_locks (io_lock_state);
492     }
493 
494     return cci_check_error (err);
495 }
496 
497 /* ------------------------------------------------------------------------ */
498 
ccs_lock_state_invalidate_lock(ccs_lock_state_t io_lock_state,ccs_lock_t in_lock)499 cc_int32 ccs_lock_state_invalidate_lock (ccs_lock_state_t io_lock_state,
500                                          ccs_lock_t       in_lock)
501 {
502     cc_int32 err = ccNoError;
503 
504     if (!io_lock_state) { err = ccErrBadParam; }
505 
506     if (!err) {
507         cc_uint64 i;
508         cc_uint64 count = ccs_lock_array_count (io_lock_state->locks);
509 
510         for (i = 0; !err && i < count; i++) {
511             ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i);
512 
513             if (lock == in_lock) {
514                 err = ccs_lock_status_remove_lock (io_lock_state, i);
515 
516                 if (!err) {
517                     err = ccs_lock_status_try_to_grant_pending_locks (io_lock_state);
518                     break;
519                 }
520             }
521         }
522     }
523 
524     return cci_check_error (err);
525 }
526