xref: /linux/drivers/md/dm-vdo/indexer/index-session.c (revision 5014bebee0cffda14fafae5a2534d08120b7b9e8)
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