File Index
Files and directories
Sign in
FILE

index-session.c

File preview with syntax highlighting.

linux-6.19.14/drivers/md/dm-vdo/indexer/index-session.c C
  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
 34enum 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
 52enum 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. */
 63static 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 */
 75static 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
101int 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
137static 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
149static inline void count_once(u64 *count_ptr)
150{
151	WRITE_ONCE(*count_ptr, READ_ONCE(*count_ptr) + 1);
152}
153
154static 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
203static 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
215static 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
240int 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
250static 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
269static 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
281static 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
305static 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 */
323int 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
360static 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
370static 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
376static 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 */
412int 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
459static 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 */
475int 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
545static 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. */
584int 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. */
623int 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. */
677int 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. */
685static 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
705int 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
726void 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}
~/ Read-only file preview.