Commit 89f9b701 authored by Matthew Sakai's avatar Matthew Sakai Committed by Mike Snitzer
Browse files

dm vdo: add thread and synchronization utilities



This patch adds utilities for managing and using named threads, as well as
several locking and synchronization utilities. These utilities help dm-vdo
minimize thread transitions and manage interactions between threads.

Co-developed-by: default avatarJ. corwin Coburn <corwin@hurlbutnet.net>
Signed-off-by: default avatarJ. corwin Coburn <corwin@hurlbutnet.net>
Co-developed-by: default avatarMichael Sclafani <dm-devel@lists.linux.dev>
Signed-off-by: default avatarMichael Sclafani <dm-devel@lists.linux.dev>
Co-developed-by: default avatarThomas Jaskiewicz <tom@jaskiewicz.us>
Signed-off-by: default avatarThomas Jaskiewicz <tom@jaskiewicz.us>
Co-developed-by: default avatarBruce Johnston <bjohnsto@redhat.com>
Signed-off-by: default avatarBruce Johnston <bjohnsto@redhat.com>
Co-developed-by: default avatarKen Raeburn <raeburn@redhat.com>
Signed-off-by: default avatarKen Raeburn <raeburn@redhat.com>
Signed-off-by: default avatarMatthew Sakai <msakai@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@kernel.org>
parent 4fcb4290
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2023 Red Hat
 */

#include <linux/jiffies.h>
#include <linux/minmax.h>

#include "errors.h"
#include "time-utils.h"
#include "uds-threads.h"

int uds_init_cond(struct cond_var *cv)
{
	init_waitqueue_head(&cv->wait_queue);
	return UDS_SUCCESS;
}

int uds_signal_cond(struct cond_var *cv)
{
	wake_up(&cv->wait_queue);
	return UDS_SUCCESS;
}

int uds_broadcast_cond(struct cond_var *cv)
{
	wake_up_all(&cv->wait_queue);
	return UDS_SUCCESS;
}

int uds_wait_cond(struct cond_var *cv, struct mutex *mutex)
{
	DEFINE_WAIT(__wait);

	prepare_to_wait(&cv->wait_queue, &__wait, TASK_IDLE);
	uds_unlock_mutex(mutex);
	schedule();
	finish_wait(&cv->wait_queue, &__wait);
	uds_lock_mutex(mutex);
	return UDS_SUCCESS;
}

int uds_destroy_cond(struct cond_var *cv)
{
	return UDS_SUCCESS;
}
+36 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2023 Red Hat
 */

#include "thread-device.h"

#include "thread-registry.h"

/* A registry of threads associated with device id numbers. */
static struct thread_registry device_id_thread_registry;

/* Any registered thread must be unregistered. */
void uds_register_thread_device_id(struct registered_thread *new_thread,
				   unsigned int *id_ptr)
{
	uds_register_thread(&device_id_thread_registry, new_thread, id_ptr);
}

void uds_unregister_thread_device_id(void)
{
	uds_unregister_thread(&device_id_thread_registry);
}

int uds_get_thread_device_id(void)
{
	const unsigned int *pointer;

	pointer = uds_lookup_thread(&device_id_thread_registry);
	return (pointer != NULL) ? *pointer : -1;
}

void uds_initialize_thread_device_registry(void)
{
	uds_initialize_thread_registry(&device_id_thread_registry);
}
+20 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright 2023 Red Hat
 */

#ifndef UDS_THREAD_DEVICE_H
#define UDS_THREAD_DEVICE_H

#include "thread-registry.h"

void uds_register_thread_device_id(struct registered_thread *new_thread,
				   unsigned int *id_ptr);

void uds_unregister_thread_device_id(void);

int uds_get_thread_device_id(void);

void uds_initialize_thread_device_registry(void);

#endif /* UDS_THREAD_DEVICE_H */
+92 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright 2023 Red Hat
 */

#include "thread-registry.h"

#include <linux/rculist.h>

#include "permassert.h"

/*
 * We need to be careful when using other facilities that may use thread registry functions in
 * their normal operation. For example, we do not want to invoke the logger while holding a lock.
 */

void uds_initialize_thread_registry(struct thread_registry *registry)
{
	INIT_LIST_HEAD(&registry->links);
	spin_lock_init(&registry->lock);
}

/* Register the current thread and associate it with a data pointer. */
void uds_register_thread(struct thread_registry *registry,
			 struct registered_thread *new_thread, const void *pointer)
{
	struct registered_thread *thread;
	bool found_it = false;

	INIT_LIST_HEAD(&new_thread->links);
	new_thread->pointer = pointer;
	new_thread->task = current;

	spin_lock(&registry->lock);
	list_for_each_entry(thread, &registry->links, links) {
		if (thread->task == current) {
			/* There should be no existing entry. */
			list_del_rcu(&thread->links);
			found_it = true;
			break;
		}
	}
	list_add_tail_rcu(&new_thread->links, &registry->links);
	spin_unlock(&registry->lock);

	ASSERT_LOG_ONLY(!found_it, "new thread not already in registry");
	if (found_it) {
		/* Ensure no RCU iterators see it before re-initializing. */
		synchronize_rcu();
		INIT_LIST_HEAD(&thread->links);
	}
}

void uds_unregister_thread(struct thread_registry *registry)
{
	struct registered_thread *thread;
	bool found_it = false;

	spin_lock(&registry->lock);
	list_for_each_entry(thread, &registry->links, links) {
		if (thread->task == current) {
			list_del_rcu(&thread->links);
			found_it = true;
			break;
		}
	}
	spin_unlock(&registry->lock);

	ASSERT_LOG_ONLY(found_it, "thread found in registry");
	if (found_it) {
		/* Ensure no RCU iterators see it before re-initializing. */
		synchronize_rcu();
		INIT_LIST_HEAD(&thread->links);
	}
}

const void *uds_lookup_thread(struct thread_registry *registry)
{
	struct registered_thread *thread;
	const void *result = NULL;

	rcu_read_lock();
	list_for_each_entry_rcu(thread, &registry->links, links) {
		if (thread->task == current) {
			result = thread->pointer;
			break;
		}
	}
	rcu_read_unlock();

	return result;
}
+32 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright 2023 Red Hat
 */

#ifndef UDS_THREAD_REGISTRY_H
#define UDS_THREAD_REGISTRY_H

#include <linux/list.h>
#include <linux/spinlock.h>

struct thread_registry {
	struct list_head links;
	spinlock_t lock;
};

struct registered_thread {
	struct list_head links;
	const void *pointer;
	struct task_struct *task;
};

void uds_initialize_thread_registry(struct thread_registry *registry);

void uds_register_thread(struct thread_registry *registry,
			 struct registered_thread *new_thread, const void *pointer);

void uds_unregister_thread(struct thread_registry *registry);

const void *uds_lookup_thread(struct thread_registry *registry);

#endif /* UDS_THREAD_REGISTRY_H */
Loading