Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests: revocable: Add kselftest cases

Add kselftest cases for the revocable API.

The test consists of three parts:
- A kernel module (revocable_test.ko) that creates a debugfs interface
with `/provider` and `/consumer` files.
- A user-space C program (revocable_test) that uses the kselftest
harness to interact with the debugfs files.
- An orchestrating shell script (test-revocable.sh) that loads the
module, runs the C program, and unloads the module.

The test cases cover the following scenarios:
- Basic: Verifies that a consumer can successfully access the resource
provided via the provider.
- Revocation: Verifies that after the provider revokes the resource,
the consumer correctly receives a NULL pointer on a subsequent access.
- Try Access Macro: Same as "Revocation" but uses the
REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED().

Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
Link: https://patch.msgid.link/20260116080235.350305-4-tzungbi@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Tzung-Bi Shih and committed by
Greg Kroah-Hartman
9d4502fe cd769341

+389
+1
MAINTAINERS
··· 22375 22375 F: drivers/base/revocable.c 22376 22376 F: drivers/base/revocable_test.c 22377 22377 F: include/linux/revocable.h 22378 + F: tools/testing/selftests/drivers/base/revocable/ 22378 22379 22379 22380 RFKILL 22380 22381 M: Johannes Berg <johannes@sipsolutions.net>
+1
tools/testing/selftests/Makefile
··· 17 17 TARGETS += devices/error_logs 18 18 TARGETS += devices/probe 19 19 TARGETS += dmabuf-heaps 20 + TARGETS += drivers/base/revocable 20 21 TARGETS += drivers/dma-buf 21 22 TARGETS += drivers/ntsync 22 23 TARGETS += drivers/s390x/uvdevice
+7
tools/testing/selftests/drivers/base/revocable/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + 3 + TEST_GEN_MODS_DIR := test_modules 4 + TEST_GEN_PROGS_EXTENDED := revocable_test 5 + TEST_PROGS := test-revocable.sh 6 + 7 + include ../../../lib.mk
+136
tools/testing/selftests/drivers/base/revocable/revocable_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright 2026 Google LLC 4 + * 5 + * A selftest for the revocable API. 6 + * 7 + * The test cases cover the following scenarios: 8 + * 9 + * - Basic: Verifies that a consumer can successfully access the resource 10 + * provided via the provider. 11 + * 12 + * - Revocation: Verifies that after the provider revokes the resource, 13 + * the consumer correctly receives a NULL pointer on a subsequent access. 14 + * 15 + * - Try Access Macro: Same as "Revocation" but uses the 16 + * REVOCABLE_TRY_ACCESS_WITH() and REVOCABLE_TRY_ACCESS_SCOPED(). 17 + */ 18 + 19 + #include <fcntl.h> 20 + #include <unistd.h> 21 + 22 + #include "../../../kselftest_harness.h" 23 + 24 + #define DEBUGFS_PATH "/sys/kernel/debug/revocable_test" 25 + #define TEST_CMD_RESOURCE_GONE "resource_gone" 26 + #define TEST_DATA "12345678" 27 + #define TEST_MAGIC_OFFSET 0x1234 28 + #define TEST_MAGIC_OFFSET2 0x5678 29 + 30 + FIXTURE(revocable_fixture) { 31 + int pfd; 32 + int cfd; 33 + }; 34 + 35 + FIXTURE_SETUP(revocable_fixture) { 36 + int ret; 37 + 38 + self->pfd = open(DEBUGFS_PATH "/provider", O_WRONLY); 39 + ASSERT_NE(-1, self->pfd) 40 + TH_LOG("failed to open provider fd"); 41 + 42 + ret = write(self->pfd, TEST_DATA, strlen(TEST_DATA)); 43 + ASSERT_NE(-1, ret) { 44 + close(self->pfd); 45 + TH_LOG("failed to write test data"); 46 + } 47 + 48 + self->cfd = open(DEBUGFS_PATH "/consumer", O_RDONLY); 49 + ASSERT_NE(-1, self->cfd) 50 + TH_LOG("failed to open consumer fd"); 51 + } 52 + 53 + FIXTURE_TEARDOWN(revocable_fixture) { 54 + close(self->cfd); 55 + close(self->pfd); 56 + } 57 + 58 + /* 59 + * ASSERT_* is only available in TEST or TEST_F block. Use 60 + * macro for the helper. 61 + */ 62 + #define READ_TEST_DATA(_fd, _offset, _data, _msg) \ 63 + do { \ 64 + int ret; \ 65 + \ 66 + ret = lseek(_fd, _offset, SEEK_SET); \ 67 + ASSERT_NE(-1, ret) \ 68 + TH_LOG("failed to lseek"); \ 69 + \ 70 + ret = read(_fd, _data, sizeof(_data) - 1); \ 71 + ASSERT_NE(-1, ret) \ 72 + TH_LOG(_msg); \ 73 + data[ret] = '\0'; \ 74 + } while (0) 75 + 76 + TEST_F(revocable_fixture, basic) { 77 + char data[16]; 78 + 79 + READ_TEST_DATA(self->cfd, 0, data, "failed to read test data"); 80 + EXPECT_STREQ(TEST_DATA, data); 81 + } 82 + 83 + TEST_F(revocable_fixture, revocation) { 84 + char data[16]; 85 + int ret; 86 + 87 + READ_TEST_DATA(self->cfd, 0, data, "failed to read test data"); 88 + EXPECT_STREQ(TEST_DATA, data); 89 + 90 + ret = write(self->pfd, TEST_CMD_RESOURCE_GONE, 91 + strlen(TEST_CMD_RESOURCE_GONE)); 92 + ASSERT_NE(-1, ret) 93 + TH_LOG("failed to write resource gone cmd"); 94 + 95 + READ_TEST_DATA(self->cfd, 0, data, 96 + "failed to read test data after resource gone"); 97 + EXPECT_STREQ("(null)", data); 98 + } 99 + 100 + TEST_F(revocable_fixture, try_access_macro) { 101 + char data[16]; 102 + int ret; 103 + 104 + READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET, data, 105 + "failed to read test data"); 106 + EXPECT_STREQ(TEST_DATA, data); 107 + 108 + ret = write(self->pfd, TEST_CMD_RESOURCE_GONE, 109 + strlen(TEST_CMD_RESOURCE_GONE)); 110 + ASSERT_NE(-1, ret) 111 + TH_LOG("failed to write resource gone cmd"); 112 + 113 + READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET, data, 114 + "failed to read test data after resource gone"); 115 + EXPECT_STREQ("(null)", data); 116 + } 117 + 118 + TEST_F(revocable_fixture, try_access_macro2) { 119 + char data[16]; 120 + int ret; 121 + 122 + READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET2, data, 123 + "failed to read test data"); 124 + EXPECT_STREQ(TEST_DATA, data); 125 + 126 + ret = write(self->pfd, TEST_CMD_RESOURCE_GONE, 127 + strlen(TEST_CMD_RESOURCE_GONE)); 128 + ASSERT_NE(-1, ret) 129 + TH_LOG("failed to write resource gone cmd"); 130 + 131 + READ_TEST_DATA(self->cfd, TEST_MAGIC_OFFSET2, data, 132 + "failed to read test data after resource gone"); 133 + EXPECT_STREQ("(null)", data); 134 + } 135 + 136 + TEST_HARNESS_MAIN
+39
tools/testing/selftests/drivers/base/revocable/test-revocable.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + mod_name="revocable_test" 5 + ksft_fail=1 6 + ksft_skip=4 7 + 8 + if [ "$(id -u)" -ne 0 ]; then 9 + echo "$0: Must be run as root" 10 + exit "$ksft_skip" 11 + fi 12 + 13 + if ! which insmod > /dev/null 2>&1; then 14 + echo "$0: Need insmod" 15 + exit "$ksft_skip" 16 + fi 17 + 18 + if ! which rmmod > /dev/null 2>&1; then 19 + echo "$0: Need rmmod" 20 + exit "$ksft_skip" 21 + fi 22 + 23 + insmod test_modules/"${mod_name}".ko 24 + 25 + if [ ! -d /sys/kernel/debug/revocable_test/ ]; then 26 + mount -t debugfs none /sys/kernel/debug/ 27 + 28 + if [ ! -d /sys/kernel/debug/revocable_test/ ]; then 29 + echo "$0: Error mounting debugfs" 30 + exit "$ksft_fail" 31 + fi 32 + fi 33 + 34 + ./revocable_test 35 + ret=$? 36 + 37 + rmmod "${mod_name}" 38 + 39 + exit "${ret}"
+10
tools/testing/selftests/drivers/base/revocable/test_modules/Makefile
··· 1 + TESTMODS_DIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) 2 + KDIR ?= /lib/modules/$(shell uname -r)/build 3 + 4 + obj-m += revocable_test.o 5 + 6 + all: 7 + $(Q)$(MAKE) -C $(KDIR) M=$(TESTMODS_DIR) 8 + 9 + clean: 10 + $(Q)$(MAKE) -C $(KDIR) M=$(TESTMODS_DIR) clean
+195
tools/testing/selftests/drivers/base/revocable/test_modules/revocable_test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright 2026 Google LLC 4 + * 5 + * A kernel module for testing the revocable API. 6 + */ 7 + 8 + #include <linux/debugfs.h> 9 + #include <linux/module.h> 10 + #include <linux/revocable.h> 11 + #include <linux/slab.h> 12 + 13 + #define TEST_CMD_RESOURCE_GONE "resource_gone" 14 + #define TEST_MAGIC_OFFSET 0x1234 15 + #define TEST_MAGIC_OFFSET2 0x5678 16 + 17 + static struct dentry *debugfs_dir; 18 + 19 + struct revocable_test_provider_priv { 20 + struct revocable_provider *rp; 21 + struct dentry *dentry; 22 + char res[16]; 23 + }; 24 + 25 + static int revocable_test_consumer_open(struct inode *inode, struct file *filp) 26 + { 27 + struct revocable *rev; 28 + struct revocable_provider *rp = inode->i_private; 29 + 30 + rev = revocable_alloc(rp); 31 + if (!rev) 32 + return -ENOMEM; 33 + filp->private_data = rev; 34 + 35 + return 0; 36 + } 37 + 38 + static int revocable_test_consumer_release(struct inode *inode, 39 + struct file *filp) 40 + { 41 + struct revocable *rev = filp->private_data; 42 + 43 + revocable_free(rev); 44 + return 0; 45 + } 46 + 47 + static ssize_t revocable_test_consumer_read(struct file *filp, 48 + char __user *buf, 49 + size_t count, loff_t *offset) 50 + { 51 + char *res; 52 + char data[16]; 53 + size_t len; 54 + struct revocable *rev = filp->private_data; 55 + 56 + switch (*offset) { 57 + case 0: 58 + res = revocable_try_access(rev); 59 + snprintf(data, sizeof(data), "%s", res ?: "(null)"); 60 + revocable_withdraw_access(rev); 61 + break; 62 + case TEST_MAGIC_OFFSET: 63 + { 64 + REVOCABLE_TRY_ACCESS_WITH(rev, res); 65 + snprintf(data, sizeof(data), "%s", res ?: "(null)"); 66 + } 67 + break; 68 + case TEST_MAGIC_OFFSET2: 69 + REVOCABLE_TRY_ACCESS_SCOPED(rev, res) 70 + snprintf(data, sizeof(data), "%s", res ?: "(null)"); 71 + break; 72 + default: 73 + return 0; 74 + } 75 + 76 + len = min_t(size_t, strlen(data), count); 77 + if (copy_to_user(buf, data, len)) 78 + return -EFAULT; 79 + 80 + *offset = len; 81 + return len; 82 + } 83 + 84 + static const struct file_operations revocable_test_consumer_fops = { 85 + .open = revocable_test_consumer_open, 86 + .release = revocable_test_consumer_release, 87 + .read = revocable_test_consumer_read, 88 + .llseek = default_llseek, 89 + }; 90 + 91 + static int revocable_test_provider_open(struct inode *inode, struct file *filp) 92 + { 93 + struct revocable_test_provider_priv *priv; 94 + 95 + priv = kzalloc(sizeof(*priv), GFP_KERNEL); 96 + if (!priv) 97 + return -ENOMEM; 98 + filp->private_data = priv; 99 + 100 + return 0; 101 + } 102 + 103 + static int revocable_test_provider_release(struct inode *inode, 104 + struct file *filp) 105 + { 106 + struct revocable_test_provider_priv *priv = filp->private_data; 107 + 108 + debugfs_remove(priv->dentry); 109 + if (priv->rp) 110 + revocable_provider_revoke(priv->rp); 111 + kfree(priv); 112 + 113 + return 0; 114 + } 115 + 116 + static ssize_t revocable_test_provider_write(struct file *filp, 117 + const char __user *buf, 118 + size_t count, loff_t *offset) 119 + { 120 + size_t copied; 121 + char data[64]; 122 + struct revocable_test_provider_priv *priv = filp->private_data; 123 + 124 + copied = strncpy_from_user(data, buf, sizeof(data)); 125 + if (copied < 0) 126 + return copied; 127 + if (copied == sizeof(data)) 128 + data[sizeof(data) - 1] = '\0'; 129 + 130 + /* 131 + * Note: The test can't just close the FD for signaling the 132 + * resource gone. Subsequent file operations on the opening 133 + * FD of debugfs return -EIO after calling debugfs_remove(). 134 + * See also debugfs_file_get(). 135 + * 136 + * Here is a side command channel for signaling the resource 137 + * gone. 138 + */ 139 + if (!strcmp(data, TEST_CMD_RESOURCE_GONE)) { 140 + revocable_provider_revoke(priv->rp); 141 + priv->rp = NULL; 142 + } else { 143 + if (priv->res[0] != '\0') 144 + return 0; 145 + 146 + strscpy(priv->res, data); 147 + 148 + priv->rp = revocable_provider_alloc(&priv->res); 149 + if (!priv->rp) 150 + return -ENOMEM; 151 + 152 + priv->dentry = debugfs_create_file("consumer", 0400, 153 + debugfs_dir, priv->rp, 154 + &revocable_test_consumer_fops); 155 + if (!priv->dentry) { 156 + revocable_provider_revoke(priv->rp); 157 + return -ENOMEM; 158 + } 159 + } 160 + 161 + return copied; 162 + } 163 + 164 + static const struct file_operations revocable_test_provider_fops = { 165 + .open = revocable_test_provider_open, 166 + .release = revocable_test_provider_release, 167 + .write = revocable_test_provider_write, 168 + }; 169 + 170 + static int __init revocable_test_init(void) 171 + { 172 + debugfs_dir = debugfs_create_dir("revocable_test", NULL); 173 + if (!debugfs_dir) 174 + return -ENOMEM; 175 + 176 + if (!debugfs_create_file("provider", 0200, debugfs_dir, NULL, 177 + &revocable_test_provider_fops)) { 178 + debugfs_remove_recursive(debugfs_dir); 179 + return -ENOMEM; 180 + } 181 + 182 + return 0; 183 + } 184 + 185 + static void __exit revocable_test_exit(void) 186 + { 187 + debugfs_remove_recursive(debugfs_dir); 188 + } 189 + 190 + module_init(revocable_test_init); 191 + module_exit(revocable_test_exit); 192 + 193 + MODULE_LICENSE("GPL"); 194 + MODULE_AUTHOR("Tzung-Bi Shih <tzungbi@kernel.org>"); 195 + MODULE_DESCRIPTION("Revocable Kselftest");