12061Sjkh// SPDX-License-Identifier: GPL-2.0-only
250479Speter/*
32061Sjkh * Copyright 2023 Red Hat
438666Sjb */
532427Sjb
6111131Sru#include "index-session.h"
7111131Sru
8217733Sbz#include <linux/atomic.h>
9217733Sbz
1038666Sjb#include "logger.h"
1138666Sjb#include "memory-alloc.h"
1238666Sjb#include "time-utils.h"
13159363Strhodes
1464049Salex#include "funnel-requestqueue.h"
1564049Salex#include "index.h"
16116679Ssimokawa#include "index-layout.h"
1766071Smarkm
18116679Ssimokawa/*
1973504Sobrien * The index session contains a lock (the request_mutex) which ensures that only one thread can
20204661Simp * change the state of its index at a time. The state field indicates the current state of the
21158962Snetchild * index through a set of descriptive flags. The request_mutex must be notified whenever a
22223148Sru * non-transient state flag is cleared. The request_mutex is also used to count the number of
23169597Sdes * requests currently in progress so that they can be drained when suspending or closing the index.
24169597Sdes *
25169597Sdes * If the index session is suspended shortly after opening an index, it may have to suspend during
26169597Sdes * a rebuild. Depending on the size of the index, a rebuild may take a significant amount of time,
27169597Sdes * so UDS allows the rebuild to be paused in order to suspend the session in a timely manner. When
28169597Sdes * the index session is resumed, the rebuild can continue from where it left off. If the index
29169597Sdes * session is shut down with a suspended rebuild, the rebuild progress is abandoned and the rebuild
30169597Sdes * will start from the beginning the next time the index is loaded. The mutex and status fields in
31217815Sbz * the index_load_context are used to record the state of any interrupted rebuild.
32217815Sbz */
33218524Sjhb
3432427Sjbenum index_session_flag_bit {
3538666Sjb	IS_FLAG_BIT_START = 8,
36108451Sschweikh	/* The session has started loading an index but not completed it. */
3738666Sjb	IS_FLAG_BIT_LOADING = IS_FLAG_BIT_START,
3838666Sjb	/* The session has loaded an index, which can handle requests. */
3938666Sjb	IS_FLAG_BIT_LOADED,
4038666Sjb	/* The session's index has been permanently disabled. */
4117308Speter	IS_FLAG_BIT_DISABLED,
42217273Simp	/* The session's index is suspended. */
43217294Simp	IS_FLAG_BIT_SUSPENDED,
4419175Sbde	/* The session is handling some index state change. */
4596205Sjwd	IS_FLAG_BIT_WAITING,
46217297Simp	/* The session's index is closing and draining requests. */
47217297Simp	IS_FLAG_BIT_CLOSING,
4838042Sbde	/* The session is being destroyed and is draining requests. */
4996205Sjwd	IS_FLAG_BIT_DESTROYING,
5096205Sjwd};
5138042Sbde
5296205Sjwdenum index_session_flag {
53159363Strhodes	IS_FLAG_LOADED = (1 << IS_FLAG_BIT_LOADED),
54159363Strhodes	IS_FLAG_LOADING = (1 << IS_FLAG_BIT_LOADING),
5517308Speter	IS_FLAG_DISABLED = (1 << IS_FLAG_BIT_DISABLED),
5696205Sjwd	IS_FLAG_SUSPENDED = (1 << IS_FLAG_BIT_SUSPENDED),
5796205Sjwd	IS_FLAG_WAITING = (1 << IS_FLAG_BIT_WAITING),
5817308Speter	IS_FLAG_CLOSING = (1 << IS_FLAG_BIT_CLOSING),
59148330Snetchild	IS_FLAG_DESTROYING = (1 << IS_FLAG_BIT_DESTROYING),
60148330Snetchild};
61148330Snetchild
62148330Snetchild/* Release a reference to an index session. */
63159831Sobrienstatic void release_index_session(struct uds_index_session *index_session)
64148330Snetchild{
65148330Snetchild	mutex_lock(&index_session->request_mutex);
66148330Snetchild	if (--index_session->request_count == 0)
67148330Snetchild		uds_broadcast_cond(&index_session->request_cond);
68220512Sdougb	mutex_unlock(&index_session->request_mutex);
69148330Snetchild}
70148330Snetchild
7196205Sjwd/*
7296205Sjwd * Acquire a reference to the index session for an asynchronous index request. The reference must
7396205Sjwd * eventually be released with a corresponding call to release_index_session().
74162147Sru */
75162147Srustatic int get_index_session(struct uds_index_session *index_session)
7698723Sdillon{
7798723Sdillon	unsigned int state;
7898723Sdillon	int result = UDS_SUCCESS;
7938666Sjb
8038666Sjb	mutex_lock(&index_session->request_mutex);
8117308Speter	index_session->request_count++;
82123311Speter	state = index_session->state;
83123311Speter	mutex_unlock(&index_session->request_mutex);
84123311Speter
85123311Speter	if (state == IS_FLAG_LOADED) {
86175833Sjhb		return UDS_SUCCESS;
87175833Sjhb	} else if (state & IS_FLAG_DISABLED) {
88169597Sdes		result = UDS_DISABLED;
89169597Sdes	} else if ((state & IS_FLAG_LOADING) ||
90169597Sdes		   (state & IS_FLAG_SUSPENDED) ||
91169597Sdes		   (state & IS_FLAG_WAITING)) {
92219177Snwhitehorn		result = -EBUSY;
93219177Snwhitehorn	} else {
94158962Snetchild		result = UDS_NO_INDEX;
95219177Snwhitehorn	}
96219177Snwhitehorn
97158962Snetchild	release_index_session(index_session);
98156840Sru	return result;
99123311Speter}
100137288Speter
101209128Srajint uds_launch_request(struct uds_request *request)
102209128Sraj{
103156740Sru	size_t internal_size;
1042061Sjkh	int result;
10597769Sru
10697252Sru	if (request->callback == NULL) {
107119579Sru		vdo_log_error("missing required callback");
10897252Sru		return -EINVAL;
10995730Sru	}
11095793Sru
111111617Sru	switch (request->type) {
11295730Sru	case UDS_DELETE:
113116679Ssimokawa	case UDS_POST:
11495730Sru	case UDS_QUERY:
115116679Ssimokawa	case UDS_QUERY_NO_UPDATE:
11695730Sru	case UDS_UPDATE:
117110035Sru		break;
118107516Sru	default:
119138921Sru		vdo_log_error("received invalid callback type");
120156145Syar		return -EINVAL;
121138921Sru	}
122133942Sru
123133942Sru	/* Reset all internal fields before processing. */
124156145Syar	internal_size =
125133942Sru		sizeof(struct uds_request) - offsetof(struct uds_request, zone_number);
126110035Sru	// FIXME should be using struct_group for this instead
127117234Sru	memset((char *) request + sizeof(*request) - internal_size, 0, internal_size);
128110035Sru
129117229Sru	result = get_index_session(request->session);
130218206Simp	if (result != UDS_SUCCESS)
13154324Smarcel		return result;
132218130Simp
133218130Simp	request->found = false;
134221869Sattilio	request->unbatched = false;
135218130Simp	request->index = request->session->index;
136218130Simp
137218130Simp	uds_enqueue_request(request, STAGE_TRIAGE);
138218130Simp	return UDS_SUCCESS;
139218130Simp}
140218130Simp
141218130Simpstatic void enter_callback_stage(struct uds_request *request)
142218130Simp{
143218130Simp	if (request->status != UDS_SUCCESS) {
144218130Simp		/* All request errors are considered unrecoverable */
145218130Simp		mutex_lock(&request->session->request_mutex);
146218130Simp		request->session->state |= IS_FLAG_DISABLED;
147218130Simp		mutex_unlock(&request->session->request_mutex);
148218130Simp	}
149218130Simp
150218130Simp	uds_request_queue_enqueue(request->session->callback_queue, request);
151218130Simp}
152218130Simp
153218130Simpstatic inline void count_once(u64 *count_ptr)
154218130Simp{
155218130Simp	WRITE_ONCE(*count_ptr, READ_ONCE(*count_ptr) + 1);
156218130Simp}
157218130Simp
158218130Simpstatic void update_session_stats(struct uds_request *request)
159218130Simp{
160218130Simp	struct session_stats *session_stats = &request->session->stats;
161218130Simp
162218130Simp	count_once(&session_stats->requests);
163218130Simp
16417308Speter	switch (request->type) {
165119519Smarcel	case UDS_POST:
166119519Smarcel		if (request->found)
167119519Smarcel			count_once(&session_stats->posts_found);
168119519Smarcel		else
169119519Smarcel			count_once(&session_stats->posts_not_found);
170119519Smarcel
171119579Sru		if (request->location == UDS_LOCATION_IN_OPEN_CHAPTER)
172119519Smarcel			count_once(&session_stats->posts_found_open_chapter);
173119519Smarcel		else if (request->location == UDS_LOCATION_IN_DENSE)
174119519Smarcel			count_once(&session_stats->posts_found_dense);
175119519Smarcel		else if (request->location == UDS_LOCATION_IN_SPARSE)
176119519Smarcel			count_once(&session_stats->posts_found_sparse);
177126031Sgad		break;
178126024Sgad
179126024Sgad	case UDS_UPDATE:
180126024Sgad		if (request->found)
181126024Sgad			count_once(&session_stats->updates_found);
182126024Sgad		else
183126024Sgad			count_once(&session_stats->updates_not_found);
184126024Sgad		break;
185227879Sgjb
186126024Sgad	case UDS_DELETE:
187126024Sgad		if (request->found)
188227879Sgjb			count_once(&session_stats->deletions_found);
189227879Sgjb		else
190227879Sgjb			count_once(&session_stats->deletions_not_found);
191126024Sgad		break;
192126024Sgad
193126031Sgad	case UDS_QUERY:
194126024Sgad	case UDS_QUERY_NO_UPDATE:
195126024Sgad		if (request->found)
196126024Sgad			count_once(&session_stats->queries_found);
197172744Sdelphij		else
198126024Sgad			count_once(&session_stats->queries_not_found);
199126024Sgad		break;
200126024Sgad
201133376Sharti	default:
202126024Sgad		request->status = VDO_ASSERT(false, "unknown request type: %d",
203126024Sgad					     request->type);
204172744Sdelphij	}
205126024Sgad}
206126024Sgad
207125885Sgadstatic void handle_callbacks(struct uds_request *request)
208125885Sgad{
20938666Sjb	struct uds_index_session *index_session = request->session;
21017308Speter
211119519Smarcel	if (request->status == UDS_SUCCESS)
212119579Sru		update_session_stats(request);
213218206Simp
2142302Spaul	request->status = uds_status_to_errno(request->status);
21539206Sjkh	request->callback(request);
21639206Sjkh	release_index_session(index_session);
21739206Sjkh}
218133945Sru
219220556Sbzstatic int __must_check make_empty_index_session(struct uds_index_session **index_session_ptr)
220177609Sru{
221177609Sru	int result;
222177609Sru	struct uds_index_session *session;
223133945Sru
224132358Smarkm	result = vdo_allocate(1, struct uds_index_session, __func__, &session);
22517308Speter	if (result != VDO_SUCCESS)
22654324Smarcel		return result;
22754324Smarcel
228132234Smarcel	mutex_init(&session->request_mutex);
229132234Smarcel	uds_init_cond(&session->request_cond);
230132234Smarcel	mutex_init(&session->load_context.mutex);
231132234Smarcel	uds_init_cond(&session->load_context.cond);
23254324Smarcel
23354324Smarcel	result = uds_make_request_queue("callbackW", &handle_callbacks,
23454324Smarcel					&session->callback_queue);
235118531Sru	if (result != UDS_SUCCESS) {
23654324Smarcel		vdo_free(session);
23754324Smarcel		return result;
23854324Smarcel	}
23954324Smarcel
24054324Smarcel	*index_session_ptr = session;
24154324Smarcel	return UDS_SUCCESS;
242133376Sharti}
24354324Smarcel
244133376Shartiint uds_create_index_session(struct uds_index_session **session)
245133376Sharti{
24654324Smarcel	if (session == NULL) {
24754324Smarcel		vdo_log_error("missing session pointer");
24854324Smarcel		return -EINVAL;
24954324Smarcel	}
25054324Smarcel
251133376Sharti	return uds_status_to_errno(make_empty_index_session(session));
25254324Smarcel}
25354324Smarcel
25454324Smarcelstatic int __must_check start_loading_index_session(struct uds_index_session *index_session)
255118531Sru{
256118531Sru	int result;
25754324Smarcel
258132234Smarcel	mutex_lock(&index_session->request_mutex);
259132234Smarcel	if (index_session->state & IS_FLAG_SUSPENDED) {
260132234Smarcel		vdo_log_info("Index session is suspended");
261132234Smarcel		result = -EBUSY;
262132234Smarcel	} else if (index_session->state != 0) {
263132588Skensmith		vdo_log_info("Index is already loaded");
264132358Smarkm		result = -EBUSY;
265132234Smarcel	} else {
266132358Smarkm		index_session->state |= IS_FLAG_LOADING;
267132234Smarcel		result = UDS_SUCCESS;
268132234Smarcel	}
269132234Smarcel	mutex_unlock(&index_session->request_mutex);
27054324Smarcel	return result;
27154324Smarcel}
27295730Sru
27395730Srustatic void finish_loading_index_session(struct uds_index_session *index_session,
27495730Sru					 int result)
27595730Sru{
27695730Sru	mutex_lock(&index_session->request_mutex);
27795730Sru	index_session->state &= ~IS_FLAG_LOADING;
27895730Sru	if (result == UDS_SUCCESS)
27938666Sjb		index_session->state |= IS_FLAG_LOADED;
280107374Sru
28117308Speter	uds_broadcast_cond(&index_session->request_cond);
28255678Smarcel	mutex_unlock(&index_session->request_mutex);
283143032Sharti}
284138515Sharti
285117793Srustatic int initialize_index_session(struct uds_index_session *index_session,
286110035Sru				    enum uds_open_index_type open_type)
287174564Simp{
288110035Sru	int result;
2892061Sjkh	struct uds_configuration *config;
29017308Speter
291107516Sru	result = uds_make_configuration(&index_session->parameters, &config);
292174539Simp	if (result != UDS_SUCCESS) {
293174539Simp		vdo_log_error_strerror(result, "Failed to allocate config");
29455678Smarcel		return result;
295107516Sru	}
296107516Sru
297107516Sru	memset(&index_session->stats, 0, sizeof(index_session->stats));
298174564Simp	result = uds_make_index(config, open_type, &index_session->load_context,
299107516Sru				enter_callback_stage, &index_session->index);
300139112Sru	if (result != UDS_SUCCESS)
301164470Sjb		vdo_log_error_strerror(result, "Failed to make index");
302107516Sru	else
303122204Skris		uds_log_configuration(config);
30455678Smarcel
30555678Smarcel	uds_free_configuration(config);
306116696Sru	return result;
30755678Smarcel}
308133376Sharti
309107516Srustatic const char *get_open_type_string(enum uds_open_index_type open_type)
310107516Sru{
311107516Sru	switch (open_type) {
312107516Sru	case UDS_CREATE:
31355678Smarcel		return "creating index";
314185499Salfred	case UDS_LOAD:
315218524Sjhb		return "loading or rebuilding index";
316185499Salfred	case UDS_NO_REBUILD:
317218524Sjhb		return "loading index";
318218524Sjhb	default:
319218524Sjhb		return "unknown open method";
32055678Smarcel	}
321111131Sru}
322111131Sru
323111131Sru/*
324133945Sru * Open an index under the given session. This operation will fail if the
325111131Sru * index session is suspended, or if there is already an open index.
326111131Sru */
327217125Simpint uds_open_index(enum uds_open_index_type open_type,
328221869Sattilio		   const struct uds_parameters *parameters,
329216520Snwhitehorn		   struct uds_index_session *session)
330221216Sjhb{
331216520Snwhitehorn	int result;
332216520Snwhitehorn	char name[BDEVNAME_SIZE];
333216520Snwhitehorn
334216520Snwhitehorn	if (parameters == NULL) {
335216520Snwhitehorn		vdo_log_error("missing required parameters");
336168280Smarcel		return -EINVAL;
337218524Sjhb	}
338218524Sjhb	if (parameters->bdev == NULL) {
339218524Sjhb		vdo_log_error("missing required block device");
340218524Sjhb		return -EINVAL;
341218524Sjhb	}
342219137Sjhb	if (session == NULL) {
343218524Sjhb		vdo_log_error("missing required session pointer");
344217125Simp		return -EINVAL;
345217815Sbz	}
346217125Simp
347217125Simp	result = start_loading_index_session(session);
348217125Simp	if (result != UDS_SUCCESS)
349217125Simp		return uds_status_to_errno(result);
350217125Simp
351217125Simp	session->parameters = *parameters;
352185499Salfred	format_dev_t(name, parameters->bdev->bd_dev);
353217735Sbz	vdo_log_info("%s: %s", get_open_type_string(open_type), name);
354185499Salfred
355185499Salfred	result = initialize_index_session(session, open_type);
356185499Salfred	if (result != UDS_SUCCESS)
357185499Salfred		vdo_log_error_strerror(result, "Failed %s",
358185499Salfred				       get_open_type_string(open_type));
359133945Sru
360133945Sru	finish_loading_index_session(session, result);
361103985Sphk	return uds_status_to_errno(result);
362103985Sphk}
363103985Sphk
364185499Salfredstatic void wait_for_no_requests_in_progress(struct uds_index_session *index_session)
365217754Sbz{
366185499Salfred	mutex_lock(&index_session->request_mutex);
367168280Smarcel	while (index_session->request_count > 0) {
368162147Sru		uds_wait_cond(&index_session->request_cond,
369162147Sru			      &index_session->request_mutex);
370216520Snwhitehorn	}
371216520Snwhitehorn	mutex_unlock(&index_session->request_mutex);
372216520Snwhitehorn}
373179232Sjb
374216520Snwhitehornstatic int __must_check save_index(struct uds_index_session *index_session)
375216520Snwhitehorn{
376216520Snwhitehorn	wait_for_no_requests_in_progress(index_session);
377218524Sjhb	return uds_save_index(index_session->index);
378185250Sdes}
379218524Sjhb
380162147Srustatic void suspend_rebuild(struct uds_index_session *session)
381216520Snwhitehorn{
382218524Sjhb	mutex_lock(&session->load_context.mutex);
383218524Sjhb	switch (session->load_context.status) {
384218524Sjhb	case INDEX_OPENING:
385216520Snwhitehorn		session->load_context.status = INDEX_SUSPENDING;
386218524Sjhb
387216520Snwhitehorn		/* Wait until the index indicates that it is not replaying. */
388179232Sjb		while ((session->load_context.status != INDEX_SUSPENDED) &&
389205290Sdougb		       (session->load_context.status != INDEX_READY)) {
390219137Sjhb			uds_wait_cond(&session->load_context.cond,
391219137Sjhb				      &session->load_context.mutex);
392185250Sdes		}
393185499Salfred
394185499Salfred		break;
395103985Sphk
396201815Sbz	case INDEX_READY:
397201815Sbz		/* Index load does not need to be suspended. */
398205290Sdougb		break;
399201815Sbz
400201815Sbz	case INDEX_SUSPENDED:
401201815Sbz	case INDEX_SUSPENDING:
402202095Sbz	case INDEX_FREEING:
403202095Sbz	default:
404202095Sbz		/* These cases should not happen. */
405219137Sjhb		VDO_ASSERT_LOG_ONLY(false, "Bad load context state %u",
406201815Sbz				    session->load_context.status);
407201815Sbz		break;
408201815Sbz	}
409148154Sru	mutex_unlock(&session->load_context.mutex);
410219137Sjhb}
411219137Sjhb
412216934Simp/*
413216934Simp * Suspend index operation, draining all current index requests and preventing new index requests
414216934Simp * from starting. Optionally saves all index data before returning.
415216934Simp */
416216520Snwhitehornint uds_suspend_index_session(struct uds_index_session *session, bool save)
417216520Snwhitehorn{
418185250Sdes	int result = UDS_SUCCESS;
419185250Sdes	bool no_work = false;
420201815Sbz	bool rebuilding = false;
421216520Snwhitehorn
422148154Sru	/* Wait for any current index state change to complete. */
423201815Sbz	mutex_lock(&session->request_mutex);
424201815Sbz	while (session->state & IS_FLAG_CLOSING)
425201815Sbz		uds_wait_cond(&session->request_cond, &session->request_mutex);
426148154Sru
427133945Sru	if ((session->state & IS_FLAG_WAITING) || (session->state & IS_FLAG_DESTROYING)) {
428133945Sru		no_work = true;
429103985Sphk		vdo_log_info("Index session is already changing state");
430118531Sru		result = -EBUSY;
431118531Sru	} else if (session->state & IS_FLAG_SUSPENDED) {
432103985Sphk		no_work = true;
433185499Salfred	} else if (session->state & IS_FLAG_LOADING) {
434185499Salfred		session->state |= IS_FLAG_WAITING;
435185499Salfred		rebuilding = true;
436185499Salfred	} else if (session->state & IS_FLAG_LOADED) {
437185499Salfred		session->state |= IS_FLAG_WAITING;
438185499Salfred	} else {
439133945Sru		no_work = true;
440185499Salfred		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
463static 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 */
479int 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
549static 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. */
588int 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. */
627int 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. */
681int 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. */
689static 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
709int 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
730void 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