1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright 2023 Red Hat
4 */
5
6 #include "index-session.h"
7
8 #include <linux/atomic.h>
9
10 #include "logger.h"
11 #include "memory-alloc.h"
12 #include "time-utils.h"
13
14 #include "funnel-requestqueue.h"
15 #include "index.h"
16 #include "index-layout.h"
17
18 /*
19 * The index session contains a lock (the request_mutex) which ensures that only one thread can
20 * change the state of its index at a time. The state field indicates the current state of the
21 * index through a set of descriptive flags. The request_mutex must be notified whenever a
22 * non-transient state flag is cleared. The request_mutex is also used to count the number of
23 * requests currently in progress so that they can be drained when suspending or closing the index.
24 *
25 * If the index session is suspended shortly after opening an index, it may have to suspend during
26 * a rebuild. Depending on the size of the index, a rebuild may take a significant amount of time,
27 * so UDS allows the rebuild to be paused in order to suspend the session in a timely manner. When
28 * the index session is resumed, the rebuild can continue from where it left off. If the index
29 * session is shut down with a suspended rebuild, the rebuild progress is abandoned and the rebuild
30 * will start from the beginning the next time the index is loaded. The mutex and status fields in
31 * the index_load_context are used to record the state of any interrupted rebuild.
32 */
33
34 enum index_session_flag_bit {
35 IS_FLAG_BIT_START = 8,
36 /* The session has started loading an index but not completed it. */
37 IS_FLAG_BIT_LOADING = IS_FLAG_BIT_START,
38 /* The session has loaded an index, which can handle requests. */
39 IS_FLAG_BIT_LOADED,
40 /* The session's index has been permanently disabled. */
41 IS_FLAG_BIT_DISABLED,
42 /* The session's index is suspended. */
43 IS_FLAG_BIT_SUSPENDED,
44 /* The session is handling some index state change. */
45 IS_FLAG_BIT_WAITING,
46 /* The session's index is closing and draining requests. */
47 IS_FLAG_BIT_CLOSING,
48 /* The session is being destroyed and is draining requests. */
49 IS_FLAG_BIT_DESTROYING,
50 };
51
52 enum index_session_flag {
53 IS_FLAG_LOADED = (1 << IS_FLAG_BIT_LOADED),
54 IS_FLAG_LOADING = (1 << IS_FLAG_BIT_LOADING),
55 IS_FLAG_DISABLED = (1 << IS_FLAG_BIT_DISABLED),
56 IS_FLAG_SUSPENDED = (1 << IS_FLAG_BIT_SUSPENDED),
57 IS_FLAG_WAITING = (1 << IS_FLAG_BIT_WAITING),
58 IS_FLAG_CLOSING = (1 << IS_FLAG_BIT_CLOSING),
59 IS_FLAG_DESTROYING = (1 << IS_FLAG_BIT_DESTROYING),
60 };
61
62 /* Release a reference to an index session. */
release_index_session(struct uds_index_session * index_session)63 static void release_index_session(struct uds_index_session *index_session)
64 {
65 mutex_lock(&index_session->request_mutex);
66 if (--index_session->request_count == 0)
67 uds_broadcast_cond(&index_session->request_cond);
68 mutex_unlock(&index_session->request_mutex);
69 }
70
71 /*
72 * Acquire a reference to the index session for an asynchronous index request. The reference must
73 * eventually be released with a corresponding call to release_index_session().
74 */
get_index_session(struct uds_index_session * index_session)75 static int get_index_session(struct uds_index_session *index_session)
76 {
77 unsigned int state;
78 int result = UDS_SUCCESS;
79
80 mutex_lock(&index_session->request_mutex);
81 index_session->request_count++;
82 state = index_session->state;
83 mutex_unlock(&index_session->request_mutex);
84
85 if (state == IS_FLAG_LOADED) {
86 return UDS_SUCCESS;
87 } else if (state & IS_FLAG_DISABLED) {
88 result = UDS_DISABLED;
89 } else if ((state & IS_FLAG_LOADING) ||
90 (state & IS_FLAG_SUSPENDED) ||
91 (state & IS_FLAG_WAITING)) {
92 result = -EBUSY;
93 } else {
94 result = UDS_NO_INDEX;
95 }
96
97 release_index_session(index_session);
98 return result;
99 }
100
uds_launch_request(struct uds_request * request)101 int uds_launch_request(struct uds_request *request)
102 {
103 size_t internal_size;
104 int result;
105
106 if (request->callback == NULL) {
107 vdo_log_error("missing required callback");
108 return -EINVAL;
109 }
110
111 switch (request->type) {
112 case UDS_DELETE:
113 case UDS_POST:
114 case UDS_QUERY:
115 case UDS_QUERY_NO_UPDATE:
116 case UDS_UPDATE:
117 break;
118 default:
119 vdo_log_error("received invalid callback type");
120 return -EINVAL;
121 }
122
123 /* Reset all internal fields before processing. */
124 internal_size =
125 sizeof(struct uds_request) - offsetof(struct uds_request, zone_number);
126 // FIXME should be using struct_group for this instead
127 memset((char *) request + sizeof(*request) - internal_size, 0, internal_size);
128
129 result = get_index_session(request->session);
130 if (result != UDS_SUCCESS)
131 return result;
132
133 request->found = false;
134 request->unbatched = false;
135 request->index = request->session->index;
136
137 uds_enqueue_request(request, STAGE_TRIAGE);
138 return UDS_SUCCESS;
139 }
140
enter_callback_stage(struct uds_request * request)141 static void enter_callback_stage(struct uds_request *request)
142 {
143 if (request->status != UDS_SUCCESS) {
144 /* All request errors are considered unrecoverable */
145 mutex_lock(&request->session->request_mutex);
146 request->session->state |= IS_FLAG_DISABLED;
147 mutex_unlock(&request->session->request_mutex);
148 }
149
150 uds_request_queue_enqueue(request->session->callback_queue, request);
151 }
152
count_once(u64 * count_ptr)153 static inline void count_once(u64 *count_ptr)
154 {
155 WRITE_ONCE(*count_ptr, READ_ONCE(*count_ptr) + 1);
156 }
157
update_session_stats(struct uds_request * request)158 static void update_session_stats(struct uds_request *request)
159 {
160 struct session_stats *session_stats = &request->session->stats;
161
162 count_once(&session_stats->requests);
163
164 switch (request->type) {
165 case UDS_POST:
166 if (request->found)
167 count_once(&session_stats->posts_found);
168 else
169 count_once(&session_stats->posts_not_found);
170
171 if (request->location == UDS_LOCATION_IN_OPEN_CHAPTER)
172 count_once(&session_stats->posts_found_open_chapter);
173 else if (request->location == UDS_LOCATION_IN_DENSE)
174 count_once(&session_stats->posts_found_dense);
175 else if (request->location == UDS_LOCATION_IN_SPARSE)
176 count_once(&session_stats->posts_found_sparse);
177 break;
178
179 case UDS_UPDATE:
180 if (request->found)
181 count_once(&session_stats->updates_found);
182 else
183 count_once(&session_stats->updates_not_found);
184 break;
185
186 case UDS_DELETE:
187 if (request->found)
188 count_once(&session_stats->deletions_found);
189 else
190 count_once(&session_stats->deletions_not_found);
191 break;
192
193 case UDS_QUERY:
194 case UDS_QUERY_NO_UPDATE:
195 if (request->found)
196 count_once(&session_stats->queries_found);
197 else
198 count_once(&session_stats->queries_not_found);
199 break;
200
201 default:
202 request->status = VDO_ASSERT(false, "unknown request type: %d",
203 request->type);
204 }
205 }
206
handle_callbacks(struct uds_request * request)207 static void handle_callbacks(struct uds_request *request)
208 {
209 struct uds_index_session *index_session = request->session;
210
211 if (request->status == UDS_SUCCESS)
212 update_session_stats(request);
213
214 request->status = uds_status_to_errno(request->status);
215 request->callback(request);
216 release_index_session(index_session);
217 }
218
make_empty_index_session(struct uds_index_session ** index_session_ptr)219 static int __must_check make_empty_index_session(struct uds_index_session **index_session_ptr)
220 {
221 int result;
222 struct uds_index_session *session;
223
224 result = vdo_allocate(1, struct uds_index_session, __func__, &session);
225 if (result != VDO_SUCCESS)
226 return result;
227
228 mutex_init(&session->request_mutex);
229 uds_init_cond(&session->request_cond);
230 mutex_init(&session->load_context.mutex);
231 uds_init_cond(&session->load_context.cond);
232
233 result = uds_make_request_queue("callbackW", &handle_callbacks,
234 &session->callback_queue);
235 if (result != UDS_SUCCESS) {
236 vdo_free(session);
237 return result;
238 }
239
240 *index_session_ptr = session;
241 return UDS_SUCCESS;
242 }
243
uds_create_index_session(struct uds_index_session ** session)244 int uds_create_index_session(struct uds_index_session **session)
245 {
246 if (session == NULL) {
247 vdo_log_error("missing session pointer");
248 return -EINVAL;
249 }
250
251 return uds_status_to_errno(make_empty_index_session(session));
252 }
253
start_loading_index_session(struct uds_index_session * index_session)254 static int __must_check start_loading_index_session(struct uds_index_session *index_session)
255 {
256 int result;
257
258 mutex_lock(&index_session->request_mutex);
259 if (index_session->state & IS_FLAG_SUSPENDED) {
260 vdo_log_info("Index session is suspended");
261 result = -EBUSY;
262 } else if (index_session->state != 0) {
263 vdo_log_info("Index is already loaded");
264 result = -EBUSY;
265 } else {
266 index_session->state |= IS_FLAG_LOADING;
267 result = UDS_SUCCESS;
268 }
269 mutex_unlock(&index_session->request_mutex);
270 return result;
271 }
272
finish_loading_index_session(struct uds_index_session * index_session,int result)273 static void finish_loading_index_session(struct uds_index_session *index_session,
274 int result)
275 {
276 mutex_lock(&index_session->request_mutex);
277 index_session->state &= ~IS_FLAG_LOADING;
278 if (result == UDS_SUCCESS)
279 index_session->state |= IS_FLAG_LOADED;
280
281 uds_broadcast_cond(&index_session->request_cond);
282 mutex_unlock(&index_session->request_mutex);
283 }
284
initialize_index_session(struct uds_index_session * index_session,enum uds_open_index_type open_type)285 static int initialize_index_session(struct uds_index_session *index_session,
286 enum uds_open_index_type open_type)
287 {
288 int result;
289 struct uds_configuration *config;
290
291 result = uds_make_configuration(&index_session->parameters, &config);
292 if (result != UDS_SUCCESS) {
293 vdo_log_error_strerror(result, "Failed to allocate config");
294 return result;
295 }
296
297 memset(&index_session->stats, 0, sizeof(index_session->stats));
298 result = uds_make_index(config, open_type, &index_session->load_context,
299 enter_callback_stage, &index_session->index);
300 if (result != UDS_SUCCESS)
301 vdo_log_error_strerror(result, "Failed to make index");
302 else
303 uds_log_configuration(config);
304
305 uds_free_configuration(config);
306 return result;
307 }
308
get_open_type_string(enum uds_open_index_type open_type)309 static const char *get_open_type_string(enum uds_open_index_type open_type)
310 {
311 switch (open_type) {
312 case UDS_CREATE:
313 return "creating index";
314 case UDS_LOAD:
315 return "loading or rebuilding index";
316 case UDS_NO_REBUILD:
317 return "loading index";
318 default:
319 return "unknown open method";
320 }
321 }
322
323 /*
324 * Open an index under the given session. This operation will fail if the
325 * index session is suspended, or if there is already an open index.
326 */
uds_open_index(enum uds_open_index_type open_type,const struct uds_parameters * parameters,struct uds_index_session * session)327 int uds_open_index(enum uds_open_index_type open_type,
328 const struct uds_parameters *parameters,
329 struct uds_index_session *session)
330 {
331 int result;
332 char name[BDEVNAME_SIZE];
333
334 if (parameters == NULL) {
335 vdo_log_error("missing required parameters");
336 return -EINVAL;
337 }
338 if (parameters->bdev == NULL) {
339 vdo_log_error("missing required block device");
340 return -EINVAL;
341 }
342 if (session == NULL) {
343 vdo_log_error("missing required session pointer");
344 return -EINVAL;
345 }
346
347 result = start_loading_index_session(session);
348 if (result != UDS_SUCCESS)
349 return uds_status_to_errno(result);
350
351 session->parameters = *parameters;
352 format_dev_t(name, parameters->bdev->bd_dev);
353 vdo_log_info("%s: %s", get_open_type_string(open_type), name);
354
355 result = initialize_index_session(session, open_type);
356 if (result != UDS_SUCCESS)
357 vdo_log_error_strerror(result, "Failed %s",
358 get_open_type_string(open_type));
359
360 finish_loading_index_session(session, result);
361 return uds_status_to_errno(result);
362 }
363
wait_for_no_requests_in_progress(struct uds_index_session * index_session)364 static void wait_for_no_requests_in_progress(struct uds_index_session *index_session)
365 {
366 mutex_lock(&index_session->request_mutex);
367 while (index_session->request_count > 0) {
368 uds_wait_cond(&index_session->request_cond,
369 &index_session->request_mutex);
370 }
371 mutex_unlock(&index_session->request_mutex);
372 }
373
save_index(struct uds_index_session * index_session)374 static int __must_check save_index(struct uds_index_session *index_session)
375 {
376 wait_for_no_requests_in_progress(index_session);
377 return uds_save_index(index_session->index);
378 }
379
suspend_rebuild(struct uds_index_session * session)380 static void suspend_rebuild(struct uds_index_session *session)
381 {
382 mutex_lock(&session->load_context.mutex);
383 switch (session->load_context.status) {
384 case INDEX_OPENING:
385 session->load_context.status = INDEX_SUSPENDING;
386
387 /* Wait until the index indicates that it is not replaying. */
388 while ((session->load_context.status != INDEX_SUSPENDED) &&
389 (session->load_context.status != INDEX_READY)) {
390 uds_wait_cond(&session->load_context.cond,
391 &session->load_context.mutex);
392 }
393
394 break;
395
396 case INDEX_READY:
397 /* Index load does not need to be suspended. */
398 break;
399
400 case INDEX_SUSPENDED:
401 case INDEX_SUSPENDING:
402 case INDEX_FREEING:
403 default:
404 /* These cases should not happen. */
405 VDO_ASSERT_LOG_ONLY(false, "Bad load context state %u",
406 session->load_context.status);
407 break;
408 }
409 mutex_unlock(&session->load_context.mutex);
410 }
411
412 /*
413 * Suspend index operation, draining all current index requests and preventing new index requests
414 * from starting. Optionally saves all index data before returning.
415 */
uds_suspend_index_session(struct uds_index_session * session,bool save)416 int uds_suspend_index_session(struct uds_index_session *session, bool save)
417 {
418 int result = UDS_SUCCESS;
419 bool no_work = false;
420 bool rebuilding = false;
421
422 /* Wait for any current index state change to complete. */
423 mutex_lock(&session->request_mutex);
424 while (session->state & IS_FLAG_CLOSING)
425 uds_wait_cond(&session->request_cond, &session->request_mutex);
426
427 if ((session->state & IS_FLAG_WAITING) || (session->state & IS_FLAG_DESTROYING)) {
428 no_work = true;
429 vdo_log_info("Index session is already changing state");
430 result = -EBUSY;
431 } else if (session->state & IS_FLAG_SUSPENDED) {
432 no_work = true;
433 } else if (session->state & IS_FLAG_LOADING) {
434 session->state |= IS_FLAG_WAITING;
435 rebuilding = true;
436 } else if (session->state & IS_FLAG_LOADED) {
437 session->state |= IS_FLAG_WAITING;
438 } else {
439 no_work = true;
440 session->state |= IS_FLAG_SUSPENDED;
441 uds_broadcast_cond(&session->request_cond);
442 }
443 mutex_unlock(&session->request_mutex);
444
445 if (no_work)
446 return uds_status_to_errno(result);
447
448 if (rebuilding)
449 suspend_rebuild(session);
450 else if (save)
451 result = save_index(session);
452 else
453 result = uds_flush_index_session(session);
454
455 mutex_lock(&session->request_mutex);
456 session->state &= ~IS_FLAG_WAITING;
457 session->state |= IS_FLAG_SUSPENDED;
458 uds_broadcast_cond(&session->request_cond);
459 mutex_unlock(&session->request_mutex);
460 return uds_status_to_errno(result);
461 }
462
replace_device(struct uds_index_session * session,struct block_device * bdev)463 static int replace_device(struct uds_index_session *session, struct block_device *bdev)
464 {
465 int result;
466
467 result = uds_replace_index_storage(session->index, bdev);
468 if (result != UDS_SUCCESS)
469 return result;
470
471 session->parameters.bdev = bdev;
472 return UDS_SUCCESS;
473 }
474
475 /*
476 * Resume index operation after being suspended. If the index is suspended and the supplied block
477 * device differs from the current backing store, the index will start using the new backing store.
478 */
uds_resume_index_session(struct uds_index_session * session,struct block_device * bdev)479 int uds_resume_index_session(struct uds_index_session *session,
480 struct block_device *bdev)
481 {
482 int result = UDS_SUCCESS;
483 bool no_work = false;
484 bool resume_replay = false;
485
486 mutex_lock(&session->request_mutex);
487 if (session->state & IS_FLAG_WAITING) {
488 vdo_log_info("Index session is already changing state");
489 no_work = true;
490 result = -EBUSY;
491 } else if (!(session->state & IS_FLAG_SUSPENDED)) {
492 /* If not suspended, just succeed. */
493 no_work = true;
494 result = UDS_SUCCESS;
495 } else {
496 session->state |= IS_FLAG_WAITING;
497 if (session->state & IS_FLAG_LOADING)
498 resume_replay = true;
499 }
500 mutex_unlock(&session->request_mutex);
501
502 if (no_work)
503 return result;
504
505 if ((session->index != NULL) && (bdev != session->parameters.bdev)) {
506 result = replace_device(session, bdev);
507 if (result != UDS_SUCCESS) {
508 mutex_lock(&session->request_mutex);
509 session->state &= ~IS_FLAG_WAITING;
510 uds_broadcast_cond(&session->request_cond);
511 mutex_unlock(&session->request_mutex);
512 return uds_status_to_errno(result);
513 }
514 }
515
516 if (resume_replay) {
517 mutex_lock(&session->load_context.mutex);
518 switch (session->load_context.status) {
519 case INDEX_SUSPENDED:
520 session->load_context.status = INDEX_OPENING;
521 /* Notify the index to start replaying again. */
522 uds_broadcast_cond(&session->load_context.cond);
523 break;
524
525 case INDEX_READY:
526 /* There is no index rebuild to resume. */
527 break;
528
529 case INDEX_OPENING:
530 case INDEX_SUSPENDING:
531 case INDEX_FREEING:
532 default:
533 /* These cases should not happen; do nothing. */
534 VDO_ASSERT_LOG_ONLY(false, "Bad load context state %u",
535 session->load_context.status);
536 break;
537 }
538 mutex_unlock(&session->load_context.mutex);
539 }
540
541 mutex_lock(&session->request_mutex);
542 session->state &= ~IS_FLAG_WAITING;
543 session->state &= ~IS_FLAG_SUSPENDED;
544 uds_broadcast_cond(&session->request_cond);
545 mutex_unlock(&session->request_mutex);
546 return UDS_SUCCESS;
547 }
548
save_and_free_index(struct uds_index_session * index_session)549 static int save_and_free_index(struct uds_index_session *index_session)
550 {
551 int result = UDS_SUCCESS;
552 bool suspended;
553 struct uds_index *index = index_session->index;
554
555 if (index == NULL)
556 return UDS_SUCCESS;
557
558 mutex_lock(&index_session->request_mutex);
559 suspended = (index_session->state & IS_FLAG_SUSPENDED);
560 mutex_unlock(&index_session->request_mutex);
561
562 if (!suspended) {
563 result = uds_save_index(index);
564 if (result != UDS_SUCCESS)
565 vdo_log_warning_strerror(result,
566 "ignoring error from save_index");
567 }
568 uds_free_index(index);
569 index_session->index = NULL;
570
571 /*
572 * Reset all index state that happens to be in the index
573 * session, so it doesn't affect any future index.
574 */
575 mutex_lock(&index_session->load_context.mutex);
576 index_session->load_context.status = INDEX_OPENING;
577 mutex_unlock(&index_session->load_context.mutex);
578
579 mutex_lock(&index_session->request_mutex);
580 /* Only the suspend bit will remain relevant. */
581 index_session->state &= IS_FLAG_SUSPENDED;
582 mutex_unlock(&index_session->request_mutex);
583
584 return result;
585 }
586
587 /* Save and close the current index. */
uds_close_index(struct uds_index_session * index_session)588 int uds_close_index(struct uds_index_session *index_session)
589 {
590 int result = UDS_SUCCESS;
591
592 /* Wait for any current index state change to complete. */
593 mutex_lock(&index_session->request_mutex);
594 while ((index_session->state & IS_FLAG_WAITING) ||
595 (index_session->state & IS_FLAG_CLOSING)) {
596 uds_wait_cond(&index_session->request_cond,
597 &index_session->request_mutex);
598 }
599
600 if (index_session->state & IS_FLAG_SUSPENDED) {
601 vdo_log_info("Index session is suspended");
602 result = -EBUSY;
603 } else if ((index_session->state & IS_FLAG_DESTROYING) ||
604 !(index_session->state & IS_FLAG_LOADED)) {
605 /* The index doesn't exist, hasn't finished loading, or is being destroyed. */
606 result = UDS_NO_INDEX;
607 } else {
608 index_session->state |= IS_FLAG_CLOSING;
609 }
610 mutex_unlock(&index_session->request_mutex);
611 if (result != UDS_SUCCESS)
612 return uds_status_to_errno(result);
613
614 vdo_log_debug("Closing index");
615 wait_for_no_requests_in_progress(index_session);
616 result = save_and_free_index(index_session);
617 vdo_log_debug("Closed index");
618
619 mutex_lock(&index_session->request_mutex);
620 index_session->state &= ~IS_FLAG_CLOSING;
621 uds_broadcast_cond(&index_session->request_cond);
622 mutex_unlock(&index_session->request_mutex);
623 return uds_status_to_errno(result);
624 }
625
626 /* This will save and close an open index before destroying the session. */
uds_destroy_index_session(struct uds_index_session * index_session)627 int uds_destroy_index_session(struct uds_index_session *index_session)
628 {
629 int result;
630 bool load_pending = false;
631
632 vdo_log_debug("Destroying index session");
633
634 /* Wait for any current index state change to complete. */
635 mutex_lock(&index_session->request_mutex);
636 while ((index_session->state & IS_FLAG_WAITING) ||
637 (index_session->state & IS_FLAG_CLOSING)) {
638 uds_wait_cond(&index_session->request_cond,
639 &index_session->request_mutex);
640 }
641
642 if (index_session->state & IS_FLAG_DESTROYING) {
643 mutex_unlock(&index_session->request_mutex);
644 vdo_log_info("Index session is already closing");
645 return -EBUSY;
646 }
647
648 index_session->state |= IS_FLAG_DESTROYING;
649 load_pending = ((index_session->state & IS_FLAG_LOADING) &&
650 (index_session->state & IS_FLAG_SUSPENDED));
651 mutex_unlock(&index_session->request_mutex);
652
653 if (load_pending) {
654 /* Tell the index to terminate the rebuild. */
655 mutex_lock(&index_session->load_context.mutex);
656 if (index_session->load_context.status == INDEX_SUSPENDED) {
657 index_session->load_context.status = INDEX_FREEING;
658 uds_broadcast_cond(&index_session->load_context.cond);
659 }
660 mutex_unlock(&index_session->load_context.mutex);
661
662 /* Wait until the load exits before proceeding. */
663 mutex_lock(&index_session->request_mutex);
664 while (index_session->state & IS_FLAG_LOADING) {
665 uds_wait_cond(&index_session->request_cond,
666 &index_session->request_mutex);
667 }
668 mutex_unlock(&index_session->request_mutex);
669 }
670
671 wait_for_no_requests_in_progress(index_session);
672 result = save_and_free_index(index_session);
673 uds_request_queue_finish(index_session->callback_queue);
674 index_session->callback_queue = NULL;
675 vdo_log_debug("Destroyed index session");
676 vdo_free(index_session);
677 return uds_status_to_errno(result);
678 }
679
680 /* Wait until all callbacks for index operations are complete. */
uds_flush_index_session(struct uds_index_session * index_session)681 int uds_flush_index_session(struct uds_index_session *index_session)
682 {
683 wait_for_no_requests_in_progress(index_session);
684 uds_wait_for_idle_index(index_session->index);
685 return UDS_SUCCESS;
686 }
687
688 /* Statistics collection is intended to be thread-safe. */
collect_stats(const struct uds_index_session * index_session,struct uds_index_stats * stats)689 static void collect_stats(const struct uds_index_session *index_session,
690 struct uds_index_stats *stats)
691 {
692 const struct session_stats *session_stats = &index_session->stats;
693
694 stats->current_time = ktime_to_seconds(current_time_ns(CLOCK_REALTIME));
695 stats->posts_found = READ_ONCE(session_stats->posts_found);
696 stats->in_memory_posts_found = READ_ONCE(session_stats->posts_found_open_chapter);
697 stats->dense_posts_found = READ_ONCE(session_stats->posts_found_dense);
698 stats->sparse_posts_found = READ_ONCE(session_stats->posts_found_sparse);
699 stats->posts_not_found = READ_ONCE(session_stats->posts_not_found);
700 stats->updates_found = READ_ONCE(session_stats->updates_found);
701 stats->updates_not_found = READ_ONCE(session_stats->updates_not_found);
702 stats->deletions_found = READ_ONCE(session_stats->deletions_found);
703 stats->deletions_not_found = READ_ONCE(session_stats->deletions_not_found);
704 stats->queries_found = READ_ONCE(session_stats->queries_found);
705 stats->queries_not_found = READ_ONCE(session_stats->queries_not_found);
706 stats->requests = READ_ONCE(session_stats->requests);
707 }
708
uds_get_index_session_stats(struct uds_index_session * index_session,struct uds_index_stats * stats)709 int uds_get_index_session_stats(struct uds_index_session *index_session,
710 struct uds_index_stats *stats)
711 {
712 if (stats == NULL) {
713 vdo_log_error("received a NULL index stats pointer");
714 return -EINVAL;
715 }
716
717 collect_stats(index_session, stats);
718 if (index_session->index != NULL) {
719 uds_get_index_stats(index_session->index, stats);
720 } else {
721 stats->entries_indexed = 0;
722 stats->memory_used = 0;
723 stats->collisions = 0;
724 stats->entries_discarded = 0;
725 }
726
727 return UDS_SUCCESS;
728 }
729
uds_wait_cond(struct cond_var * cv,struct mutex * mutex)730 void uds_wait_cond(struct cond_var *cv, struct mutex *mutex)
731 {
732 DEFINE_WAIT(__wait);
733
734 prepare_to_wait(&cv->wait_queue, &__wait, TASK_IDLE);
735 mutex_unlock(mutex);
736 schedule();
737 finish_wait(&cv->wait_queue, &__wait);
738 mutex_lock(mutex);
739 }
740