diff --git a/drivers/ultraeth/Makefile b/drivers/ultraeth/Makefile
index e30373d4b5dc..dc0c07eeef65 100644
--- a/drivers/ultraeth/Makefile
+++ b/drivers/ultraeth/Makefile
@@ -1,3 +1,3 @@
 obj-$(CONFIG_ULTRAETH) += ultraeth.o
 
-ultraeth-objs := uet_main.o
+ultraeth-objs := uet_main.o uet_context.o
diff --git a/drivers/ultraeth/uet_context.c b/drivers/ultraeth/uet_context.c
new file mode 100644
index 000000000000..1c74cd8bbd56
--- /dev/null
+++ b/drivers/ultraeth/uet_context.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+
+#include <net/ultraeth/uet_context.h>
+
+#define MAX_CONTEXT_ID 256
+static DECLARE_BITMAP(uet_context_ids, MAX_CONTEXT_ID);
+static LIST_HEAD(uet_context_list);
+static DEFINE_MUTEX(uet_context_lock);
+
+static int uet_context_get_new_id(int id)
+{
+	if (WARN_ON(id < -1 || id >= MAX_CONTEXT_ID))
+		return -EINVAL;
+
+	mutex_lock(&uet_context_lock);
+	if (id == -1)
+		id = find_first_zero_bit(uet_context_ids, MAX_CONTEXT_ID);
+	if (id < MAX_CONTEXT_ID) {
+		if (test_and_set_bit(id, uet_context_ids))
+			id = -EBUSY;
+	} else {
+		id = -ENOSPC;
+	}
+	mutex_unlock(&uet_context_lock);
+
+	return id;
+}
+
+static void uet_context_put_id(struct uet_context *ctx)
+{
+	clear_bit(ctx->id, uet_context_ids);
+}
+
+static void uet_context_link(struct uet_context *ctx)
+{
+	WARN_ON(!list_empty(&ctx->list));
+	list_add(&ctx->list, &uet_context_list);
+}
+
+static void uet_context_unlink(struct uet_context *ctx)
+{
+	list_del_init(&ctx->list);
+	if (refcount_dec_and_test(&ctx->refcnt))
+		return;
+
+	mutex_unlock(&uet_context_lock);
+	wait_event(ctx->refcnt_wait, refcount_read(&ctx->refcnt) == 0);
+	mutex_lock(&uet_context_lock);
+	WARN_ON(refcount_read(&ctx->refcnt) > 0);
+}
+
+static struct uet_context *uet_context_find(int id)
+{
+	struct uet_context *ctx;
+
+	if (!test_bit(id, uet_context_ids))
+		return NULL;
+
+	list_for_each_entry(ctx, &uet_context_list, list)
+		if (ctx->id == id)
+			return ctx;
+
+	return NULL;
+}
+
+struct uet_context *uet_context_get_by_id(int id)
+{
+	struct uet_context *ctx;
+
+	mutex_lock(&uet_context_lock);
+	ctx = uet_context_find(id);
+	if (ctx)
+		refcount_inc(&ctx->refcnt);
+	mutex_unlock(&uet_context_lock);
+
+	return ctx;
+}
+
+void uet_context_put(struct uet_context *ctx)
+{
+	if (refcount_dec_and_test(&ctx->refcnt))
+		wake_up(&ctx->refcnt_wait);
+}
+
+int uet_context_create(int id)
+{
+	struct uet_context *ctx;
+	int err = -ENOMEM;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return err;
+
+	INIT_LIST_HEAD(&ctx->list);
+	init_waitqueue_head(&ctx->refcnt_wait);
+	refcount_set(&ctx->refcnt, 1);
+
+	ctx->id = uet_context_get_new_id(id);
+	if (ctx->id < 0) {
+		err = ctx->id;
+		goto ctx_id_err;
+	}
+
+	uet_context_link(ctx);
+
+	return 0;
+
+ctx_id_err:
+	kfree(ctx);
+
+	return err;
+}
+
+static void __uet_context_destroy(struct uet_context *ctx)
+{
+	uet_context_unlink(ctx);
+	uet_context_put_id(ctx);
+	kfree(ctx);
+}
+
+bool uet_context_destroy(int id)
+{
+	struct uet_context *ctx;
+	bool found = false;
+
+	mutex_lock(&uet_context_lock);
+	ctx = uet_context_find(id);
+	if (ctx) {
+		__uet_context_destroy(ctx);
+		found = true;
+	}
+	mutex_unlock(&uet_context_lock);
+
+	return found;
+}
+
+void uet_context_destroy_all(void)
+{
+	struct uet_context *ctx;
+
+	mutex_lock(&uet_context_lock);
+	while ((ctx = list_first_entry_or_null(&uet_context_list,
+						 struct uet_context,
+						 list)))
+		__uet_context_destroy(ctx);
+
+	WARN_ON(!list_empty(&uet_context_list));
+	mutex_unlock(&uet_context_lock);
+}
diff --git a/drivers/ultraeth/uet_main.c b/drivers/ultraeth/uet_main.c
index 0d74175fc047..0f8383c6aba0 100644
--- a/drivers/ultraeth/uet_main.c
+++ b/drivers/ultraeth/uet_main.c
@@ -3,6 +3,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/types.h>
+#include <net/ultraeth/uet_context.h>
 
 static int __init uet_init(void)
 {
@@ -11,6 +12,7 @@ static int __init uet_init(void)
 
 static void __exit uet_exit(void)
 {
+	uet_context_destroy_all();
 }
 
 module_init(uet_init);
diff --git a/include/net/ultraeth/uet_context.h b/include/net/ultraeth/uet_context.h
new file mode 100644
index 000000000000..150ad2c9b456
--- /dev/null
+++ b/include/net/ultraeth/uet_context.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+
+#ifndef _UET_CONTEXT_H
+#define _UET_CONTEXT_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/refcount.h>
+#include <linux/wait.h>
+
+struct uet_context {
+	int id;
+	refcount_t refcnt;
+	wait_queue_head_t refcnt_wait;
+	struct list_head list;
+};
+
+struct uet_context *uet_context_get_by_id(int id);
+void uet_context_put(struct uet_context *ses_pl);
+
+int uet_context_create(int id);
+bool uet_context_destroy(int id);
+void uet_context_destroy_all(void);
+
+#endif /* _UET_CONTEXT_H */
