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