* [PATCH v3 23/30] selftests: ntsync: Add some tests for manual-reset event state.
From: Elizabeth Figura @ 2024-03-29 0:06 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
Test event-specific ioctls NTSYNC_IOC_EVENT_SET, NTSYNC_IOC_EVENT_RESET,
NTSYNC_IOC_EVENT_PULSE, NTSYNC_IOC_EVENT_READ for manual-reset events, and
waiting on manual-reset events.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
.../testing/selftests/drivers/ntsync/ntsync.c | 89 +++++++++++++++++++
1 file changed, 89 insertions(+)
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
index b77fb0b2c4b1..b6481c2b85cc 100644
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
@@ -73,6 +73,27 @@ static int unlock_mutex(int mutex, __u32 owner, __u32 *count)
return ret;
}
+static int read_event_state(int event, __u32 *signaled, __u32 *manual)
+{
+ struct ntsync_event_args args;
+ int ret;
+
+ memset(&args, 0xcc, sizeof(args));
+ ret = ioctl(event, NTSYNC_IOC_EVENT_READ, &args);
+ *signaled = args.signaled;
+ *manual = args.manual;
+ return ret;
+}
+
+#define check_event_state(event, signaled, manual) \
+ ({ \
+ __u32 __signaled, __manual; \
+ int ret = read_event_state((event), &__signaled, &__manual); \
+ EXPECT_EQ(0, ret); \
+ EXPECT_EQ((signaled), __signaled); \
+ EXPECT_EQ((manual), __manual); \
+ })
+
static int wait_objs(int fd, unsigned long request, __u32 count,
const int *objs, __u32 owner, __u32 *index)
{
@@ -353,6 +374,74 @@ TEST(mutex_state)
close(fd);
}
+TEST(manual_event_state)
+{
+ struct ntsync_event_args event_args;
+ __u32 index, signaled;
+ int fd, event, ret;
+
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
+ ASSERT_LE(0, fd);
+
+ event_args.manual = 1;
+ event_args.signaled = 0;
+ event_args.event = 0xdeadbeef;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, event_args.event);
+ event = event_args.event;
+ check_event_state(event, 0, 1);
+
+ signaled = 0xdeadbeef;
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, signaled);
+ check_event_state(event, 1, 1);
+
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, signaled);
+ check_event_state(event, 1, 1);
+
+ ret = wait_any(fd, 1, &event, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
+ check_event_state(event, 1, 1);
+
+ signaled = 0xdeadbeef;
+ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, signaled);
+ check_event_state(event, 0, 1);
+
+ ret = ioctl(event, NTSYNC_IOC_EVENT_RESET, &signaled);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, signaled);
+ check_event_state(event, 0, 1);
+
+ ret = wait_any(fd, 1, &event, 123, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
+ ret = ioctl(event, NTSYNC_IOC_EVENT_SET, &signaled);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, signaled);
+
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, signaled);
+ check_event_state(event, 0, 1);
+
+ ret = ioctl(event, NTSYNC_IOC_EVENT_PULSE, &signaled);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, signaled);
+ check_event_state(event, 0, 1);
+
+ close(event);
+
+ close(fd);
+}
+
TEST(test_wait_any)
{
int objs[NTSYNC_MAX_WAIT_COUNT + 1], fd, ret;
--
2.43.0
^ permalink raw reply related
* [PATCH v3 22/30] selftests: ntsync: Add some tests for wakeup signaling with WINESYNC_IOC_WAIT_ALL.
From: Elizabeth Figura @ 2024-03-29 0:06 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
Test contended "wait-for-all" waits, to make sure that scheduling and wakeup
logic works correctly, and that the wait only exits once objects are all
simultaneously signaled.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
.../testing/selftests/drivers/ntsync/ntsync.c | 98 +++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
index 993f5db23768..b77fb0b2c4b1 100644
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
@@ -706,4 +706,102 @@ TEST(wake_any)
close(fd);
}
+TEST(wake_all)
+{
+ struct ntsync_mutex_args mutex_args = {0};
+ struct ntsync_wait_args wait_args = {0};
+ struct ntsync_sem_args sem_args = {0};
+ struct wait_args thread_args;
+ int objs[2], fd, ret;
+ __u32 count, index;
+ pthread_t thread;
+
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
+ ASSERT_LE(0, fd);
+
+ sem_args.count = 0;
+ sem_args.max = 3;
+ sem_args.sem = 0xdeadbeef;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
+
+ mutex_args.owner = 123;
+ mutex_args.count = 1;
+ mutex_args.mutex = 0xdeadbeef;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_MUTEX, &mutex_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, mutex_args.mutex);
+
+ objs[0] = sem_args.sem;
+ objs[1] = mutex_args.mutex;
+
+ wait_args.timeout = get_abs_timeout(1000);
+ wait_args.objs = (uintptr_t)objs;
+ wait_args.count = 2;
+ wait_args.owner = 456;
+ thread_args.fd = fd;
+ thread_args.args = &wait_args;
+ thread_args.request = NTSYNC_IOC_WAIT_ALL;
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
+ count = 1;
+ ret = post_sem(sem_args.sem, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, count);
+
+ ret = pthread_tryjoin_np(thread, NULL);
+ EXPECT_EQ(EBUSY, ret);
+
+ check_sem_state(sem_args.sem, 1, 3);
+
+ ret = wait_any(fd, 1, &sem_args.sem, 123, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
+
+ ret = unlock_mutex(mutex_args.mutex, 123, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, count);
+
+ ret = pthread_tryjoin_np(thread, NULL);
+ EXPECT_EQ(EBUSY, ret);
+
+ check_mutex_state(mutex_args.mutex, 0, 0);
+
+ count = 2;
+ ret = post_sem(sem_args.sem, &count);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, count);
+ check_sem_state(sem_args.sem, 1, 3);
+ check_mutex_state(mutex_args.mutex, 1, 456);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, thread_args.ret);
+
+ /* delete an object while it's being waited on */
+
+ wait_args.timeout = get_abs_timeout(200);
+ wait_args.owner = 123;
+ ret = pthread_create(&thread, NULL, wait_thread, &thread_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_for_thread(thread, 100);
+ EXPECT_EQ(ETIMEDOUT, ret);
+
+ close(sem_args.sem);
+ close(mutex_args.mutex);
+
+ ret = wait_for_thread(thread, 200);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(-1, thread_args.ret);
+ EXPECT_EQ(ETIMEDOUT, thread_args.err);
+
+ close(fd);
+}
+
TEST_HARNESS_MAIN
--
2.43.0
^ permalink raw reply related
* [PATCH v3 05/30] ntsync: Introduce NTSYNC_IOC_WAIT_ALL.
From: Elizabeth Figura @ 2024-03-29 0:05 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
This is similar to NTSYNC_IOC_WAIT_ANY, but waits until all of the objects are
simultaneously signaled, and then acquires all of them as a single atomic
operation.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
drivers/misc/ntsync.c | 243 ++++++++++++++++++++++++++++++++++--
include/uapi/linux/ntsync.h | 1 +
2 files changed, 236 insertions(+), 8 deletions(-)
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index c6f84a5fc8c0..e914d626465a 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -55,7 +55,34 @@ struct ntsync_obj {
} sem;
} u;
+ /*
+ * any_waiters is protected by the object lock, but all_waiters is
+ * protected by the device wait_all_lock.
+ */
struct list_head any_waiters;
+ struct list_head all_waiters;
+
+ /*
+ * Hint describing how many tasks are queued on this object in a
+ * wait-all operation.
+ *
+ * Any time we do a wake, we may need to wake "all" waiters as well as
+ * "any" waiters. In order to atomically wake "all" waiters, we must
+ * lock all of the objects, and that means grabbing the wait_all_lock
+ * below (and, due to lock ordering rules, before locking this object).
+ * However, wait-all is a rare operation, and grabbing the wait-all
+ * lock for every wake would create unnecessary contention.
+ * Therefore we first check whether all_hint is zero, and, if it is,
+ * we skip trying to wake "all" waiters.
+ *
+ * This hint isn't protected by any lock. It might change during the
+ * course of a wake, but there's no meaningful race there; it's only a
+ * hint.
+ *
+ * Since wait requests must originate from user-space threads, we're
+ * limited here by PID_MAX_LIMIT, so there's no risk of overflow.
+ */
+ atomic_t all_hint;
};
struct ntsync_q_entry {
@@ -76,14 +103,100 @@ struct ntsync_q {
*/
atomic_t signaled;
+ bool all;
__u32 count;
struct ntsync_q_entry entries[];
};
struct ntsync_device {
+ /*
+ * Wait-all operations must atomically grab all objects, and be totally
+ * ordered with respect to each other and wait-any operations.
+ * If one thread is trying to acquire several objects, another thread
+ * cannot touch the object at the same time.
+ *
+ * We achieve this by grabbing multiple object locks at the same time.
+ * However, this creates a lock ordering problem. To solve that problem,
+ * wait_all_lock is taken first whenever multiple objects must be locked
+ * at the same time.
+ */
+ spinlock_t wait_all_lock;
+
struct file *file;
};
+static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
+{
+ lockdep_assert_held(&obj->lock);
+
+ switch (obj->type) {
+ case NTSYNC_TYPE_SEM:
+ return !!obj->u.sem.count;
+ }
+
+ WARN(1, "bad object type %#x\n", obj->type);
+ return false;
+}
+
+/*
+ * "locked_obj" is an optional pointer to an object which is already locked and
+ * should not be locked again. This is necessary so that changing an object's
+ * state and waking it can be a single atomic operation.
+ */
+static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
+ struct ntsync_obj *locked_obj)
+{
+ __u32 count = q->count;
+ bool can_wake = true;
+ int signaled = -1;
+ __u32 i;
+
+ lockdep_assert_held(&dev->wait_all_lock);
+ if (locked_obj)
+ lockdep_assert_held(&locked_obj->lock);
+
+ for (i = 0; i < count; i++) {
+ if (q->entries[i].obj != locked_obj)
+ spin_lock_nest_lock(&q->entries[i].obj->lock, &dev->wait_all_lock);
+ }
+
+ for (i = 0; i < count; i++) {
+ if (!is_signaled(q->entries[i].obj, q->owner)) {
+ can_wake = false;
+ break;
+ }
+ }
+
+ if (can_wake && atomic_try_cmpxchg(&q->signaled, &signaled, 0)) {
+ for (i = 0; i < count; i++) {
+ struct ntsync_obj *obj = q->entries[i].obj;
+
+ switch (obj->type) {
+ case NTSYNC_TYPE_SEM:
+ obj->u.sem.count--;
+ break;
+ }
+ }
+ wake_up_process(q->task);
+ }
+
+ for (i = 0; i < count; i++) {
+ if (q->entries[i].obj != locked_obj)
+ spin_unlock(&q->entries[i].obj->lock);
+ }
+}
+
+static void try_wake_all_obj(struct ntsync_device *dev, struct ntsync_obj *obj)
+{
+ struct ntsync_q_entry *entry;
+
+ lockdep_assert_held(&dev->wait_all_lock);
+ lockdep_assert_held(&obj->lock);
+
+ list_for_each_entry(entry, &obj->all_waiters, node)
+ try_wake_all(dev, entry->q, obj);
+}
+
static void try_wake_any_sem(struct ntsync_obj *sem)
{
struct ntsync_q_entry *entry;
@@ -124,6 +237,7 @@ static int post_sem_state(struct ntsync_obj *sem, __u32 count)
static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
{
+ struct ntsync_device *dev = sem->dev;
__u32 __user *user_args = argp;
__u32 prev_count;
__u32 args;
@@ -135,14 +249,29 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
if (sem->type != NTSYNC_TYPE_SEM)
return -EINVAL;
- spin_lock(&sem->lock);
+ if (atomic_read(&sem->all_hint) > 0) {
+ spin_lock(&dev->wait_all_lock);
+ spin_lock_nest_lock(&sem->lock, &dev->wait_all_lock);
- prev_count = sem->u.sem.count;
- ret = post_sem_state(sem, args);
- if (!ret)
- try_wake_any_sem(sem);
+ prev_count = sem->u.sem.count;
+ ret = post_sem_state(sem, args);
+ if (!ret) {
+ try_wake_all_obj(dev, sem);
+ try_wake_any_sem(sem);
+ }
- spin_unlock(&sem->lock);
+ spin_unlock(&sem->lock);
+ spin_unlock(&dev->wait_all_lock);
+ } else {
+ spin_lock(&sem->lock);
+
+ prev_count = sem->u.sem.count;
+ ret = post_sem_state(sem, args);
+ if (!ret)
+ try_wake_any_sem(sem);
+
+ spin_unlock(&sem->lock);
+ }
if (!ret && put_user(prev_count, user_args))
ret = -EFAULT;
@@ -195,6 +324,8 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
get_file(dev->file);
spin_lock_init(&obj->lock);
INIT_LIST_HEAD(&obj->any_waiters);
+ INIT_LIST_HEAD(&obj->all_waiters);
+ atomic_set(&obj->all_hint, 0);
return obj;
}
@@ -306,7 +437,7 @@ static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_ar
* Allocate and initialize the ntsync_q structure, but do not queue us yet.
*/
static int setup_wait(struct ntsync_device *dev,
- const struct ntsync_wait_args *args,
+ const struct ntsync_wait_args *args, bool all,
struct ntsync_q **ret_q)
{
const __u32 count = args->count;
@@ -333,6 +464,7 @@ static int setup_wait(struct ntsync_device *dev,
q->task = current;
q->owner = args->owner;
atomic_set(&q->signaled, -1);
+ q->all = all;
q->count = count;
for (i = 0; i < count; i++) {
@@ -342,6 +474,16 @@ static int setup_wait(struct ntsync_device *dev,
if (!obj)
goto err;
+ if (all) {
+ /* Check that the objects are all distinct. */
+ for (j = 0; j < i; j++) {
+ if (obj == q->entries[j].obj) {
+ put_obj(obj);
+ goto err;
+ }
+ }
+ }
+
entry->obj = obj;
entry->q = q;
entry->index = i;
@@ -377,7 +519,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
if (copy_from_user(&args, argp, sizeof(args)))
return -EFAULT;
- ret = setup_wait(dev, &args, &q);
+ ret = setup_wait(dev, &args, false, &q);
if (ret < 0)
return ret;
@@ -439,6 +581,87 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
return ret;
}
+static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
+{
+ struct ntsync_wait_args args;
+ struct ntsync_q *q;
+ int signaled;
+ __u32 i;
+ int ret;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
+ ret = setup_wait(dev, &args, true, &q);
+ if (ret < 0)
+ return ret;
+
+ /* queue ourselves */
+
+ spin_lock(&dev->wait_all_lock);
+
+ for (i = 0; i < args.count; i++) {
+ struct ntsync_q_entry *entry = &q->entries[i];
+ struct ntsync_obj *obj = entry->obj;
+
+ atomic_inc(&obj->all_hint);
+
+ /*
+ * obj->all_waiters is protected by dev->wait_all_lock rather
+ * than obj->lock, so there is no need to acquire obj->lock
+ * here.
+ */
+ list_add_tail(&entry->node, &obj->all_waiters);
+ }
+
+ /* check if we are already signaled */
+
+ try_wake_all(dev, q, NULL);
+
+ spin_unlock(&dev->wait_all_lock);
+
+ /* sleep */
+
+ ret = ntsync_schedule(q, &args);
+
+ /* and finally, unqueue */
+
+ spin_lock(&dev->wait_all_lock);
+
+ for (i = 0; i < args.count; i++) {
+ struct ntsync_q_entry *entry = &q->entries[i];
+ struct ntsync_obj *obj = entry->obj;
+
+ /*
+ * obj->all_waiters is protected by dev->wait_all_lock rather
+ * than obj->lock, so there is no need to acquire it here.
+ */
+ list_del(&entry->node);
+
+ atomic_dec(&obj->all_hint);
+
+ put_obj(obj);
+ }
+
+ spin_unlock(&dev->wait_all_lock);
+
+ signaled = atomic_read(&q->signaled);
+ if (signaled != -1) {
+ struct ntsync_wait_args __user *user_args = argp;
+
+ /* even if we caught a signal, we need to communicate success */
+ ret = 0;
+
+ if (put_user(signaled, &user_args->index))
+ ret = -EFAULT;
+ } else if (!ret) {
+ ret = -ETIMEDOUT;
+ }
+
+ kfree(q);
+ return ret;
+}
+
static int ntsync_char_open(struct inode *inode, struct file *file)
{
struct ntsync_device *dev;
@@ -447,6 +670,8 @@ static int ntsync_char_open(struct inode *inode, struct file *file)
if (!dev)
return -ENOMEM;
+ spin_lock_init(&dev->wait_all_lock);
+
file->private_data = dev;
dev->file = file;
return nonseekable_open(inode, file);
@@ -470,6 +695,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case NTSYNC_IOC_CREATE_SEM:
return ntsync_create_sem(dev, argp);
+ case NTSYNC_IOC_WAIT_ALL:
+ return ntsync_wait_all(dev, argp);
case NTSYNC_IOC_WAIT_ANY:
return ntsync_wait_any(dev, argp);
default:
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
index 60ad414b5552..83784d4438a1 100644
--- a/include/uapi/linux/ntsync.h
+++ b/include/uapi/linux/ntsync.h
@@ -33,6 +33,7 @@ struct ntsync_wait_args {
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
+#define NTSYNC_IOC_WAIT_ALL _IOWR('N', 0x83, struct ntsync_wait_args)
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
--
2.43.0
^ permalink raw reply related
* [PATCH v3 03/30] ntsync: Introduce NTSYNC_IOC_SEM_POST.
From: Elizabeth Figura @ 2024-03-29 0:05 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
This corresponds to the NT syscall NtReleaseSemaphore().
This increases the semaphore's internal counter by the given value, and returns
the previous value. If the counter would overflow the defined maximum, the
function instead fails and returns -EOVERFLOW.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
drivers/misc/ntsync.c | 72 +++++++++++++++++++++++++++++++++++--
include/uapi/linux/ntsync.h | 2 ++
2 files changed, 71 insertions(+), 3 deletions(-)
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index 20158ec148bc..3c2f743c58b0 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -10,7 +10,9 @@
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <uapi/linux/ntsync.h>
#define NTSYNC_NAME "ntsync"
@@ -31,23 +33,70 @@ enum ntsync_type {
*/
struct ntsync_obj {
+ spinlock_t lock;
+
enum ntsync_type type;
+ struct file *file;
+ struct ntsync_device *dev;
+
+ /* The following fields are protected by the object lock. */
union {
struct {
__u32 count;
__u32 max;
} sem;
} u;
-
- struct file *file;
- struct ntsync_device *dev;
};
struct ntsync_device {
struct file *file;
};
+/*
+ * Actually change the semaphore state, returning -EOVERFLOW if it is made
+ * invalid.
+ */
+static int post_sem_state(struct ntsync_obj *sem, __u32 count)
+{
+ __u32 sum;
+
+ lockdep_assert_held(&sem->lock);
+
+ if (check_add_overflow(sem->u.sem.count, count, &sum) ||
+ sum > sem->u.sem.max)
+ return -EOVERFLOW;
+
+ sem->u.sem.count = sum;
+ return 0;
+}
+
+static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
+{
+ __u32 __user *user_args = argp;
+ __u32 prev_count;
+ __u32 args;
+ int ret;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
+ if (sem->type != NTSYNC_TYPE_SEM)
+ return -EINVAL;
+
+ spin_lock(&sem->lock);
+
+ prev_count = sem->u.sem.count;
+ ret = post_sem_state(sem, args);
+
+ spin_unlock(&sem->lock);
+
+ if (!ret && put_user(prev_count, user_args))
+ ret = -EFAULT;
+
+ return ret;
+}
+
static int ntsync_obj_release(struct inode *inode, struct file *file)
{
struct ntsync_obj *obj = file->private_data;
@@ -58,9 +107,25 @@ static int ntsync_obj_release(struct inode *inode, struct file *file)
return 0;
}
+static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
+ unsigned long parm)
+{
+ struct ntsync_obj *obj = file->private_data;
+ void __user *argp = (void __user *)parm;
+
+ switch (cmd) {
+ case NTSYNC_IOC_SEM_POST:
+ return ntsync_sem_post(obj, argp);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
static const struct file_operations ntsync_obj_fops = {
.owner = THIS_MODULE,
.release = ntsync_obj_release,
+ .unlocked_ioctl = ntsync_obj_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
.llseek = no_llseek,
};
@@ -75,6 +140,7 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
obj->type = type;
obj->dev = dev;
get_file(dev->file);
+ spin_lock_init(&obj->lock);
return obj;
}
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
index 6a4867a6c97b..dcfa38fdc93c 100644
--- a/include/uapi/linux/ntsync.h
+++ b/include/uapi/linux/ntsync.h
@@ -18,4 +18,6 @@ struct ntsync_sem_args {
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
+#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
+
#endif
--
2.43.0
^ permalink raw reply related
* [PATCH v3 04/30] ntsync: Introduce NTSYNC_IOC_WAIT_ANY.
From: Elizabeth Figura @ 2024-03-29 0:05 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
This corresponds to part of the functionality of the NT syscall
NtWaitForMultipleObjects(). Specifically, it implements the behaviour where
the third argument (wait_any) is TRUE, and it does not handle alertable waits.
Those features have been split out into separate patches to ease review.
NTSYNC_IOC_WAIT_ANY is a vectored wait function similar to poll(). Unlike
poll(), it "consumes" objects when they are signaled. For semaphores, this means
decreasing one from the internal counter. At most one object can be consumed by
this function.
Up to 64 objects can be waited on at once. As soon as one is signaled, the
object with the lowest index is consumed, and that index is returned via the
"index" field.
A timeout is supported. The timeout is passed as a u64 nanosecond value, which
represents absolute time measured against either the MONOTONIC or REALTIME clock
(controlled by the flags argument). If U64_MAX is passed, the ioctl waits
indefinitely.
This ioctl validates that all objects belong to the relevant device. This is not
necessary for any technical reason related to NTSYNC_IOC_WAIT_ANY, but will be
necessary for NTSYNC_IOC_WAIT_ALL introduced in the following patch.
Two u32s of padding are left in the ntsync_wait_args structure; one will be used
by a patch later in the series (which is split out to ease review).
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
drivers/misc/ntsync.c | 250 ++++++++++++++++++++++++++++++++++++
include/uapi/linux/ntsync.h | 16 +++
2 files changed, 266 insertions(+)
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index 3c2f743c58b0..c6f84a5fc8c0 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -6,11 +6,16 @@
*/
#include <linux/anon_inodes.h>
+#include <linux/atomic.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/overflow.h>
+#include <linux/sched.h>
+#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <uapi/linux/ntsync.h>
@@ -30,6 +35,8 @@ enum ntsync_type {
*
* Both rely on struct file for reference counting. Individual
* ntsync_obj objects take a reference to the device when created.
+ * Wait operations take a reference to each object being waited on for
+ * the duration of the wait.
*/
struct ntsync_obj {
@@ -47,12 +54,56 @@ struct ntsync_obj {
__u32 max;
} sem;
} u;
+
+ struct list_head any_waiters;
+};
+
+struct ntsync_q_entry {
+ struct list_head node;
+ struct ntsync_q *q;
+ struct ntsync_obj *obj;
+ __u32 index;
+};
+
+struct ntsync_q {
+ struct task_struct *task;
+ __u32 owner;
+
+ /*
+ * Protected via atomic_try_cmpxchg(). Only the thread that wins the
+ * compare-and-swap may actually change object states and wake this
+ * task.
+ */
+ atomic_t signaled;
+
+ __u32 count;
+ struct ntsync_q_entry entries[];
};
struct ntsync_device {
struct file *file;
};
+static void try_wake_any_sem(struct ntsync_obj *sem)
+{
+ struct ntsync_q_entry *entry;
+
+ lockdep_assert_held(&sem->lock);
+
+ list_for_each_entry(entry, &sem->any_waiters, node) {
+ struct ntsync_q *q = entry->q;
+ int signaled = -1;
+
+ if (!sem->u.sem.count)
+ break;
+
+ if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) {
+ sem->u.sem.count--;
+ wake_up_process(q->task);
+ }
+ }
+}
+
/*
* Actually change the semaphore state, returning -EOVERFLOW if it is made
* invalid.
@@ -88,6 +139,8 @@ static int ntsync_sem_post(struct ntsync_obj *sem, void __user *argp)
prev_count = sem->u.sem.count;
ret = post_sem_state(sem, args);
+ if (!ret)
+ try_wake_any_sem(sem);
spin_unlock(&sem->lock);
@@ -141,6 +194,7 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
obj->dev = dev;
get_file(dev->file);
spin_lock_init(&obj->lock);
+ INIT_LIST_HEAD(&obj->any_waiters);
return obj;
}
@@ -191,6 +245,200 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
return put_user(fd, &user_args->sem);
}
+static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
+{
+ struct file *file = fget(fd);
+ struct ntsync_obj *obj;
+
+ if (!file)
+ return NULL;
+
+ if (file->f_op != &ntsync_obj_fops) {
+ fput(file);
+ return NULL;
+ }
+
+ obj = file->private_data;
+ if (obj->dev != dev) {
+ fput(file);
+ return NULL;
+ }
+
+ return obj;
+}
+
+static void put_obj(struct ntsync_obj *obj)
+{
+ fput(obj->file);
+}
+
+static int ntsync_schedule(const struct ntsync_q *q, const struct ntsync_wait_args *args)
+{
+ ktime_t timeout = ns_to_ktime(args->timeout);
+ clockid_t clock = CLOCK_MONOTONIC;
+ ktime_t *timeout_ptr;
+ int ret = 0;
+
+ timeout_ptr = (args->timeout == U64_MAX ? NULL : &timeout);
+
+ if (args->flags & NTSYNC_WAIT_REALTIME)
+ clock = CLOCK_REALTIME;
+
+ do {
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (atomic_read(&q->signaled) != -1) {
+ ret = 0;
+ break;
+ }
+ ret = schedule_hrtimeout_range_clock(timeout_ptr, 0, HRTIMER_MODE_ABS, clock);
+ } while (ret < 0);
+ __set_current_state(TASK_RUNNING);
+
+ return ret;
+}
+
+/*
+ * Allocate and initialize the ntsync_q structure, but do not queue us yet.
+ */
+static int setup_wait(struct ntsync_device *dev,
+ const struct ntsync_wait_args *args,
+ struct ntsync_q **ret_q)
+{
+ const __u32 count = args->count;
+ int fds[NTSYNC_MAX_WAIT_COUNT];
+ struct ntsync_q *q;
+ __u32 i, j;
+
+ if (!args->owner)
+ return -EINVAL;
+
+ if (args->pad || args->pad2 || (args->flags & ~NTSYNC_WAIT_REALTIME))
+ return -EINVAL;
+
+ if (args->count > NTSYNC_MAX_WAIT_COUNT)
+ return -EINVAL;
+
+ if (copy_from_user(fds, u64_to_user_ptr(args->objs),
+ array_size(count, sizeof(*fds))))
+ return -EFAULT;
+
+ q = kmalloc(struct_size(q, entries, count), GFP_KERNEL);
+ if (!q)
+ return -ENOMEM;
+ q->task = current;
+ q->owner = args->owner;
+ atomic_set(&q->signaled, -1);
+ q->count = count;
+
+ for (i = 0; i < count; i++) {
+ struct ntsync_q_entry *entry = &q->entries[i];
+ struct ntsync_obj *obj = get_obj(dev, fds[i]);
+
+ if (!obj)
+ goto err;
+
+ entry->obj = obj;
+ entry->q = q;
+ entry->index = i;
+ }
+
+ *ret_q = q;
+ return 0;
+
+err:
+ for (j = 0; j < i; j++)
+ put_obj(q->entries[j].obj);
+ kfree(q);
+ return -EINVAL;
+}
+
+static void try_wake_any_obj(struct ntsync_obj *obj)
+{
+ switch (obj->type) {
+ case NTSYNC_TYPE_SEM:
+ try_wake_any_sem(obj);
+ break;
+ }
+}
+
+static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
+{
+ struct ntsync_wait_args args;
+ struct ntsync_q *q;
+ int signaled;
+ __u32 i;
+ int ret;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
+ ret = setup_wait(dev, &args, &q);
+ if (ret < 0)
+ return ret;
+
+ /* queue ourselves */
+
+ for (i = 0; i < args.count; i++) {
+ struct ntsync_q_entry *entry = &q->entries[i];
+ struct ntsync_obj *obj = entry->obj;
+
+ spin_lock(&obj->lock);
+ list_add_tail(&entry->node, &obj->any_waiters);
+ spin_unlock(&obj->lock);
+ }
+
+ /* check if we are already signaled */
+
+ for (i = 0; i < args.count; i++) {
+ struct ntsync_obj *obj = q->entries[i].obj;
+
+ if (atomic_read(&q->signaled) != -1)
+ break;
+
+ spin_lock(&obj->lock);
+ try_wake_any_obj(obj);
+ spin_unlock(&obj->lock);
+ }
+
+ /* sleep */
+
+ ret = ntsync_schedule(q, &args);
+
+ /* and finally, unqueue */
+
+ for (i = 0; i < args.count; i++) {
+ struct ntsync_q_entry *entry = &q->entries[i];
+ struct ntsync_obj *obj = entry->obj;
+
+ spin_lock(&obj->lock);
+ list_del(&entry->node);
+ spin_unlock(&obj->lock);
+
+ put_obj(obj);
+ }
+
+ signaled = atomic_read(&q->signaled);
+ if (signaled != -1) {
+ struct ntsync_wait_args __user *user_args = argp;
+
+ /* even if we caught a signal, we need to communicate success */
+ ret = 0;
+
+ if (put_user(signaled, &user_args->index))
+ ret = -EFAULT;
+ } else if (!ret) {
+ ret = -ETIMEDOUT;
+ }
+
+ kfree(q);
+ return ret;
+}
+
static int ntsync_char_open(struct inode *inode, struct file *file)
{
struct ntsync_device *dev;
@@ -222,6 +470,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case NTSYNC_IOC_CREATE_SEM:
return ntsync_create_sem(dev, argp);
+ case NTSYNC_IOC_WAIT_ANY:
+ return ntsync_wait_any(dev, argp);
default:
return -ENOIOCTLCMD;
}
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
index dcfa38fdc93c..60ad414b5552 100644
--- a/include/uapi/linux/ntsync.h
+++ b/include/uapi/linux/ntsync.h
@@ -16,7 +16,23 @@ struct ntsync_sem_args {
__u32 max;
};
+#define NTSYNC_WAIT_REALTIME 0x1
+
+struct ntsync_wait_args {
+ __u64 timeout;
+ __u64 objs;
+ __u32 count;
+ __u32 owner;
+ __u32 index;
+ __u32 flags;
+ __u32 pad;
+ __u32 pad2;
+};
+
+#define NTSYNC_MAX_WAIT_COUNT 64
+
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
+#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
--
2.43.0
^ permalink raw reply related
* [PATCH v3 02/30] ntsync: Introduce NTSYNC_IOC_CREATE_SEM.
From: Elizabeth Figura @ 2024-03-29 0:05 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
This corresponds to the NT syscall NtCreateSemaphore().
Semaphores are one of three types of object to be implemented in this driver,
the others being mutexes and events.
An NT semaphore contains a 32-bit counter, and is signaled and can be acquired
when the counter is nonzero. The counter has a maximum value which is specified
at creation time. The initial value of the semaphore is also specified at
creation time. There are no restrictions on the maximum and initial value.
Each object is exposed as an file, to which any number of fds may be opened.
When all fds are closed, the object is deleted.
Objects hold a pointer to the ntsync_device that created them. The device's
reference count is driven by struct file.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
.../userspace-api/ioctl/ioctl-number.rst | 2 +
drivers/misc/ntsync.c | 131 ++++++++++++++++++
include/uapi/linux/ntsync.h | 21 +++
3 files changed, 154 insertions(+)
create mode 100644 include/uapi/linux/ntsync.h
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index c472423412bf..a141e8e65c5d 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -174,6 +174,8 @@ Code Seq# Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'N' 40-7F drivers/block/nvme.c
+'N' 80-8F uapi/linux/ntsync.h NT synchronization primitives
+ <mailto:wine-devel@winehq.org>
'O' 00-06 mtd/ubi-user.h UBI
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index bd76e653d83e..20158ec148bc 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -5,26 +5,157 @@
* Copyright (C) 2024 Elizabeth Figura <zfigura@codeweavers.com>
*/
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
+#include <linux/slab.h>
+#include <uapi/linux/ntsync.h>
#define NTSYNC_NAME "ntsync"
+enum ntsync_type {
+ NTSYNC_TYPE_SEM,
+};
+
+/*
+ * Individual synchronization primitives are represented by
+ * struct ntsync_obj, and each primitive is backed by a file.
+ *
+ * The whole namespace is represented by a struct ntsync_device also
+ * backed by a file.
+ *
+ * Both rely on struct file for reference counting. Individual
+ * ntsync_obj objects take a reference to the device when created.
+ */
+
+struct ntsync_obj {
+ enum ntsync_type type;
+
+ union {
+ struct {
+ __u32 count;
+ __u32 max;
+ } sem;
+ } u;
+
+ struct file *file;
+ struct ntsync_device *dev;
+};
+
+struct ntsync_device {
+ struct file *file;
+};
+
+static int ntsync_obj_release(struct inode *inode, struct file *file)
+{
+ struct ntsync_obj *obj = file->private_data;
+
+ fput(obj->dev->file);
+ kfree(obj);
+
+ return 0;
+}
+
+static const struct file_operations ntsync_obj_fops = {
+ .owner = THIS_MODULE,
+ .release = ntsync_obj_release,
+ .llseek = no_llseek,
+};
+
+static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
+ enum ntsync_type type)
+{
+ struct ntsync_obj *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return NULL;
+ obj->type = type;
+ obj->dev = dev;
+ get_file(dev->file);
+
+ return obj;
+}
+
+static int ntsync_obj_get_fd(struct ntsync_obj *obj)
+{
+ struct file *file;
+ int fd;
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+ file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR);
+ if (IS_ERR(file)) {
+ put_unused_fd(fd);
+ return PTR_ERR(file);
+ }
+ obj->file = file;
+ fd_install(fd, file);
+
+ return fd;
+}
+
+static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
+{
+ struct ntsync_sem_args __user *user_args = argp;
+ struct ntsync_sem_args args;
+ struct ntsync_obj *sem;
+ int fd;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
+ if (args.count > args.max)
+ return -EINVAL;
+
+ sem = ntsync_alloc_obj(dev, NTSYNC_TYPE_SEM);
+ if (!sem)
+ return -ENOMEM;
+ sem->u.sem.count = args.count;
+ sem->u.sem.max = args.max;
+ fd = ntsync_obj_get_fd(sem);
+ if (fd < 0) {
+ kfree(sem);
+ return fd;
+ }
+
+ return put_user(fd, &user_args->sem);
+}
+
static int ntsync_char_open(struct inode *inode, struct file *file)
{
+ struct ntsync_device *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ file->private_data = dev;
+ dev->file = file;
return nonseekable_open(inode, file);
}
static int ntsync_char_release(struct inode *inode, struct file *file)
{
+ struct ntsync_device *dev = file->private_data;
+
+ kfree(dev);
+
return 0;
}
static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
unsigned long parm)
{
+ struct ntsync_device *dev = file->private_data;
+ void __user *argp = (void __user *)parm;
+
switch (cmd) {
+ case NTSYNC_IOC_CREATE_SEM:
+ return ntsync_create_sem(dev, argp);
default:
return -ENOIOCTLCMD;
}
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
new file mode 100644
index 000000000000..6a4867a6c97b
--- /dev/null
+++ b/include/uapi/linux/ntsync.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Kernel support for NT synchronization primitive emulation
+ *
+ * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
+ */
+
+#ifndef __LINUX_NTSYNC_H
+#define __LINUX_NTSYNC_H
+
+#include <linux/types.h>
+
+struct ntsync_sem_args {
+ __u32 sem;
+ __u32 count;
+ __u32 max;
+};
+
+#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
+
+#endif
--
2.43.0
^ permalink raw reply related
* [PATCH v3 06/30] ntsync: Introduce NTSYNC_IOC_CREATE_MUTEX.
From: Elizabeth Figura @ 2024-03-29 0:05 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
This corresponds to the NT syscall NtCreateMutant().
An NT mutex is recursive, with a 32-bit recursion counter. When acquired via
NtWaitForMultipleObjects(), the recursion counter is incremented by one.
The OS records the thread which acquired it. However, in order to keep this
driver self-contained, the owning thread ID is managed by user-space, and passed
as a parameter to all relevant ioctls.
The initial owner and recursion count, if any, are specified when the mutex is
created.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
drivers/misc/ntsync.c | 68 +++++++++++++++++++++++++++++++++++++
include/uapi/linux/ntsync.h | 7 ++++
2 files changed, 75 insertions(+)
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index e914d626465a..173513aeeacc 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -24,6 +24,7 @@
enum ntsync_type {
NTSYNC_TYPE_SEM,
+ NTSYNC_TYPE_MUTEX,
};
/*
@@ -53,6 +54,10 @@ struct ntsync_obj {
__u32 count;
__u32 max;
} sem;
+ struct {
+ __u32 count;
+ __u32 owner;
+ } mutex;
} u;
/*
@@ -132,6 +137,10 @@ static bool is_signaled(struct ntsync_obj *obj, __u32 owner)
switch (obj->type) {
case NTSYNC_TYPE_SEM:
return !!obj->u.sem.count;
+ case NTSYNC_TYPE_MUTEX:
+ if (obj->u.mutex.owner && obj->u.mutex.owner != owner)
+ return false;
+ return obj->u.mutex.count < UINT_MAX;
}
WARN(1, "bad object type %#x\n", obj->type);
@@ -175,6 +184,10 @@ static void try_wake_all(struct ntsync_device *dev, struct ntsync_q *q,
case NTSYNC_TYPE_SEM:
obj->u.sem.count--;
break;
+ case NTSYNC_TYPE_MUTEX:
+ obj->u.mutex.count++;
+ obj->u.mutex.owner = q->owner;
+ break;
}
}
wake_up_process(q->task);
@@ -217,6 +230,29 @@ static void try_wake_any_sem(struct ntsync_obj *sem)
}
}
+static void try_wake_any_mutex(struct ntsync_obj *mutex)
+{
+ struct ntsync_q_entry *entry;
+
+ lockdep_assert_held(&mutex->lock);
+
+ list_for_each_entry(entry, &mutex->any_waiters, node) {
+ struct ntsync_q *q = entry->q;
+ int signaled = -1;
+
+ if (mutex->u.mutex.count == UINT_MAX)
+ break;
+ if (mutex->u.mutex.owner && mutex->u.mutex.owner != q->owner)
+ continue;
+
+ if (atomic_try_cmpxchg(&q->signaled, &signaled, entry->index)) {
+ mutex->u.mutex.count++;
+ mutex->u.mutex.owner = q->owner;
+ wake_up_process(q->task);
+ }
+ }
+}
+
/*
* Actually change the semaphore state, returning -EOVERFLOW if it is made
* invalid.
@@ -376,6 +412,33 @@ static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
return put_user(fd, &user_args->sem);
}
+static int ntsync_create_mutex(struct ntsync_device *dev, void __user *argp)
+{
+ struct ntsync_mutex_args __user *user_args = argp;
+ struct ntsync_mutex_args args;
+ struct ntsync_obj *mutex;
+ int fd;
+
+ if (copy_from_user(&args, argp, sizeof(args)))
+ return -EFAULT;
+
+ if (!args.owner != !args.count)
+ return -EINVAL;
+
+ mutex = ntsync_alloc_obj(dev, NTSYNC_TYPE_MUTEX);
+ if (!mutex)
+ return -ENOMEM;
+ mutex->u.mutex.count = args.count;
+ mutex->u.mutex.owner = args.owner;
+ fd = ntsync_obj_get_fd(mutex);
+ if (fd < 0) {
+ kfree(mutex);
+ return fd;
+ }
+
+ return put_user(fd, &user_args->mutex);
+}
+
static struct ntsync_obj *get_obj(struct ntsync_device *dev, int fd)
{
struct file *file = fget(fd);
@@ -505,6 +568,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
case NTSYNC_TYPE_SEM:
try_wake_any_sem(obj);
break;
+ case NTSYNC_TYPE_MUTEX:
+ try_wake_any_mutex(obj);
+ break;
}
}
@@ -693,6 +759,8 @@ static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
void __user *argp = (void __user *)parm;
switch (cmd) {
+ case NTSYNC_IOC_CREATE_MUTEX:
+ return ntsync_create_mutex(dev, argp);
case NTSYNC_IOC_CREATE_SEM:
return ntsync_create_sem(dev, argp);
case NTSYNC_IOC_WAIT_ALL:
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
index 83784d4438a1..cd7841cdba49 100644
--- a/include/uapi/linux/ntsync.h
+++ b/include/uapi/linux/ntsync.h
@@ -16,6 +16,12 @@ struct ntsync_sem_args {
__u32 max;
};
+struct ntsync_mutex_args {
+ __u32 mutex;
+ __u32 owner;
+ __u32 count;
+};
+
#define NTSYNC_WAIT_REALTIME 0x1
struct ntsync_wait_args {
@@ -34,6 +40,7 @@ struct ntsync_wait_args {
#define NTSYNC_IOC_CREATE_SEM _IOWR('N', 0x80, struct ntsync_sem_args)
#define NTSYNC_IOC_WAIT_ANY _IOWR('N', 0x82, struct ntsync_wait_args)
#define NTSYNC_IOC_WAIT_ALL _IOWR('N', 0x83, struct ntsync_wait_args)
+#define NTSYNC_IOC_CREATE_MUTEX _IOWR('N', 0x84, struct ntsync_sem_args)
#define NTSYNC_IOC_SEM_POST _IOWR('N', 0x81, __u32)
--
2.43.0
^ permalink raw reply related
* [PATCH v3 26/30] selftests: ntsync: Add tests for alertable waits.
From: Elizabeth Figura @ 2024-03-29 0:06 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
Test the "alert" functionality of NTSYNC_IOC_WAIT_ALL and NTSYNC_IOC_WAIT_ANY,
when a wait is woken with an alert and when it is woken by an object.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
.../testing/selftests/drivers/ntsync/ntsync.c | 179 +++++++++++++++++-
1 file changed, 176 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
index 5d17eff6a370..5465a16d38b3 100644
--- a/tools/testing/selftests/drivers/ntsync/ntsync.c
+++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
@@ -95,7 +95,7 @@ static int read_event_state(int event, __u32 *signaled, __u32 *manual)
})
static int wait_objs(int fd, unsigned long request, __u32 count,
- const int *objs, __u32 owner, __u32 *index)
+ const int *objs, __u32 owner, int alert, __u32 *index)
{
struct ntsync_wait_args args = {0};
struct timespec timeout;
@@ -108,6 +108,7 @@ static int wait_objs(int fd, unsigned long request, __u32 count,
args.objs = (uintptr_t)objs;
args.owner = owner;
args.index = 0xdeadbeef;
+ args.alert = alert;
ret = ioctl(fd, request, &args);
*index = args.index;
return ret;
@@ -115,12 +116,26 @@ static int wait_objs(int fd, unsigned long request, __u32 count,
static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
{
- return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, index);
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY, count, objs, owner, 0, index);
}
static int wait_all(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
{
- return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, index);
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL, count, objs, owner, 0, index);
+}
+
+static int wait_any_alert(int fd, __u32 count, const int *objs,
+ __u32 owner, int alert, __u32 *index)
+{
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ANY,
+ count, objs, owner, alert, index);
+}
+
+static int wait_all_alert(int fd, __u32 count, const int *objs,
+ __u32 owner, int alert, __u32 *index)
+{
+ return wait_objs(fd, NTSYNC_IOC_WAIT_ALL,
+ count, objs, owner, alert, index);
}
TEST(semaphore_state)
@@ -1095,4 +1110,162 @@ TEST(wake_all)
close(fd);
}
+TEST(alert_any)
+{
+ struct ntsync_event_args event_args = {0};
+ struct ntsync_sem_args sem_args = {0};
+ __u32 index, count, signaled;
+ int objs[2], fd, ret;
+
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
+ ASSERT_LE(0, fd);
+
+ sem_args.count = 0;
+ sem_args.max = 2;
+ sem_args.sem = 0xdeadbeef;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
+ objs[0] = sem_args.sem;
+
+ sem_args.count = 1;
+ sem_args.max = 2;
+ sem_args.sem = 0xdeadbeef;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
+ objs[1] = sem_args.sem;
+
+ event_args.manual = true;
+ event_args.signaled = true;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_any_alert(fd, 0, NULL, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
+
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_RESET, &signaled);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_any_alert(fd, 0, NULL, 123, event_args.event, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
+ ret = ioctl(event_args.event, NTSYNC_IOC_EVENT_SET, &signaled);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(1, index);
+
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, index);
+
+ close(event_args.event);
+
+ /* test with an auto-reset event */
+
+ event_args.manual = false;
+ event_args.signaled = true;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
+ EXPECT_EQ(0, ret);
+
+ count = 1;
+ ret = post_sem(objs[0], &count);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
+
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, index);
+
+ ret = wait_any_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
+ close(event_args.event);
+
+ close(objs[0]);
+ close(objs[1]);
+
+ close(fd);
+}
+
+TEST(alert_all)
+{
+ struct ntsync_event_args event_args = {0};
+ struct ntsync_sem_args sem_args = {0};
+ __u32 index, count, signaled;
+ int objs[2], fd, ret;
+
+ fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
+ ASSERT_LE(0, fd);
+
+ sem_args.count = 2;
+ sem_args.max = 2;
+ sem_args.sem = 0xdeadbeef;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
+ objs[0] = sem_args.sem;
+
+ sem_args.count = 1;
+ sem_args.max = 2;
+ sem_args.sem = 0xdeadbeef;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
+ EXPECT_EQ(0, ret);
+ EXPECT_NE(0xdeadbeef, sem_args.sem);
+ objs[1] = sem_args.sem;
+
+ event_args.manual = true;
+ event_args.signaled = true;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
+
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, index);
+
+ close(event_args.event);
+
+ /* test with an auto-reset event */
+
+ event_args.manual = false;
+ event_args.signaled = true;
+ ret = ioctl(fd, NTSYNC_IOC_CREATE_EVENT, &event_args);
+ EXPECT_EQ(0, ret);
+
+ count = 2;
+ ret = post_sem(objs[1], &count);
+ EXPECT_EQ(0, ret);
+
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(0, index);
+
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ(2, index);
+
+ ret = wait_all_alert(fd, 2, objs, 123, event_args.event, &index);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(ETIMEDOUT, errno);
+
+ close(event_args.event);
+
+ close(objs[0]);
+ close(objs[1]);
+
+ close(fd);
+}
+
TEST_HARNESS_MAIN
--
2.43.0
^ permalink raw reply related
* [PATCH v3 15/30] ntsync: Introduce NTSYNC_IOC_EVENT_READ.
From: Elizabeth Figura @ 2024-03-29 0:06 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
This corresponds to the NT syscall NtQueryEvent().
This returns the signaled state of the event and whether it is manual-reset.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
drivers/misc/ntsync.c | 21 +++++++++++++++++++++
include/uapi/linux/ntsync.h | 1 +
2 files changed, 22 insertions(+)
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index bd043dccc9fa..a03c6fceb518 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -572,6 +572,25 @@ static int ntsync_mutex_read(struct ntsync_obj *mutex, void __user *argp)
return ret;
}
+static int ntsync_event_read(struct ntsync_obj *event, void __user *argp)
+{
+ struct ntsync_event_args __user *user_args = argp;
+ struct ntsync_event_args args;
+
+ if (event->type != NTSYNC_TYPE_EVENT)
+ return -EINVAL;
+
+ args.event = 0;
+ spin_lock(&event->lock);
+ args.manual = event->u.event.manual;
+ args.signaled = event->u.event.signaled;
+ spin_unlock(&event->lock);
+
+ if (copy_to_user(user_args, &args, sizeof(args)))
+ return -EFAULT;
+ return 0;
+}
+
static int ntsync_obj_release(struct inode *inode, struct file *file)
{
struct ntsync_obj *obj = file->private_data;
@@ -605,6 +624,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
return ntsync_event_reset(obj, argp);
case NTSYNC_IOC_EVENT_PULSE:
return ntsync_event_set(obj, argp, true);
+ case NTSYNC_IOC_EVENT_READ:
+ return ntsync_event_read(obj, argp);
default:
return -ENOIOCTLCMD;
}
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
index 797e8df10a3a..80f36de46a75 100644
--- a/include/uapi/linux/ntsync.h
+++ b/include/uapi/linux/ntsync.h
@@ -57,5 +57,6 @@ struct ntsync_wait_args {
#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
#define NTSYNC_IOC_MUTEX_READ _IOR ('N', 0x8c, struct ntsync_mutex_args)
+#define NTSYNC_IOC_EVENT_READ _IOR ('N', 0x8d, struct ntsync_event_args)
#endif
--
2.43.0
^ permalink raw reply related
* [PATCH v3 14/30] ntsync: Introduce NTSYNC_IOC_MUTEX_READ.
From: Elizabeth Figura @ 2024-03-29 0:06 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
This corresponds to the NT syscall NtQueryMutant().
This returns the recursion count, owner, and abandoned state of the mutex.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
drivers/misc/ntsync.c | 23 +++++++++++++++++++++++
include/uapi/linux/ntsync.h | 1 +
2 files changed, 24 insertions(+)
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index 961e8d241602..bd043dccc9fa 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -551,6 +551,27 @@ static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp)
return 0;
}
+static int ntsync_mutex_read(struct ntsync_obj *mutex, void __user *argp)
+{
+ struct ntsync_mutex_args __user *user_args = argp;
+ struct ntsync_mutex_args args;
+ int ret;
+
+ if (mutex->type != NTSYNC_TYPE_MUTEX)
+ return -EINVAL;
+
+ args.mutex = 0;
+ spin_lock(&mutex->lock);
+ args.count = mutex->u.mutex.count;
+ args.owner = mutex->u.mutex.owner;
+ ret = mutex->u.mutex.ownerdead ? -EOWNERDEAD : 0;
+ spin_unlock(&mutex->lock);
+
+ if (copy_to_user(user_args, &args, sizeof(args)))
+ return -EFAULT;
+ return ret;
+}
+
static int ntsync_obj_release(struct inode *inode, struct file *file)
{
struct ntsync_obj *obj = file->private_data;
@@ -576,6 +597,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
return ntsync_mutex_unlock(obj, argp);
case NTSYNC_IOC_MUTEX_KILL:
return ntsync_mutex_kill(obj, argp);
+ case NTSYNC_IOC_MUTEX_READ:
+ return ntsync_mutex_read(obj, argp);
case NTSYNC_IOC_EVENT_SET:
return ntsync_event_set(obj, argp, false);
case NTSYNC_IOC_EVENT_RESET:
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
index e298400bf25a..797e8df10a3a 100644
--- a/include/uapi/linux/ntsync.h
+++ b/include/uapi/linux/ntsync.h
@@ -56,5 +56,6 @@ struct ntsync_wait_args {
#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
+#define NTSYNC_IOC_MUTEX_READ _IOR ('N', 0x8c, struct ntsync_mutex_args)
#endif
--
2.43.0
^ permalink raw reply related
* [PATCH v3 13/30] ntsync: Introduce NTSYNC_IOC_SEM_READ.
From: Elizabeth Figura @ 2024-03-29 0:06 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
This corresponds to the NT syscall NtQuerySemaphore().
This returns the current count and maximum count of the semaphore.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
drivers/misc/ntsync.c | 21 +++++++++++++++++++++
include/uapi/linux/ntsync.h | 1 +
2 files changed, 22 insertions(+)
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index adba4657bf26..961e8d241602 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -532,6 +532,25 @@ static int ntsync_event_reset(struct ntsync_obj *event, void __user *argp)
return 0;
}
+static int ntsync_sem_read(struct ntsync_obj *sem, void __user *argp)
+{
+ struct ntsync_sem_args __user *user_args = argp;
+ struct ntsync_sem_args args;
+
+ if (sem->type != NTSYNC_TYPE_SEM)
+ return -EINVAL;
+
+ args.sem = 0;
+ spin_lock(&sem->lock);
+ args.count = sem->u.sem.count;
+ args.max = sem->u.sem.max;
+ spin_unlock(&sem->lock);
+
+ if (copy_to_user(user_args, &args, sizeof(args)))
+ return -EFAULT;
+ return 0;
+}
+
static int ntsync_obj_release(struct inode *inode, struct file *file)
{
struct ntsync_obj *obj = file->private_data;
@@ -551,6 +570,8 @@ static long ntsync_obj_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case NTSYNC_IOC_SEM_POST:
return ntsync_sem_post(obj, argp);
+ case NTSYNC_IOC_SEM_READ:
+ return ntsync_sem_read(obj, argp);
case NTSYNC_IOC_MUTEX_UNLOCK:
return ntsync_mutex_unlock(obj, argp);
case NTSYNC_IOC_MUTEX_KILL:
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
index 57721f5d31ba..e298400bf25a 100644
--- a/include/uapi/linux/ntsync.h
+++ b/include/uapi/linux/ntsync.h
@@ -55,5 +55,6 @@ struct ntsync_wait_args {
#define NTSYNC_IOC_EVENT_SET _IOR ('N', 0x88, __u32)
#define NTSYNC_IOC_EVENT_RESET _IOR ('N', 0x89, __u32)
#define NTSYNC_IOC_EVENT_PULSE _IOR ('N', 0x8a, __u32)
+#define NTSYNC_IOC_SEM_READ _IOR ('N', 0x8b, struct ntsync_sem_args)
#endif
--
2.43.0
^ permalink raw reply related
* [PATCH v3 16/30] ntsync: Introduce alertable waits.
From: Elizabeth Figura @ 2024-03-29 0:06 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
NT waits can optionally be made "alertable". This is a special channel for
thread wakeup that is mildly similar to SIGIO. A thread has an internal single
bit of "alerted" state, and if a thread is alerted while an alertable wait, the
wait will return a special value, consume the "alerted" state, and will not
consume any of its objects.
Alerts are implemented using events; the user-space NT emulator is expected to
create an internal ntsync event for each thread and pass that event to wait
functions.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
drivers/misc/ntsync.c | 68 ++++++++++++++++++++++++++++++++-----
include/uapi/linux/ntsync.h | 2 +-
2 files changed, 60 insertions(+), 10 deletions(-)
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index a03c6fceb518..19fd70ac3f50 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -819,25 +819,32 @@ static int setup_wait(struct ntsync_device *dev,
const struct ntsync_wait_args *args, bool all,
struct ntsync_q **ret_q)
{
+ int fds[NTSYNC_MAX_WAIT_COUNT + 1];
const __u32 count = args->count;
- int fds[NTSYNC_MAX_WAIT_COUNT];
struct ntsync_q *q;
+ __u32 total_count;
__u32 i, j;
if (!args->owner)
return -EINVAL;
- if (args->pad || args->pad2 || (args->flags & ~NTSYNC_WAIT_REALTIME))
+ if (args->pad || (args->flags & ~NTSYNC_WAIT_REALTIME))
return -EINVAL;
if (args->count > NTSYNC_MAX_WAIT_COUNT)
return -EINVAL;
+ total_count = count;
+ if (args->alert)
+ total_count++;
+
if (copy_from_user(fds, u64_to_user_ptr(args->objs),
array_size(count, sizeof(*fds))))
return -EFAULT;
+ if (args->alert)
+ fds[count] = args->alert;
- q = kmalloc(struct_size(q, entries, count), GFP_KERNEL);
+ q = kmalloc(struct_size(q, entries, total_count), GFP_KERNEL);
if (!q)
return -ENOMEM;
q->task = current;
@@ -847,7 +854,7 @@ static int setup_wait(struct ntsync_device *dev,
q->ownerdead = false;
q->count = count;
- for (i = 0; i < count; i++) {
+ for (i = 0; i < total_count; i++) {
struct ntsync_q_entry *entry = &q->entries[i];
struct ntsync_obj *obj = get_obj(dev, fds[i]);
@@ -897,9 +904,9 @@ static void try_wake_any_obj(struct ntsync_obj *obj)
static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
{
struct ntsync_wait_args args;
+ __u32 i, total_count;
struct ntsync_q *q;
int signaled;
- __u32 i;
int ret;
if (copy_from_user(&args, argp, sizeof(args)))
@@ -909,9 +916,13 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
if (ret < 0)
return ret;
+ total_count = args.count;
+ if (args.alert)
+ total_count++;
+
/* queue ourselves */
- for (i = 0; i < args.count; i++) {
+ for (i = 0; i < total_count; i++) {
struct ntsync_q_entry *entry = &q->entries[i];
struct ntsync_obj *obj = entry->obj;
@@ -920,9 +931,15 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
spin_unlock(&obj->lock);
}
- /* check if we are already signaled */
+ /*
+ * Check if we are already signaled.
+ *
+ * Note that the API requires that normal objects are checked before
+ * the alert event. Hence we queue the alert event last, and check
+ * objects in order.
+ */
- for (i = 0; i < args.count; i++) {
+ for (i = 0; i < total_count; i++) {
struct ntsync_obj *obj = q->entries[i].obj;
if (atomic_read(&q->signaled) != -1)
@@ -939,7 +956,7 @@ static int ntsync_wait_any(struct ntsync_device *dev, void __user *argp)
/* and finally, unqueue */
- for (i = 0; i < args.count; i++) {
+ for (i = 0; i < total_count; i++) {
struct ntsync_q_entry *entry = &q->entries[i];
struct ntsync_obj *obj = entry->obj;
@@ -999,6 +1016,14 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
*/
list_add_tail(&entry->node, &obj->all_waiters);
}
+ if (args.alert) {
+ struct ntsync_q_entry *entry = &q->entries[args.count];
+ struct ntsync_obj *obj = entry->obj;
+
+ spin_lock_nest_lock(&obj->lock, &dev->wait_all_lock);
+ list_add_tail(&entry->node, &obj->any_waiters);
+ spin_unlock(&obj->lock);
+ }
/* check if we are already signaled */
@@ -1006,6 +1031,21 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
spin_unlock(&dev->wait_all_lock);
+ /*
+ * Check if the alert event is signaled, making sure to do so only
+ * after checking if the other objects are signaled.
+ */
+
+ if (args.alert) {
+ struct ntsync_obj *obj = q->entries[args.count].obj;
+
+ if (atomic_read(&q->signaled) == -1) {
+ spin_lock(&obj->lock);
+ try_wake_any_obj(obj);
+ spin_unlock(&obj->lock);
+ }
+ }
+
/* sleep */
ret = ntsync_schedule(q, &args);
@@ -1028,6 +1068,16 @@ static int ntsync_wait_all(struct ntsync_device *dev, void __user *argp)
put_obj(obj);
}
+ if (args.alert) {
+ struct ntsync_q_entry *entry = &q->entries[args.count];
+ struct ntsync_obj *obj = entry->obj;
+
+ spin_lock_nest_lock(&obj->lock, &dev->wait_all_lock);
+ list_del(&entry->node);
+ spin_unlock(&obj->lock);
+
+ put_obj(obj);
+ }
spin_unlock(&dev->wait_all_lock);
diff --git a/include/uapi/linux/ntsync.h b/include/uapi/linux/ntsync.h
index 80f36de46a75..f21dbac42164 100644
--- a/include/uapi/linux/ntsync.h
+++ b/include/uapi/linux/ntsync.h
@@ -37,8 +37,8 @@ struct ntsync_wait_args {
__u32 owner;
__u32 index;
__u32 flags;
+ __u32 alert;
__u32 pad;
- __u32 pad2;
};
#define NTSYNC_MAX_WAIT_COUNT 64
--
2.43.0
^ permalink raw reply related
* [PATCH v3 01/30] ntsync: Introduce the ntsync driver and character device.
From: Elizabeth Figura @ 2024-03-29 0:05 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
In-Reply-To: <20240329000621.148791-1-zfigura@codeweavers.com>
ntsync uses a misc device as the simplest and least intrusive uAPI interface.
Each file description on the device represents an isolated NT instance, intended
to correspond to a single NT virtual machine.
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
---
drivers/misc/Kconfig | 11 +++++++++
drivers/misc/Makefile | 1 +
drivers/misc/ntsync.c | 52 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 64 insertions(+)
create mode 100644 drivers/misc/ntsync.c
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4fb291f0bf7c..801ed229ed7d 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -506,6 +506,17 @@ config OPEN_DICE
If unsure, say N.
+config NTSYNC
+ tristate "NT synchronization primitive emulation"
+ help
+ This module provides kernel support for emulation of Windows NT
+ synchronization primitives. It is not a hardware driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ntsync.
+
+ If unsure, say N.
+
config VCPU_STALL_DETECTOR
tristate "Guest vCPU stall detector"
depends on OF && HAS_IOMEM
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index ea6ea5bbbc9c..153a3f4837e8 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_PVPANIC) += pvpanic/
obj-$(CONFIG_UACCE) += uacce/
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
+obj-$(CONFIG_NTSYNC) += ntsync.o
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
obj-$(CONFIG_OPEN_DICE) += open-dice.o
obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
new file mode 100644
index 000000000000..bd76e653d83e
--- /dev/null
+++ b/drivers/misc/ntsync.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ntsync.c - Kernel driver for NT synchronization primitives
+ *
+ * Copyright (C) 2024 Elizabeth Figura <zfigura@codeweavers.com>
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+
+#define NTSYNC_NAME "ntsync"
+
+static int ntsync_char_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+static int ntsync_char_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static long ntsync_char_ioctl(struct file *file, unsigned int cmd,
+ unsigned long parm)
+{
+ switch (cmd) {
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static const struct file_operations ntsync_fops = {
+ .owner = THIS_MODULE,
+ .open = ntsync_char_open,
+ .release = ntsync_char_release,
+ .unlocked_ioctl = ntsync_char_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice ntsync_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = NTSYNC_NAME,
+ .fops = &ntsync_fops,
+};
+
+module_misc_device(ntsync_misc);
+
+MODULE_AUTHOR("Elizabeth Figura <zfigura@codeweavers.com>");
+MODULE_DESCRIPTION("Kernel driver for NT synchronization primitives");
+MODULE_LICENSE("GPL");
--
2.43.0
^ permalink raw reply related
* [PATCH v3 00/30] NT synchronization primitive driver
From: Elizabeth Figura @ 2024-03-29 0:05 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel, linux-api, wine-devel, André Almeida,
Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra, Andy Lutomirski,
linux-doc, linux-kselftest, Randy Dunlap, Elizabeth Figura
This patch series introduces a new char misc driver, /dev/ntsync, which is used
to implement Windows NT synchronization primitives.
== Background ==
The Wine project emulates the Windows API in user space. One particular part of
that API, namely the NT synchronization primitives, have historically been
implemented via RPC to a dedicated "kernel" process. However, more recent
applications use these APIs more strenuously, and the overhead of RPC has become
a bottleneck.
The NT synchronization APIs are too complex to implement on top of existing
primitives without sacrificing correctness. Certain operations, such as
NtPulseEvent() or the "wait-for-all" mode of NtWaitForMultipleObjects(), require
direct control over the underlying wait queue, and implementing a wait queue
sufficiently robust for Wine in user space is not possible. This proposed
driver, therefore, implements the problematic interfaces directly in the Linux
kernel.
This driver was presented at Linux Plumbers Conference 2023. For those further
interested in the history of synchronization in Wine and past attempts to solve
this problem in user space, a recording of the presentation can be viewed here:
https://www.youtube.com/watch?v=NjU4nyWyhU8
== Performance ==
The gain in performance varies wildly depending on the application in question
and the user's hardware. For some games NT synchronization is not a bottleneck
and no change can be observed, but for others frame rate improvements of 50 to
150 percent are not atypical. The following table lists frame rate measurements
from a variety of games on a variety of hardware, taken by users Dmitry
Skvortsov, FuzzyQuils, OnMars, and myself:
Game Upstream ntsync improvement
===========================================================================
Anger Foot 69 99 43%
Call of Juarez 99.8 224.1 125%
Dirt 3 110.6 860.7 678%
Forza Horizon 5 108 160 48%
Lara Croft: Temple of Osiris 141 326 131%
Metro 2033 164.4 199.2 21%
Resident Evil 2 26 77 196%
The Crew 26 51 96%
Tiny Tina's Wonderlands 130 360 177%
Total War Saga: Troy 109 146 34%
===========================================================================
== Patches ==
The intended semantics of the patches are broadly intended to match those of the
corresponding Windows functions. For those not already familiar with the Windows
functions (or their undocumented behaviour), patch 31/31 provides a detailed
specification, and individual patches also include a brief description of the
API they are implementing.
The patches making use of this driver in Wine can be retrieved or browsed here:
https://repo.or.cz/wine/zf.git/shortlog/refs/heads/ntsync5
== Implementation ==
Some aspects of the implementation may deserve particular comment:
* In the interest of performance, each object is governed only by a single
spinlock. However, NTSYNC_IOC_WAIT_ALL requires that the state of multiple
objects be changed as a single atomic operation. In order to achieve this, we
first take a device-wide lock ("wait_all_lock") any time we are going to lock
more than one object at a time.
The maximum number of objects that can be used in a vectored wait, and
therefore the maximum that can be locked simultaneously, is 64. This number is
NT's own limit.
The acquisition of multiple spinlocks will degrade performance. This is a
conscious choice, however. Wait-for-all is known to be a very rare operation
in practice, especially with counts that approach the maximum, and it is the
intent of the ntsync driver to optimize wait-for-any at the expense of
wait-for-all as much as possible.
* NT mutexes are tied to their threads on an OS level, and the kernel includes
builtin support for "robust" mutexes. In order to keep the ntsync driver
self-contained and avoid touching more code than necessary, it does not hook
into task exit nor use pids.
Instead, the user space emulator is expected to manage thread IDs and pass
them as an argument to any relevant functions; this is the "owner" field of
ntsync_wait_args and ntsync_mutex_args.
When the emulator detects that a thread dies, it should therefore call
NTSYNC_IOC_MUTEX_KILL on any open mutexes.
* ntsync is module-capable mostly because there was nothing preventing it, and
because it aided development. It is not a hard requirement, though.
== Previous versions ==
Changes from v2:
* Check the result of fget() for NULL.
* Squash patch 31 (introducing the NTSYNC_WAIT_REALTIME flag) into patch 4, per
Arnd Bergmann.
* Use atomic_try_cmpxchg() instead of atomic_cmpxchg(), per off-list review from
Uros Bizjak.
* Link to v2: https://lore.kernel.org/lkml/20240219223833.95710-1-zfigura@codeweavers.com/
* Link to v1: https://lore.kernel.org/lkml/20240214233645.9273-1-zfigura@codeweavers.com/
* Link to RFC v2: https://lore.kernel.org/lkml/20240131021356.10322-1-zfigura@codeweavers.com/
* Link to RFC v1: https://lore.kernel.org/lkml/20240124004028.16826-1-zfigura@codeweavers.com/
Elizabeth Figura (30):
ntsync: Introduce the ntsync driver and character device.
ntsync: Introduce NTSYNC_IOC_CREATE_SEM.
ntsync: Introduce NTSYNC_IOC_SEM_POST.
ntsync: Introduce NTSYNC_IOC_WAIT_ANY.
ntsync: Introduce NTSYNC_IOC_WAIT_ALL.
ntsync: Introduce NTSYNC_IOC_CREATE_MUTEX.
ntsync: Introduce NTSYNC_IOC_MUTEX_UNLOCK.
ntsync: Introduce NTSYNC_IOC_MUTEX_KILL.
ntsync: Introduce NTSYNC_IOC_CREATE_EVENT.
ntsync: Introduce NTSYNC_IOC_EVENT_SET.
ntsync: Introduce NTSYNC_IOC_EVENT_RESET.
ntsync: Introduce NTSYNC_IOC_EVENT_PULSE.
ntsync: Introduce NTSYNC_IOC_SEM_READ.
ntsync: Introduce NTSYNC_IOC_MUTEX_READ.
ntsync: Introduce NTSYNC_IOC_EVENT_READ.
ntsync: Introduce alertable waits.
selftests: ntsync: Add some tests for semaphore state.
selftests: ntsync: Add some tests for mutex state.
selftests: ntsync: Add some tests for NTSYNC_IOC_WAIT_ANY.
selftests: ntsync: Add some tests for NTSYNC_IOC_WAIT_ALL.
selftests: ntsync: Add some tests for wakeup signaling with
WINESYNC_IOC_WAIT_ANY.
selftests: ntsync: Add some tests for wakeup signaling with
WINESYNC_IOC_WAIT_ALL.
selftests: ntsync: Add some tests for manual-reset event state.
selftests: ntsync: Add some tests for auto-reset event state.
selftests: ntsync: Add some tests for wakeup signaling with events.
selftests: ntsync: Add tests for alertable waits.
selftests: ntsync: Add some tests for wakeup signaling via alerts.
selftests: ntsync: Add a stress test for contended waits.
maintainers: Add an entry for ntsync.
docs: ntsync: Add documentation for the ntsync uAPI.
Documentation/userspace-api/index.rst | 1 +
.../userspace-api/ioctl/ioctl-number.rst | 2 +
Documentation/userspace-api/ntsync.rst | 399 +++++
MAINTAINERS | 9 +
drivers/misc/Kconfig | 11 +
drivers/misc/Makefile | 1 +
drivers/misc/ntsync.c | 1166 ++++++++++++++
include/uapi/linux/ntsync.h | 62 +
tools/testing/selftests/Makefile | 1 +
.../testing/selftests/drivers/ntsync/Makefile | 8 +
tools/testing/selftests/drivers/ntsync/config | 1 +
.../testing/selftests/drivers/ntsync/ntsync.c | 1407 +++++++++++++++++
12 files changed, 3068 insertions(+)
create mode 100644 Documentation/userspace-api/ntsync.rst
create mode 100644 drivers/misc/ntsync.c
create mode 100644 include/uapi/linux/ntsync.h
create mode 100644 tools/testing/selftests/drivers/ntsync/Makefile
create mode 100644 tools/testing/selftests/drivers/ntsync/config
create mode 100644 tools/testing/selftests/drivers/ntsync/ntsync.c
base-commit: 4cece764965020c22cff7665b18a012006359095
--
2.43.0
^ permalink raw reply
* Re: [PATCH v3 17/30] selftests: ntsync: Add some tests for semaphore state.
From: Muhammad Usama Anjum @ 2024-03-29 20:07 UTC (permalink / raw)
To: Elizabeth Figura, Arnd Bergmann, Greg Kroah-Hartman,
Jonathan Corbet, Shuah Khan
Cc: Muhammad Usama Anjum, linux-kernel, linux-api, wine-devel,
André Almeida, Wolfram Sang, Arkadiusz Hiler, Peter Zijlstra,
Andy Lutomirski, linux-doc, linux-kselftest, Randy Dunlap
In-Reply-To: <20240329000621.148791-18-zfigura@codeweavers.com>
On 3/29/24 5:06 AM, Elizabeth Figura wrote:
> Wine has tests for its synchronization primitives, but these are more accessible
> to kernel developers, and also allow us to test some edge cases that Wine does
> not care about.
>
> This patch adds tests for semaphore-specific ioctls NTSYNC_IOC_SEM_POST and
> NTSYNC_IOC_SEM_READ, and waiting on semaphores.
>
> Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
> ---
> tools/testing/selftests/Makefile | 1 +
> .../testing/selftests/drivers/ntsync/Makefile | 8 +
> tools/testing/selftests/drivers/ntsync/config | 1 +
> .../testing/selftests/drivers/ntsync/ntsync.c | 149 ++++++++++++++++++
Please add generated binary objects in .gitignore file.
> 4 files changed, 159 insertions(+)
> create mode 100644 tools/testing/selftests/drivers/ntsync/Makefile
> create mode 100644 tools/testing/selftests/drivers/ntsync/config
> create mode 100644 tools/testing/selftests/drivers/ntsync/ntsync.c
>
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index e1504833654d..6f95206325e1 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -16,6 +16,7 @@ TARGETS += damon
> TARGETS += devices
> TARGETS += dmabuf-heaps
> TARGETS += drivers/dma-buf
> +TARGETS += drivers/ntsync
> TARGETS += drivers/s390x/uvdevice
> TARGETS += drivers/net/bonding
> TARGETS += drivers/net/team
> diff --git a/tools/testing/selftests/drivers/ntsync/Makefile b/tools/testing/selftests/drivers/ntsync/Makefile
> new file mode 100644
> index 000000000000..a34da5ccacf0
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/ntsync/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-LICENSE-IDENTIFIER: GPL-2.0-only
> +TEST_GEN_PROGS := ntsync
> +
> +top_srcdir =../../../../..
> +CFLAGS += -I$(top_srcdir)/usr/include
Please use KHDR_INCLUDES instead of specifying include path.
> +LDLIBS += -lpthread
> +
> +include ../../lib.mk
> diff --git a/tools/testing/selftests/drivers/ntsync/config b/tools/testing/selftests/drivers/ntsync/config
> new file mode 100644
> index 000000000000..60539c826d06
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/ntsync/config
> @@ -0,0 +1 @@
> +CONFIG_WINESYNC=y
> diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c
> new file mode 100644
> index 000000000000..1e145c6dfded
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/ntsync/ntsync.c
> @@ -0,0 +1,149 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Various unit tests for the "ntsync" synchronization primitive driver.
> + *
> + * Copyright (C) 2021-2022 Elizabeth Figura <zfigura@codeweavers.com>
> + */
> +
> +#define _GNU_SOURCE
> +#include <sys/ioctl.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <time.h>
> +#include <pthread.h>
> +#include <linux/ntsync.h>
> +#include "../../kselftest_harness.h"
> +
> +static int read_sem_state(int sem, __u32 *count, __u32 *max)
> +{
> + struct ntsync_sem_args args;
> + int ret;
> +
> + memset(&args, 0xcc, sizeof(args));
> + ret = ioctl(sem, NTSYNC_IOC_SEM_READ, &args);
> + *count = args.count;
> + *max = args.max;
> + return ret;
> +}
> +
> +#define check_sem_state(sem, count, max) \
> + ({ \
> + __u32 __count, __max; \
> + int ret = read_sem_state((sem), &__count, &__max); \
> + EXPECT_EQ(0, ret); \
> + EXPECT_EQ((count), __count); \
> + EXPECT_EQ((max), __max); \
> + })
> +
> +static int post_sem(int sem, __u32 *count)
> +{
> + return ioctl(sem, NTSYNC_IOC_SEM_POST, count);
> +}
> +
> +static int wait_any(int fd, __u32 count, const int *objs, __u32 owner, __u32 *index)
> +{
> + struct ntsync_wait_args args = {0};
> + struct timespec timeout;
> + int ret;
> +
> + clock_gettime(CLOCK_MONOTONIC, &timeout);
> +
> + args.timeout = timeout.tv_sec * 1000000000 + timeout.tv_nsec;
> + args.count = count;
> + args.objs = (uintptr_t)objs;
> + args.owner = owner;
> + args.index = 0xdeadbeef;
> + ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &args);
> + *index = args.index;
> + return ret;
> +}
> +
> +TEST(semaphore_state)
> +{
> + struct ntsync_sem_args sem_args;
> + struct timespec timeout;
> + __u32 count, index;
> + int fd, ret, sem;
> +
> + clock_gettime(CLOCK_MONOTONIC, &timeout);
> +
> + fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY);
> + ASSERT_LE(0, fd);
> +
> + sem_args.count = 3;
> + sem_args.max = 2;
> + sem_args.sem = 0xdeadbeef;
> + ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
> + EXPECT_EQ(-1, ret);
> + EXPECT_EQ(EINVAL, errno);
> +
> + sem_args.count = 2;
> + sem_args.max = 2;
> + sem_args.sem = 0xdeadbeef;
> + ret = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args);
> + EXPECT_EQ(0, ret);
> + EXPECT_NE(0xdeadbeef, sem_args.sem);
> + sem = sem_args.sem;
> + check_sem_state(sem, 2, 2);
> +
> + count = 0;
> + ret = post_sem(sem, &count);
> + EXPECT_EQ(0, ret);
> + EXPECT_EQ(2, count);
> + check_sem_state(sem, 2, 2);
> +
> + count = 1;
> + ret = post_sem(sem, &count);
> + EXPECT_EQ(-1, ret);
> + EXPECT_EQ(EOVERFLOW, errno);
> + check_sem_state(sem, 2, 2);
> +
> + ret = wait_any(fd, 1, &sem, 123, &index);
> + EXPECT_EQ(0, ret);
> + EXPECT_EQ(0, index);
> + check_sem_state(sem, 1, 2);
> +
> + ret = wait_any(fd, 1, &sem, 123, &index);
> + EXPECT_EQ(0, ret);
> + EXPECT_EQ(0, index);
> + check_sem_state(sem, 0, 2);
> +
> + ret = wait_any(fd, 1, &sem, 123, &index);
> + EXPECT_EQ(-1, ret);
> + EXPECT_EQ(ETIMEDOUT, errno);
> +
> + count = 3;
> + ret = post_sem(sem, &count);
> + EXPECT_EQ(-1, ret);
> + EXPECT_EQ(EOVERFLOW, errno);
> + check_sem_state(sem, 0, 2);
> +
> + count = 2;
> + ret = post_sem(sem, &count);
> + EXPECT_EQ(0, ret);
> + EXPECT_EQ(0, count);
> + check_sem_state(sem, 2, 2);
> +
> + ret = wait_any(fd, 1, &sem, 123, &index);
> + EXPECT_EQ(0, ret);
> + ret = wait_any(fd, 1, &sem, 123, &index);
> + EXPECT_EQ(0, ret);
> +
> + count = 1;
> + ret = post_sem(sem, &count);
> + EXPECT_EQ(0, ret);
> + EXPECT_EQ(0, count);
> + check_sem_state(sem, 1, 2);
> +
> + count = ~0u;
> + ret = post_sem(sem, &count);
> + EXPECT_EQ(-1, ret);
> + EXPECT_EQ(EOVERFLOW, errno);
> + check_sem_state(sem, 1, 2);
> +
> + close(sem);
> +
> + close(fd);
> +}
> +
> +TEST_HARNESS_MAIN
--
BR,
Muhammad Usama Anjum
^ permalink raw reply
* Re: RFC: Restricting userspace interfaces for CXL fabric management
From: Sreenivas Bagalkote @ 2024-04-01 16:51 UTC (permalink / raw)
To: Jonathan Cameron, Williams, Dan J, Natu, Mahesh
Cc: linux-cxl, Brett Henning, Harold Johnson, Sumanesh Samanta,
linux-kernel, Davidlohr Bueso, Dave Jiang, Alison Schofield,
Vishal Verma, Ira Weiny, linuxarm, linux-api, Lorenzo Pieralisi,
Ariel.Sibley
In-Reply-To: <CACX_a4U_==8pVqrqyrKeUhMuvvcP53g4irkwauFK3Edxbxp9NA@mail.gmail.com>
[-- Attachment #1.1: Type: text/plain, Size: 19024 bytes --]
Hello Dan, Jonathan -
> >
> > 'Maybe' if you were to publish a specification for those particular
> > vendor defined commands, it might be fine to add them to the allow list
> > for the switch-cci.
> >
>
> Your proposal sounds reasonable. I will let you all experts figure out
how to support the vendor-defined commands. CXL spec has them for a reason
and they need to be supported.
Please confirm that you will support an "allow list" of the vendor-defined
commands. We will publish it once you confirm.
Thank you
Sreeni
Sreenivas Bagalkote <Sreenivas.Bagalkote@broadcom.com>
Product Planning & Management
Broadcom Datacenter Solutions Group
On Fri, Mar 22, 2024 at 6:54 PM Sreenivas Bagalkote <
sreenivas.bagalkote@broadcom.com> wrote:
> Jonathan,
>
> >
> > What is the use case? My understanding so far is that clouds and
> > similar sometimes use an in band path but it would be from a management
> > only host, not a general purpose host running other software
> >
>
> The overwhelming majority of the PCIe switches get deployed in a single
> server. Typically four to eight switches are connected to two or more root
> complexes in one or two CPUs. The deployment scenario you have in mind -
> multiple physical hosts running general workloads and a management-only
> host - exists. But it is insignificant.
>
> >
> > For telemetry(subject to any odd corners like commands that might lock
> > the interface up for a long time, which we've seen with commands in the
> > Spec!) I don't see any problem supporting those on all host software.
> > They should be non destructive to other hosts etc.
> >
>
> Thank you. As you do this, please keep in mind that your concern about not
> affecting "other" hosts is theoretically valid but doesn't exist in the
> real world beyond science experiments. If there are real-world deployments,
> they are insignificant. I urge you all to make your stuff work with 99.99%
> of the deployments.
>
> >
> > 'Maybe' if you were to publish a specification for those particular
> > vendor defined commands, it might be fine to add them to the allow list
> > for the switch-cci.
> >
>
> Your proposal sounds reasonable. I will let you all experts figure out how
> to support the vendor-defined commands. CXL spec has them for a reason and
> they need to be supported.
>
> Sreeni
>
> On Fri, Mar 22, 2024 at 2:32 AM Jonathan Cameron <
> Jonathan.Cameron@huawei.com> wrote:
>
>> On Thu, 21 Mar 2024 14:41:00 -0700
>> Sreenivas Bagalkote <sreenivas.bagalkote@broadcom.com> wrote:
>>
>> > Thank you for kicking off this discussion, Jonathan.
>>
>> Hi Sreenivas,
>>
>> >
>> > We need guidance from the community.
>> >
>> > 1. Datacenter customers must be able to manage PCIe switches in-band.
>>
>> What is the use case? My understanding so far is that clouds and
>> similar sometimes use an in band path but it would be from a management
>> only host, not a general purpose host running other software. Sure
>> that control host just connects to a different upstream port so, from
>> a switch point of view, it's the same as any other host. From a host
>> software point of view it's not running general cloud workloads or
>> (at least in most cases) a general purpose OS distribution.
>>
>> This is the key question behind this discussion.
>>
>> > 2. Management of switches includes getting health, performance, and
>> error
>> > telemetry.
>>
>> For telemetry(subject to any odd corners like commands that might lock
>> the interface up for a long time, which we've seen with commands in the
>> Spec!) I don't see any problem supporting those on all host software.
>> They should be non destructive to other hosts etc.
>>
>> > 3. These telemetry functions are not yet part of the CXL standard
>>
>> Ok, so this we should try to pin down the boundaries around this.
>> The thread linked below lays out the reasoning behind a general rule
>> of not accepting vendor defined commands, but perhaps there are routes
>> to answer some of those concerns.
>>
>> 'Maybe' if you were to publish a specification for those particular
>> vendor defined commands, it might be fine to add them to the allow list
>> for the switch-cci. Key here is that Broadcom would be committing to not
>> using those particular opcodes from the vendor space for anything else
>> in the future (so we could match on VID + opcode). This is similar to
>> some DVSEC usage in PCIe (and why DVSEC is different from VSEC).
>>
>> Effectively you'd be publishing an additional specification building on
>> CXL.
>> Those are expected to surface anyway from various standards orgs - should
>> we treat a company published one differently? I don't see why.
>> Exactly how this would work might take some figuring out (in main code,
>> separate driver module etc?)
>>
>> That specification would be expected to provide a similar level of detail
>> to CXL spec defined commands (ideally the less vague ones, but meh, up to
>> you as long as any side effects are clearly documented!)
>>
>> Speaking for myself, I'd consider this approach.
>> Particularly true if I see clear effort in the standards org to push
>> these into future specifications as that shows broadcom are trying to
>> enhance the ecosystems.
>>
>>
>> > 4. We built the CCI mailboxes into our PCIe switches per CXL spec and
>> > developed our management scheme around them.
>> >
>> > If the Linux community does not allow a CXL spec-compliant switch to be
>> > managed via the CXL spec-defined CCI mailbox, then please guide us on
>> > the right approach. Please tell us how you propose we manage our
>> switches
>> > in-band.
>>
>> The Linux community is fine supporting this in the kernel (the BMC or
>> Fabric Management only host case - option 2 below, so the code will be
>> there)
>> the question here is what advice we offer to the general purpose
>> distributions and what protections we need to put in place to mitigate the
>> 'blast radius' concerns.
>>
>> Jonathan
>> >
>> > Thank you
>> > Sreeni
>> >
>> > On Thu, Mar 21, 2024 at 10:44 AM Jonathan Cameron <
>> > Jonathan.Cameron@huawei.com> wrote:
>> >
>> > > Hi All,
>> > >
>> > > This is has come up in a number of discussions both on list and in
>> private,
>> > > so I wanted to lay out a potential set of rules when deciding whether
>> or
>> > > not
>> > > to provide a user space interface for a particular feature of CXL
>> Fabric
>> > > Management. The intent is to drive discussion, not to simply tell
>> people
>> > > a set of rules. I've brought this to the public lists as it's a Linux
>> > > kernel
>> > > policy discussion, not a standards one.
>> > >
>> > > Whilst I'm writing the RFC this my attempt to summarize a possible
>> > > position rather than necessarily being my personal view.
>> > >
>> > > It's a straw man - shoot at it!
>> > >
>> > > Not everyone in this discussion is familiar with relevant kernel or
>> CXL
>> > > concepts
>> > > so I've provided more info than I normally would.
>> > >
>> > > First some background:
>> > > ======================
>> > >
>> > > CXL has two different types of Fabric. The comments here refer to
>> both, but
>> > > for now the kernel stack is focused on the simpler VCS fabric, not
>> the more
>> > > recent Port Based Routing (PBR) Fabrics. A typical example for 2 hosts
>> > > connected to a common switch looks something like:
>> > >
>> > > ________________ _______________
>> > > | | | | Hosts - each sees
>> > > | HOST A | | HOST B | a PCIe style tree
>> > > | | | | but from a fabric
>> > > config
>> > > | |Root Port| | | |Root Port| | point of view
>> it's more
>> > > -------|-------- -------|------- complex.
>> > > | |
>> > > | |
>> > > _______|______________________________|________
>> > > | USP (SW-CCI) USP | Switch can have
>> lots of
>> > > | | | | Upstream Ports.
>> Each one
>> > > | ____|________ _______|______ | has a virtual
>> hierarchy.
>> > > | | | | | |
>> > > | vPPB vPPB vPPB vPPB| There are virtual
>> > > | x | | | | "downstream
>> > > ports."(vPPBs)
>> > > | \ / / | That can be bound
>> to real
>> > > | \ / / | downstream ports.
>> > > | \ / / |
>> > > | \ / / | Multi Logical
>> Devices are
>> > > | DSP0 DSP1 DSP 2 | support more than
>> one
>> > > vPPB
>> > > ------------------------------------------------ bound to a single
>> > > physical
>> > > | | | DSP (transactions
>> are
>> > > tagged
>> > > | | | with an LD-ID)
>> > > SLD0 MLD0 SLD1
>> > >
>> > > Some typical fabric management activities:
>> > > 1) Bind/Unbind vPPB to physical DSP (Results in hotplug / unplug
>> events)
>> > > 2) Access config space or BAR space of End Points below the switch.
>> > > 3) Tunneling messages through to devices downstream (e.g Dynamic
>> Capacity
>> > > Forced Remove that will blow away some memory even if a host is
>> using
>> > > it).
>> > > 4) Non destructive stuff like status read back.
>> > >
>> > > Given the hosts may be using the Type 3 hosted memory (either Single
>> > > Logical
>> > > Device - SLD, or an LD on a Multi logical Device - MLD) as normal
>> memory,
>> > > unbinding a device in use can result in the memory access from a
>> > > different host being removed. The 'blast radius' is perhaps a rack of
>> > > servers. This discussion applies equally to FM-API commands sent to
>> Multi
>> > > Head Devices (see CXL r3.1).
>> > >
>> > > The Fabric Management actions are done using the CXL spec defined
>> Fabric
>> > > Management API, (FM-API) which is transported over various means
>> including
>> > > OoB MCTP over your favourite transport (I2C, PCIe-VDM...) or via
>> normal
>> > > PCIe read/write to a Switch-CCI. A Switch-CCI is mailbox in PCI BAR
>> > > space on a function found alongside one of the switch upstream ports;
>> > > this mailbox is very similar to the MMPT definition found in PCIe
>> r6.2.
>> > >
>> > > In many cases this switch CCI / MCTP connection is used by a BMC
>> rather
>> > > than a normal host, but there have been some questions raised about
>> whether
>> > > a general purpose server OS would have a valid reason to use this
>> interface
>> > > (beyond debug and testing) to configure the switch or an MHD.
>> > >
>> > > If people have a use case for this, please reply to this thread to
>> give
>> > > more details.
>> > >
>> > > The most recently posted CXL Switch-CCI support only provided the RAW
>> CXL
>> > > command IOCTL interface that is already available for Type 3 memory
>> > > devices.
>> > > That allows for unfettered control of the switch but, because it is
>> > > extremely easy to shoot yourself in the foot and cause unsolvable bug
>> > > reports,
>> > > it taints the kernel. There have been several requests to provide this
>> > > interface
>> > > without the taint for these switch configuration mailboxes.
>> > >
>> > > Last posted series:
>> > >
>> > >
>> https://lore.kernel.org/all/20231016125323.18318-1-Jonathan.Cameron@huawei.com/
>> > > Note there are unrelated reasons why that code hasn't been updated
>> since
>> > > v6.6 time,
>> > > but I am planning to get back to it shortly.
>> > >
>> > > Similar issues will occur for other uses of PCIe MMPT (new mailbox in
>> PCI
>> > > that
>> > > sometimes is used for similarly destructive activity such as PLDM
>> based
>> > > firmware update).
>> > >
>> > >
>> > > On to the proposed rules:
>> > >
>> > > 1) Kernel space use of the various mailboxes, or filtered controls
>> from
>> > > user space.
>> > >
>> > >
>> ==================================================================================
>> > >
>> > > Absolutely fine - no one worries about this, but the mediated traffic
>> will
>> > > be filtered for potentially destructive side effects. E.g. it will
>> reject
>> > > attempts to change anything routing related if the kernel either
>> knows a
>> > > host is
>> > > using memory that will be blown away, or has no way to know (so
>> affecting
>> > > routing to another host). This includes blocking 'all' vendor defined
>> > > messages as we have no idea what the do. Note this means the kernel
>> has
>> > > an allow list and new commands are not initially allowed.
>> > >
>> > > This isn't currently enabled for Switch CCIs because they are only
>> really
>> > > interesting if the potentially destructive stuff is available (an
>> earlier
>> > > version did enable query commands, but it wasn't particularly useful
>> to
>> > > know what your switch could do but not be allowed to do any of it).
>> > > If you take a MMPT usecase of PLDM firmware update, the filtering
>> would
>> > > check that the device was in a state where a firmware update won't rip
>> > > memory out from under a host, which would be messy if that host is
>> > > doing the update.
>> > >
>> > > 2) Unfiltered userspace use of mailbox for Fabric Management - BMC
>> kernels
>> > >
>> ==========================================================================
>> > >
>> > > (This would just be a kernel option that we'd advise normal server
>> > > distributions not to turn on. Would be enabled by openBMC etc)
>> > >
>> > > This is fine - there is some work to do, but the switch-cci PCI driver
>> > > will hopefully be ready for upstream merge soon. There is no
>> filtering of
>> > > accesses. Think of this as similar to all the damage you can do via
>> > > MCTP from a BMC. Similarly it is likely that much of the complexity
>> > > of the actual commands will be left to user space tooling:
>> > > https://gitlab.com/jic23/cxl-fmapi-tests has some test examples.
>> > >
>> > > Whether Kconfig help text is strong enough to ensure this only gets
>> > > enabled for BMC targeted distros is an open question we can address
>> > > alongside an updated patch set.
>> > >
>> > > (On to the one that the "debate" is about)
>> > >
>> > > 3) Unfiltered user space use of mailbox for Fabric Management - Distro
>> > > kernels
>> > >
>> > >
>> =============================================================================
>> > > (General purpose Linux Server Distro (Redhat, Suse etc))
>> > >
>> > > This is equivalent of RAW command support on CXL Type 3 memory
>> devices.
>> > > You can enable those in a distro kernel build despite the scary config
>> > > help text, but if you use it the kernel is tainted. The result
>> > > of the taint is to add a flag to bug reports and print a big message
>> to say
>> > > that you've used a feature that might result in you shooting yourself
>> > > in the foot.
>> > >
>> > > The taint is there because software is not at first written to deal
>> with
>> > > everything that can happen smoothly (e.g. surprise removal) It's hard
>> > > to survive some of these events, so is never on the initial feature
>> list
>> > > for any bus, so this flag is just to indicate we have entered a world
>> > > where almost all bets are off wrt to stability. We might not know
>> what
>> > > a command does so we can't assess the impact (and no one trusts vendor
>> > > commands to report affects right in the Command Effects Log - which
>> > > in theory tells you if a command can result problems).
>> > >
>> > > A concern was raised about GAE/FAST/LDST tables for CXL Fabrics
>> > > (a r3.1 feature) but, as I understand it, these are intended for a
>> > > host to configure and should not have side effects on other hosts?
>> > > My working assumption is that the kernel driver stack will handle
>> > > these (once we catch up with the current feature backlog!) Currently
>> > > we have no visibility of what the OS driver stack for a fabrics will
>> > > actually look like - the spec is just the starting point for that.
>> > > (patches welcome ;)
>> > >
>> > > The various CXL upstream developers and maintainers may have
>> > > differing views of course, but my current understanding is we want
>> > > to support 1 and 2, but are very resistant to 3!
>> > >
>> > > General Notes
>> > > =============
>> > >
>> > > One side aspect of why we really don't like unfiltered userspace
>> access to
>> > > any
>> > > of these devices is that people start building non standard hacks in
>> and we
>> > > lose the ecosystem advantages. Forcing a considered discussion +
>> patches
>> > > to let a particular command be supported, drives standardization.
>> > >
>> > >
>> > >
>> https://lore.kernel.org/linux-cxl/CAPcyv4gDShAYih5iWabKg_eTHhuHm54vEAei8ZkcmHnPp3B0cw@mail.gmail.com/
>> > > provides some history on vendor specific extensions and why in
>> general we
>> > > won't support them upstream.
>> > >
>> > > To address another question raised in an earlier discussion:
>> > > Putting these Fabric Management interfaces behind guard rails of some
>> type
>> > > (e.g. CONFIG_IM_A_BMC_AND_CAN_MAKE_A_MESS) does not encourage the risk
>> > > of non standard interfaces, because we will be even less likely to
>> accept
>> > > those upstream!
>> > >
>> > > If anyone needs more details on any aspect of this please ask.
>> > > There are a lot of things involved and I've only tried to give a
>> fairly
>> > > minimal illustration to drive the discussion. I may well have missed
>> > > something crucial.
>> > >
>> > > Jonathan
>> > >
>> > >
>> >
>>
>>
--
This electronic communication and the information and any files transmitted
with it, or attached to it, are confidential and are intended solely for
the use of the individual or entity to whom it is addressed and may contain
information that is confidential, legally privileged, protected by privacy
laws, or otherwise restricted from disclosure to anyone else. If you are
not the intended recipient or the person responsible for delivering the
e-mail to the intended recipient, you are hereby notified that any use,
copying, distributing, dissemination, forwarding, printing, or copying of
this e-mail is strictly prohibited. If you received this e-mail in error,
please return the e-mail to the sender, delete it from your computer, and
destroy any printed copy of it.
[-- Attachment #1.2: Type: text/html, Size: 25792 bytes --]
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 4230 bytes --]
^ permalink raw reply
* Re: [PATCH v7] posix-timers: add clock_compare system call
From: Thomas Gleixner @ 2024-04-01 20:46 UTC (permalink / raw)
To: Sagi Maimon
Cc: richardcochran, luto, mingo, bp, dave.hansen, x86, hpa, arnd,
geert, peterz, hannes, sohil.mehta, rick.p.edgecombe, nphamcs,
palmer, keescook, legion, mark.rutland, mszeredi, casey, reibax,
davem, brauner, linux-kernel, linux-api, linux-arch, netdev
In-Reply-To: <CAMuE1bF9ioo39_08Eh26X4WOtnvJ1geJ=WRVt5DhU8gEbYJNdA@mail.gmail.com>
Sagi!
On Thu, Mar 28 2024 at 17:40, Sagi Maimon wrote:
> On Sat, Mar 23, 2024 at 2:38 AM Thomas Gleixner <tglx@linutronix.de> wrote:
>> On top this needs an analyis whether any of the gettimex64()
>> implementations does something special instead of invoking the
>> ptp_read_system_prets() and ptp_read_system_postts() helpers as close as
>> possible to the PCH readout, but that's not rocket science either. It's
>> just 21 callbacks to look at.
>>
> I like your suggestion, thanks!
> it is what our user space needs from the kernel and with minimum kernel changes.
> I will write it, test it and upload it with your permission (it is you
> idea after all).
You don't need permission. I made a suggestion and when you are doing the
work I'm not in a position to veto posting it. We have an explicit tag
for that 'Suggested-by:', which only says that someone suggested it to
you, but then you went and implemented it, made sure it works etc.
>> It might also require a new set of variant '3' IOTCLS to make that flag
>> field work, but that's not going to make the change more complex and
>> it's an exercise left to the experts of that IOCTL interface.
>>
> I think that I understand your meaning.
> There is a backward compatibility problem here.
>
> Existing user space application using PTP_SYS_OFFSET_EXTENDED ioctl
> won't have any problems because of the "extoff->rsv[0] ||
> extoff->rsv[1] || extoff->rsv[2]" test, but what about all old user
> space applications using: PTP_SYS_OFFSET ?
So if there is a backwards compability issue with PTP_SYS_OFFSET2, then
you need to introduce PTP_SYS_OFFSET3. The PTP_SYS_*2 variants were
introduced to avoid backwards compatibility issues as well, but
unfortunately that did not address the reserved fields problem for
PTP_SYS_OFFSET2. PTP_SYS_OFFSET_EXTENDED2 should just work, but maybe
the PTP maintainers want a full extension to '3'. Either way is fine.
Thanks,
tglx
^ permalink raw reply
* Re: [PATCH v7] posix-timers: add clock_compare system call
From: Mahesh Bandewar (महेश बंडेवार) @ 2024-04-02 5:42 UTC (permalink / raw)
To: Thomas Gleixner
Cc: Sagi Maimon, richardcochran, luto, mingo, bp, dave.hansen, x86,
hpa, arnd, geert, peterz, hannes, sohil.mehta, rick.p.edgecombe,
nphamcs, palmer, keescook, legion, mark.rutland, mszeredi, casey,
reibax, davem, brauner, linux-kernel, linux-api, linux-arch,
netdev
In-Reply-To: <87o7asdd65.ffs@tglx>
On Mon, Apr 1, 2024 at 1:46 PM Thomas Gleixner <tglx@linutronix.de> wrote:
>
> Sagi!
>
> On Thu, Mar 28 2024 at 17:40, Sagi Maimon wrote:
> > On Sat, Mar 23, 2024 at 2:38 AM Thomas Gleixner <tglx@linutronix.de> wrote:
> >> On top this needs an analyis whether any of the gettimex64()
> >> implementations does something special instead of invoking the
> >> ptp_read_system_prets() and ptp_read_system_postts() helpers as close as
> >> possible to the PCH readout, but that's not rocket science either. It's
> >> just 21 callbacks to look at.
> >>
> > I like your suggestion, thanks!
> > it is what our user space needs from the kernel and with minimum kernel changes.
> > I will write it, test it and upload it with your permission (it is you
> > idea after all).
>
> You don't need permission. I made a suggestion and when you are doing the
> work I'm not in a position to veto posting it. We have an explicit tag
> for that 'Suggested-by:', which only says that someone suggested it to
> you, but then you went and implemented it, made sure it works etc.
>
> >> It might also require a new set of variant '3' IOTCLS to make that flag
> >> field work, but that's not going to make the change more complex and
> >> it's an exercise left to the experts of that IOCTL interface.
> >>
> > I think that I understand your meaning.
> > There is a backward compatibility problem here.
> >
> > Existing user space application using PTP_SYS_OFFSET_EXTENDED ioctl
> > won't have any problems because of the "extoff->rsv[0] ||
> > extoff->rsv[1] || extoff->rsv[2]" test, but what about all old user
> > space applications using: PTP_SYS_OFFSET ?
>
> So if there is a backwards compability issue with PTP_SYS_OFFSET2, then
> you need to introduce PTP_SYS_OFFSET3. The PTP_SYS_*2 variants were
> introduced to avoid backwards compatibility issues as well, but
> unfortunately that did not address the reserved fields problem for
> PTP_SYS_OFFSET2. PTP_SYS_OFFSET_EXTENDED2 should just work, but maybe
> the PTP maintainers want a full extension to '3'. Either way is fine.
>
https://patchwork.kernel.org/project/netdevbpf/patch/20240104212436.3276057-1-maheshb@google.com/
This was my attempt to solve a similar issue with the new ioctl op to
avoid backward compatibility issues. Instead of flags I used the
clockid_t in a similar fashion.
Thanks,
> Thanks,
>
> tglx
>
>
^ permalink raw reply
* Re: [PATCH v7] posix-timers: add clock_compare system call
From: Thomas Gleixner @ 2024-04-02 9:24 UTC (permalink / raw)
To: Mahesh Bandewar (महेश बंडेवार)
Cc: Sagi Maimon, richardcochran, luto, mingo, bp, dave.hansen, x86,
hpa, arnd, geert, peterz, hannes, sohil.mehta, rick.p.edgecombe,
nphamcs, palmer, keescook, legion, mark.rutland, mszeredi, casey,
reibax, davem, brauner, linux-kernel, linux-api, linux-arch,
netdev
In-Reply-To: <CAF2d9jjA8iM1AoPUhQPK62tdd7gPnCnt51f_NMhOAs546rU3dA@mail.gmail.com>
On Mon, Apr 01 2024 at 22:42, Mahesh Bandewar (महेश बंडेवार) wrote:
> On Mon, Apr 1, 2024 at 1:46 PM Thomas Gleixner <tglx@linutronix.de> wrote:
>> So if there is a backwards compability issue with PTP_SYS_OFFSET2, then
>> you need to introduce PTP_SYS_OFFSET3. The PTP_SYS_*2 variants were
>> introduced to avoid backwards compatibility issues as well, but
>> unfortunately that did not address the reserved fields problem for
>> PTP_SYS_OFFSET2. PTP_SYS_OFFSET_EXTENDED2 should just work, but maybe
>> the PTP maintainers want a full extension to '3'. Either way is fine.
>>
> https://patchwork.kernel.org/project/netdevbpf/patch/20240104212436.3276057-1-maheshb@google.com/
>
> This was my attempt to solve a similar issue with the new ioctl op to
> avoid backward compatibility issues. Instead of flags I used the
> clockid_t in a similar fashion.
Works as well. I'm not seing the point for CLOCK_MONOTONIC and the
change logs are not really telling anything about the problem being
solved....
Thanks,
tglx
^ permalink raw reply
* [RESEND PATCH v3 0/2] VT: Add ability to get font requirements
From: Alexey Gladkov @ 2024-04-02 10:32 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: LKML, kbd, linux-api, linux-fbdev, linux-serial
In-Reply-To: <cover.1710252966.git.legion@kernel.org>
We now have KD_FONT_OP_SET_TALL, but in fact such large fonts cannot be
loaded. No console driver supports tall fonts. Unfortunately, userspace
cannot distinguish the lack of support in the driver from errors in the
font itself. In all cases, EINVAL will be returned.
This patchset adds a separate ioctl to obtain the font parameters
supported by the console driver.
v3:
* Added the use of the in_range macro.
* Squashed the commits that add ioctl to console divers.
v2:
* Instead of the KDFONTOP extension, a new ioctl has been added to
obtain font information.
Alexey Gladkov (2):
VT: Add KDFONTINFO ioctl
VT: Allow to get max font width and height
drivers/tty/vt/vt.c | 24 ++++++++++++++++++++++++
drivers/tty/vt/vt_ioctl.c | 13 +++++++++++++
drivers/video/console/newport_con.c | 21 +++++++++++++++++----
drivers/video/console/sticon.c | 25 +++++++++++++++++++++++--
drivers/video/console/vgacon.c | 21 ++++++++++++++++++++-
drivers/video/fbdev/core/fbcon.c | 22 +++++++++++++++++++++-
include/linux/console.h | 2 ++
include/linux/vt_kern.h | 1 +
include/uapi/linux/kd.h | 13 ++++++++++++-
9 files changed, 133 insertions(+), 9 deletions(-)
--
2.44.0
^ permalink raw reply
* [RESEND PATCH v3 1/2] VT: Add KDFONTINFO ioctl
From: Alexey Gladkov @ 2024-04-02 10:32 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: LKML, kbd, linux-api, linux-fbdev, linux-serial, Helge Deller
In-Reply-To: <cover.1712053848.git.legion@kernel.org>
Each driver has its own restrictions on font size. There is currently no
way to understand what the requirements are. The new ioctl allows
userspace to get the minmum and maximum font size values.
Acked-by: Helge Deller <deller@gmx.de>
Signed-off-by: Alexey Gladkov <legion@kernel.org>
---
drivers/tty/vt/vt.c | 24 ++++++++++++++++++++++++
drivers/tty/vt/vt_ioctl.c | 13 +++++++++++++
include/linux/console.h | 2 ++
include/linux/vt_kern.h | 1 +
include/uapi/linux/kd.h | 13 ++++++++++++-
5 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 156efda7c80d..8c2a3d98b5ec 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -4680,6 +4680,30 @@ int con_font_op(struct vc_data *vc, struct console_font_op *op)
return -ENOSYS;
}
+int con_font_info(struct vc_data *vc, struct console_font_info *info)
+{
+ int rc = -EINVAL;
+
+ info->min_height = 0;
+ info->max_height = max_font_height;
+
+ info->min_width = 0;
+ info->max_width = max_font_width;
+
+ info->flags = KD_FONT_INFO_FLAG_LOW_SIZE | KD_FONT_INFO_FLAG_HIGH_SIZE;
+
+ console_lock();
+ if (vc->vc_mode != KD_TEXT)
+ rc = -EINVAL;
+ else if (vc->vc_sw->con_font_info)
+ rc = vc->vc_sw->con_font_info(vc, info);
+ else
+ rc = -ENOSYS;
+ console_unlock();
+
+ return rc;
+}
+
/*
* Interface exported to selection and vcs.
*/
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index 8c685b501404..b3b4e4b69366 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -479,6 +479,19 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd,
break;
}
+ case KDFONTINFO: {
+ struct console_font_info fnt_info;
+
+ if (copy_from_user(&fnt_info, up, sizeof(fnt_info)))
+ return -EFAULT;
+ ret = con_font_info(vc, &fnt_info);
+ if (ret)
+ return ret;
+ if (copy_to_user(up, &fnt_info, sizeof(fnt_info)))
+ return -EFAULT;
+ break;
+ }
+
default:
return -ENOIOCTLCMD;
}
diff --git a/include/linux/console.h b/include/linux/console.h
index 779d388af8a0..5bea6f6c2042 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -20,6 +20,7 @@
#include <linux/types.h>
struct vc_data;
+struct console_font_info;
struct console_font_op;
struct console_font;
struct module;
@@ -59,6 +60,7 @@ struct consw {
unsigned int lines);
int (*con_switch)(struct vc_data *vc);
int (*con_blank)(struct vc_data *vc, int blank, int mode_switch);
+ int (*con_font_info)(struct vc_data *vc, struct console_font_info *info);
int (*con_font_set)(struct vc_data *vc, struct console_font *font,
unsigned int vpitch, unsigned int flags);
int (*con_font_get)(struct vc_data *vc, struct console_font *font,
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index c1f5aebef170..6bda4cc1fe6f 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -32,6 +32,7 @@ void do_blank_screen(int entering_gfx);
void do_unblank_screen(int leaving_gfx);
void poke_blanked_console(void);
int con_font_op(struct vc_data *vc, struct console_font_op *op);
+int con_font_info(struct vc_data *vc, struct console_font_info *info);
int con_set_cmap(unsigned char __user *cmap);
int con_get_cmap(unsigned char __user *cmap);
void scrollback(struct vc_data *vc);
diff --git a/include/uapi/linux/kd.h b/include/uapi/linux/kd.h
index 6b384065c013..781e086e55bf 100644
--- a/include/uapi/linux/kd.h
+++ b/include/uapi/linux/kd.h
@@ -183,8 +183,19 @@ struct console_font {
#define KD_FONT_FLAG_DONT_RECALC 1 /* Don't recalculate hw charcell size [compat] */
+#define KDFONTINFO 0x4B73 /* font information */
+
+#define KD_FONT_INFO_FLAG_LOW_SIZE (1U << 0) /* 256 */
+#define KD_FONT_INFO_FLAG_HIGH_SIZE (1U << 1) /* 512 */
+
+struct console_font_info {
+ unsigned int min_width, min_height; /* minimal font size */
+ unsigned int max_width, max_height; /* maximum font size */
+ unsigned int flags; /* KD_FONT_INFO_FLAG_* */
+};
+
/* note: 0x4B00-0x4B4E all have had a value at some time;
don't reuse for the time being */
-/* note: 0x4B60-0x4B6D, 0x4B70-0x4B72 used above */
+/* note: 0x4B60-0x4B6D, 0x4B70-0x4B73 used above */
#endif /* _UAPI_LINUX_KD_H */
--
2.44.0
^ permalink raw reply related
* [RESEND PATCH v3 2/2] VT: Allow to get max font width and height
From: Alexey Gladkov @ 2024-04-02 10:32 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: LKML, kbd, linux-api, linux-fbdev, linux-serial, Helge Deller
In-Reply-To: <cover.1712053848.git.legion@kernel.org>
The Console drivers has more restrictive font size limits than vt_ioctl.
This leads to errors that are difficult to handle. If a font whose size
is not supported is used, an EINVAL error will be returned, which is
also returned in case of errors in the font itself. At the moment there
is no way to understand what font sizes the current console driver
supports.
To solve this problem, we need to transfer information about the
supported font to userspace from the console driver.
Acked-by: Helge Deller <deller@gmx.de>
Signed-off-by: Alexey Gladkov <legion@kernel.org>
---
drivers/video/console/newport_con.c | 21 +++++++++++++++++----
drivers/video/console/sticon.c | 25 +++++++++++++++++++++++--
drivers/video/console/vgacon.c | 21 ++++++++++++++++++++-
drivers/video/fbdev/core/fbcon.c | 22 +++++++++++++++++++++-
4 files changed, 81 insertions(+), 8 deletions(-)
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index e8e4f82cd4a1..87f174a95fa8 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -33,6 +33,9 @@
#define NEWPORT_LEN 0x10000
+#define NEWPORT_MAX_FONT_WIDTH 8
+#define NEWPORT_MAX_FONT_HEIGHT 16
+
#define FONT_DATA ((unsigned char *)font_vga_8x16.data)
static unsigned char *font_data[MAX_NR_CONSOLES];
@@ -328,8 +331,8 @@ static void newport_init(struct vc_data *vc, int init)
{
int cols, rows;
- cols = newport_xsize / 8;
- rows = newport_ysize / 16;
+ cols = newport_xsize / NEWPORT_MAX_FONT_WIDTH;
+ rows = newport_ysize / NEWPORT_MAX_FONT_HEIGHT;
vc->vc_can_do_color = 1;
if (init) {
vc->vc_cols = cols;
@@ -507,8 +510,8 @@ static int newport_set_font(int unit, struct console_font *op, unsigned int vpit
/* ladis: when I grow up, there will be a day... and more sizes will
* be supported ;-) */
- if ((w != 8) || (h != 16) || (vpitch != 32)
- || (op->charcount != 256 && op->charcount != 512))
+ if ((w != NEWPORT_MAX_FONT_WIDTH) || (h != NEWPORT_MAX_FONT_HEIGHT) ||
+ (vpitch != 32) || (op->charcount != 256 && op->charcount != 512))
return -EINVAL;
if (!(new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size,
@@ -569,6 +572,15 @@ static int newport_font_default(struct vc_data *vc, struct console_font *op, cha
return newport_set_def_font(vc->vc_num, op);
}
+static int newport_font_info(struct vc_data *vc, struct console_font_info *info)
+{
+ info->min_width = info->max_width = NEWPORT_MAX_FONT_WIDTH;
+ info->min_height = info->max_height = NEWPORT_MAX_FONT_HEIGHT;
+ info->flags = KD_FONT_INFO_FLAG_LOW_SIZE | KD_FONT_INFO_FLAG_HIGH_SIZE;
+
+ return 0;
+}
+
static int newport_font_set(struct vc_data *vc, struct console_font *font,
unsigned int vpitch, unsigned int flags)
{
@@ -688,6 +700,7 @@ const struct consw newport_con = {
.con_scroll = newport_scroll,
.con_switch = newport_switch,
.con_blank = newport_blank,
+ .con_font_info = newport_font_info,
.con_font_set = newport_font_set,
.con_font_default = newport_font_default,
.con_save_screen = newport_save_screen
diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c
index 992a4fa431aa..d32ca458eb77 100644
--- a/drivers/video/console/sticon.c
+++ b/drivers/video/console/sticon.c
@@ -56,6 +56,11 @@
#define BLANK 0
static int vga_is_gfx;
+#define STICON_MIN_FONT_WIDTH 6
+#define STICON_MIN_FONT_HEIGHT 6
+#define STICON_MAX_FONT_WIDTH 32
+#define STICON_MAX_FONT_HEIGHT 32
+
#define STI_DEF_FONT sticon_sti->font
/* borrowed from fbcon.c */
@@ -180,8 +185,10 @@ static int sticon_set_font(struct vc_data *vc, struct console_font *op,
struct sti_cooked_font *cooked_font;
unsigned char *data = op->data, *p;
- if ((w < 6) || (h < 6) || (w > 32) || (h > 32) || (vpitch != 32)
- || (op->charcount != 256 && op->charcount != 512))
+ if (!in_range(w, STICON_MIN_FONT_WIDTH, STICON_MAX_FONT_WIDTH) ||
+ !in_range(h, STICON_MIN_FONT_HEIGHT, STICON_MAX_FONT_HEIGHT) ||
+ (vpitch != 32) ||
+ (op->charcount != 256 && op->charcount != 512))
return -EINVAL;
pitch = ALIGN(w, 8) / 8;
bpc = pitch * h;
@@ -273,6 +280,19 @@ static int sticon_font_set(struct vc_data *vc, struct console_font *font,
return sticon_set_font(vc, font, vpitch);
}
+static int sticon_font_info(struct vc_data *vc, struct console_font_info *info)
+{
+ info->min_width = STICON_MIN_FONT_WIDTH;
+ info->min_height = STICON_MIN_FONT_HEIGHT;
+
+ info->max_width = STICON_MAX_FONT_WIDTH;
+ info->max_height = STICON_MAX_FONT_HEIGHT;
+
+ info->flags = KD_FONT_INFO_FLAG_LOW_SIZE | KD_FONT_INFO_FLAG_HIGH_SIZE;
+
+ return 0;
+}
+
static void sticon_init(struct vc_data *c, int init)
{
struct sti_struct *sti = sticon_sti;
@@ -371,6 +391,7 @@ static const struct consw sti_con = {
.con_scroll = sticon_scroll,
.con_switch = sticon_switch,
.con_blank = sticon_blank,
+ .con_font_info = sticon_font_info,
.con_font_set = sticon_font_set,
.con_font_default = sticon_font_default,
.con_build_attr = sticon_build_attr,
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index 8ef1579fa57f..b75d31ef3353 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -61,6 +61,10 @@ static struct vgastate vgastate;
#define BLANK 0x0020
#define VGA_FONTWIDTH 8 /* VGA does not support fontwidths != 8 */
+
+#define VGACON_MAX_FONT_WIDTH VGA_FONTWIDTH
+#define VGACON_MAX_FONT_HEIGHT 32
+
/*
* Interface used by the world
*/
@@ -1013,6 +1017,19 @@ static int vgacon_adjust_height(struct vc_data *vc, unsigned fontheight)
return 0;
}
+static int vgacon_font_info(struct vc_data *vc, struct console_font_info *info)
+{
+ info->min_width = VGACON_MAX_FONT_WIDTH;
+ info->min_height = 0;
+
+ info->max_width = VGACON_MAX_FONT_WIDTH;
+ info->max_height = VGACON_MAX_FONT_HEIGHT;
+
+ info->flags = KD_FONT_INFO_FLAG_LOW_SIZE | KD_FONT_INFO_FLAG_HIGH_SIZE;
+
+ return 0;
+}
+
static int vgacon_font_set(struct vc_data *c, struct console_font *font,
unsigned int vpitch, unsigned int flags)
{
@@ -1022,7 +1039,8 @@ static int vgacon_font_set(struct vc_data *c, struct console_font *font,
if (vga_video_type < VIDEO_TYPE_EGAM)
return -EINVAL;
- if (font->width != VGA_FONTWIDTH || font->height > 32 || vpitch != 32 ||
+ if (font->width != VGACON_MAX_FONT_WIDTH ||
+ font->height > VGACON_MAX_FONT_HEIGHT || vpitch != 32 ||
(charcount != 256 && charcount != 512))
return -EINVAL;
@@ -1177,6 +1195,7 @@ const struct consw vga_con = {
.con_scroll = vgacon_scroll,
.con_switch = vgacon_switch,
.con_blank = vgacon_blank,
+ .con_font_info = vgacon_font_info,
.con_font_set = vgacon_font_set,
.con_font_get = vgacon_font_get,
.con_resize = vgacon_resize,
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 46823c2e2ba1..e10abe416159 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -101,6 +101,9 @@ enum {
FBCON_LOGO_DONTSHOW = -3 /* do not show the logo */
};
+#define FBCON_MAX_FONT_WIDTH 32
+#define FBCON_MAX_FONT_HEIGHT 32
+
static struct fbcon_display fb_display[MAX_NR_CONSOLES];
static struct fb_info *fbcon_registered_fb[FB_MAX];
@@ -2456,6 +2459,21 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount,
return ret;
}
+
+static int fbcon_font_info(struct vc_data *vc, struct console_font_info *info)
+{
+ info->min_width = 0;
+ info->min_height = 0;
+
+ info->max_width = FBCON_MAX_FONT_WIDTH;
+ info->max_height = FBCON_MAX_FONT_HEIGHT;
+
+ info->flags = KD_FONT_INFO_FLAG_LOW_SIZE | KD_FONT_INFO_FLAG_HIGH_SIZE;
+
+ return 0;
+}
+
+
/*
* User asked to set font; we are guaranteed that charcount does not exceed 512
* but lets not assume that, since charcount of 512 is small for unicode support.
@@ -2483,7 +2501,8 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres))
return -EINVAL;
- if (font->width > 32 || font->height > 32)
+ if (font->width > FBCON_MAX_FONT_WIDTH ||
+ font->height > FBCON_MAX_FONT_HEIGHT)
return -EINVAL;
/* Make sure drawing engine can handle the font */
@@ -3158,6 +3177,7 @@ static const struct consw fb_con = {
.con_scroll = fbcon_scroll,
.con_switch = fbcon_switch,
.con_blank = fbcon_blank,
+ .con_font_info = fbcon_font_info,
.con_font_set = fbcon_set_font,
.con_font_get = fbcon_get_font,
.con_font_default = fbcon_set_def_font,
--
2.44.0
^ permalink raw reply related
* Re: [RESEND PATCH v3 1/2] VT: Add KDFONTINFO ioctl
From: Jiri Slaby @ 2024-04-02 11:02 UTC (permalink / raw)
To: Alexey Gladkov, Greg Kroah-Hartman
Cc: LKML, kbd, linux-api, linux-fbdev, linux-serial, Helge Deller
In-Reply-To: <ed056326540f04b72c97a276fbcc316e1b2f6371.1712053848.git.legion@kernel.org>
Hi,
On 02. 04. 24, 12:32, Alexey Gladkov wrote:
> Each driver has its own restrictions on font size. There is currently no
> way to understand what the requirements are. The new ioctl allows
> userspace to get the minmum and maximum font size values.
minimum
> Acked-by: Helge Deller <deller@gmx.de>
> Signed-off-by: Alexey Gladkov <legion@kernel.org>
> ---
> drivers/tty/vt/vt.c | 24 ++++++++++++++++++++++++
> drivers/tty/vt/vt_ioctl.c | 13 +++++++++++++
> include/linux/console.h | 2 ++
> include/linux/vt_kern.h | 1 +
> include/uapi/linux/kd.h | 13 ++++++++++++-
> 5 files changed, 52 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
> index 156efda7c80d..8c2a3d98b5ec 100644
> --- a/drivers/tty/vt/vt.c
> +++ b/drivers/tty/vt/vt.c
> @@ -4680,6 +4680,30 @@ int con_font_op(struct vc_data *vc, struct console_font_op *op)
> return -ENOSYS;
> }
>
> +int con_font_info(struct vc_data *vc, struct console_font_info *info)
> +{
> + int rc = -EINVAL;
This initialization appears to be unneeded.
> +
> + info->min_height = 0;
> + info->max_height = max_font_height;
> +
> + info->min_width = 0;
> + info->max_width = max_font_width;
> +
> + info->flags = KD_FONT_INFO_FLAG_LOW_SIZE | KD_FONT_INFO_FLAG_HIGH_SIZE;
> +
> + console_lock();
> + if (vc->vc_mode != KD_TEXT)
> + rc = -EINVAL;
> + else if (vc->vc_sw->con_font_info)
> + rc = vc->vc_sw->con_font_info(vc, info);
> + else
> + rc = -ENOSYS;
> + console_unlock();
> +
> + return rc;
> +}
> +
> /*
> * Interface exported to selection and vcs.
> */
> diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
> index 8c685b501404..b3b4e4b69366 100644
> --- a/drivers/tty/vt/vt_ioctl.c
> +++ b/drivers/tty/vt/vt_ioctl.c
> @@ -479,6 +479,19 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd,
> break;
> }
>
> + case KDFONTINFO: {
> + struct console_font_info fnt_info;
> +
> + if (copy_from_user(&fnt_info, up, sizeof(fnt_info)))
> + return -EFAULT;
Who uses the copied values?
> + ret = con_font_info(vc, &fnt_info);
> + if (ret)
> + return ret;
> + if (copy_to_user(up, &fnt_info, sizeof(fnt_info)))
We should do the preferred sizeof(*up) here...
> + return -EFAULT;
> + break;
> + }
> +
> default:
> return -ENOIOCTLCMD;
> }
...
> --- a/include/uapi/linux/kd.h
> +++ b/include/uapi/linux/kd.h
> @@ -183,8 +183,19 @@ struct console_font {
>
> #define KD_FONT_FLAG_DONT_RECALC 1 /* Don't recalculate hw charcell size [compat] */
>
> +#define KDFONTINFO 0x4B73 /* font information */
Why not properly define the number using IOC() et al.? K (that 0x4b) is
even reserved for kd.h.
> +#define KD_FONT_INFO_FLAG_LOW_SIZE (1U << 0) /* 256 */
> +#define KD_FONT_INFO_FLAG_HIGH_SIZE (1U << 1) /* 512 */
_BITUL()
> +struct console_font_info {
> + unsigned int min_width, min_height; /* minimal font size */
> + unsigned int max_width, max_height; /* maximum font size */
> + unsigned int flags; /* KD_FONT_INFO_FLAG_* */
This does not look like a well-defined™ and extendable uapi structure.
While it won't change anything here, still use fixed-length __u32.
And you should perhaps add some reserved fields. Do not repeat the same
mistakes as your predecessors with the current kd uapi.
> +};
thanks,
--
js
suse labs
^ permalink raw reply
* Re: [PATCH v3 2/2] VT: Allow to get max font width and height
From: Jiri Slaby @ 2024-04-02 11:09 UTC (permalink / raw)
To: Oleg Bulatov, legion
Cc: Greg Kroah-Hartman, LKML, kbd, linux-api, linux-fbdev,
linux-serial
In-Reply-To: <c3wrf2h7h45h2vee7gc42zmy43rsh7niueknvsrlsibnae4pdw@4u6b4qulfe6r>
On 13. 03. 24, 18:40, Oleg Bulatov wrote:
> On Tue, Mar 12, 2024 at 03:23:58PM +0100, legion@kernel.org wrote:
>> drivers/video/console/newport_con.c | 21 +++++++++++++++++----
>> drivers/video/console/sticon.c | 25 +++++++++++++++++++++++--
>> drivers/video/console/vgacon.c | 21 ++++++++++++++++++++-
>> drivers/video/fbdev/core/fbcon.c | 22 +++++++++++++++++++++-
>> 4 files changed, 81 insertions(+), 8 deletions(-)
>
> newport_con.c is an interesting one, apparently it's for SGI Indy and
> Indigo2, both are discontinued in 1997. Do we still have a way to test
> this driver?
I doubt that.
Care to submit a removal patch? I am afraid, there is no other way to
find out anyway...
thanks,
--
js
suse labs
^ permalink raw reply
* Re: [RESEND PATCH v3 1/2] VT: Add KDFONTINFO ioctl
From: Alexey Gladkov @ 2024-04-02 13:19 UTC (permalink / raw)
To: Jiri Slaby
Cc: Greg Kroah-Hartman, LKML, kbd, linux-api, linux-fbdev,
linux-serial, Helge Deller
In-Reply-To: <74ca50e0-61b1-4d4c-85dd-a5d920548c04@kernel.org>
On Tue, Apr 02, 2024 at 01:02:20PM +0200, Jiri Slaby wrote:
> Hi,
>
> On 02. 04. 24, 12:32, Alexey Gladkov wrote:
> > Each driver has its own restrictions on font size. There is currently no
> > way to understand what the requirements are. The new ioctl allows
> > userspace to get the minmum and maximum font size values.
>
> minimum
Typo. Sorry.
> > Acked-by: Helge Deller <deller@gmx.de>
> > Signed-off-by: Alexey Gladkov <legion@kernel.org>
> > ---
> > drivers/tty/vt/vt.c | 24 ++++++++++++++++++++++++
> > drivers/tty/vt/vt_ioctl.c | 13 +++++++++++++
> > include/linux/console.h | 2 ++
> > include/linux/vt_kern.h | 1 +
> > include/uapi/linux/kd.h | 13 ++++++++++++-
> > 5 files changed, 52 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
> > index 156efda7c80d..8c2a3d98b5ec 100644
> > --- a/drivers/tty/vt/vt.c
> > +++ b/drivers/tty/vt/vt.c
> > @@ -4680,6 +4680,30 @@ int con_font_op(struct vc_data *vc, struct console_font_op *op)
> > return -ENOSYS;
> > }
> >
> > +int con_font_info(struct vc_data *vc, struct console_font_info *info)
> > +{
> > + int rc = -EINVAL;
>
> This initialization appears to be unneeded.
>
> > +
> > + info->min_height = 0;
> > + info->max_height = max_font_height;
> > +
> > + info->min_width = 0;
> > + info->max_width = max_font_width;
> > +
> > + info->flags = KD_FONT_INFO_FLAG_LOW_SIZE | KD_FONT_INFO_FLAG_HIGH_SIZE;
> > +
> > + console_lock();
> > + if (vc->vc_mode != KD_TEXT)
> > + rc = -EINVAL;
> > + else if (vc->vc_sw->con_font_info)
> > + rc = vc->vc_sw->con_font_info(vc, info);
> > + else
> > + rc = -ENOSYS;
> > + console_unlock();
> > +
> > + return rc;
> > +}
> > +
> > /*
> > * Interface exported to selection and vcs.
> > */
> > diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
> > index 8c685b501404..b3b4e4b69366 100644
> > --- a/drivers/tty/vt/vt_ioctl.c
> > +++ b/drivers/tty/vt/vt_ioctl.c
> > @@ -479,6 +479,19 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd,
> > break;
> > }
> >
> > + case KDFONTINFO: {
> > + struct console_font_info fnt_info;
> > +
> > + if (copy_from_user(&fnt_info, up, sizeof(fnt_info)))
> > + return -EFAULT;
>
> Who uses the copied values?
No one. I did it by analogy with KDFONTOP. Thanks!
> > + ret = con_font_info(vc, &fnt_info);
> > + if (ret)
> > + return ret;
> > + if (copy_to_user(up, &fnt_info, sizeof(fnt_info)))
>
> We should do the preferred sizeof(*up) here...
>
> > + return -EFAULT;
> > + break;
> > + }
> > +
> > default:
> > return -ENOIOCTLCMD;
> > }
> ...
> > --- a/include/uapi/linux/kd.h
> > +++ b/include/uapi/linux/kd.h
> > @@ -183,8 +183,19 @@ struct console_font {
> >
> > #define KD_FONT_FLAG_DONT_RECALC 1 /* Don't recalculate hw charcell size [compat] */
> >
> > +#define KDFONTINFO 0x4B73 /* font information */
>
> Why not properly define the number using IOC() et al.? K (that 0x4b) is
> even reserved for kd.h.
I just did the same as the numbers above. This entire header does not use
IOC().
Should I convert this header as a separate commit?
> > +#define KD_FONT_INFO_FLAG_LOW_SIZE (1U << 0) /* 256 */
> > +#define KD_FONT_INFO_FLAG_HIGH_SIZE (1U << 1) /* 512 */
>
> _BITUL()
Make sense. I will use it.
> > +struct console_font_info {
> > + unsigned int min_width, min_height; /* minimal font size */
> > + unsigned int max_width, max_height; /* maximum font size */
> > + unsigned int flags; /* KD_FONT_INFO_FLAG_* */
>
> This does not look like a well-defined™ and extendable uapi structure.
> While it won't change anything here, still use fixed-length __u32.
>
> And you should perhaps add some reserved fields. Do not repeat the same
> mistakes as your predecessors with the current kd uapi.
I thought about it, but I thought it would be overengineering.
Can you suggest how best to do this?
--
Rgrds, legion
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox