From patchwork Wed Feb 14 23:36:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elizabeth Figura X-Patchwork-Id: 201186 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7300:b825:b0:106:860b:bbdd with SMTP id da37csp61330dyb; Wed, 14 Feb 2024 15:56:16 -0800 (PST) X-Forwarded-Encrypted: i=3; AJvYcCWus2C/VW4ATKjFDpI+hnP5Bo2ZTzNn1/3eDz8X6r88PYDSWE0Svpav6f8eN5jjZr9hT6LlL9jsz8Mixs9ThObob9Dang== X-Google-Smtp-Source: AGHT+IHiSq8sPZMCCXLyBoHhfJFjHzB1hwJOAiAgPZ2Y03JctK4qBVWS7HxOFE0QYM3Jh3hRp+i4 X-Received: by 2002:a0c:ae91:0:b0:68c:6f8c:6741 with SMTP id j17-20020a0cae91000000b0068c6f8c6741mr170383qvd.11.1707954975808; Wed, 14 Feb 2024 15:56:15 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1707954975; cv=pass; d=google.com; s=arc-20160816; b=Nub4VNqHZzDfqt4CQKgLCE/qz8PK6CMVG5iXq0or8WWfQWZJbc3zf7u3cuqLUUeAoZ 3SOGFSwNl9n5ufB9uw2MtMQXDAXqbgnSMbOLL1m8oNXRBM8bjTCzNgqsbYArmqtAbGyi VjfU8xpjdsNafVdhevTm50bazRFSQM5BIhW9fs7YNdbBE0A/+SFbIVfQvIYUeAq9lnrt rZB4IlhpvCgdAz3rNCL3JxWJxE9OQyLWD9uEJqeYtfXXSwxXJFgNhnQoDMTTFqsG0gPD dFN4CMlQX60vN2oAzBdPWIVg0Z3y/P7JF69rqq4cVUpa4tHs06ttK4can7L/wvs6mg4O m9Pg== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:references:in-reply-to:message-id :date:subject:cc:to:from:dkim-signature; bh=swZMnGhvO/1HH3tk0ma3xvTm3heYbVsiMB5nX61+XbM=; fh=sOAzP1IXJnllEbSCBRlwdfT0MN3UiaRLwzx1Tpw7GJA=; b=c5PTXKC4EHMt4pqUA6xqb0hQsU1dRPgOTSjIeCj7nwjQ+qVlpFK5TpmTnhpUaGV8Gk LcBkvgCMjciQPidx/dtZBIFyzEsyZTArGEiZ4h3ULZ/wX0xRZk2LR8yBAwxlQa+RtRPc KyWw3eqixDKGfsqSr8wgO/OBq0VPt9f6DcfwpJgbxYKWUuqyUiaM1dS6unURtjJkaqhy /vX5s2y8rGnc8kcpaoVPPsVCpPdx+7+FMcB+/wJUVEHMzEhzUbmOid3Semk6wtnx40rx uUZ4woZpfq2vff9Wjcr6BKUrB09UemZJtIAUHfVNwwZvrflCdZTYkLkmkdksbEcRqGk1 HjKw==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@codeweavers.com header.s=s1 header.b=qRJM8kLa; arc=pass (i=1 spf=pass spfdomain=codeweavers.com dkim=pass dkdomain=codeweavers.com dmarc=pass fromdomain=codeweavers.com); spf=pass (google.com: domain of linux-kernel+bounces-66141-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.199.223 as permitted sender) smtp.mailfrom="linux-kernel+bounces-66141-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=codeweavers.com Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org. [147.75.199.223]) by mx.google.com with ESMTPS id 2-20020a0562140dc200b0068c4f264e8asi203012qvt.175.2024.02.14.15.56.15 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 14 Feb 2024 15:56:15 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-66141-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.199.223 as permitted sender) client-ip=147.75.199.223; Authentication-Results: mx.google.com; dkim=pass header.i=@codeweavers.com header.s=s1 header.b=qRJM8kLa; arc=pass (i=1 spf=pass spfdomain=codeweavers.com dkim=pass dkdomain=codeweavers.com dmarc=pass fromdomain=codeweavers.com); spf=pass (google.com: domain of linux-kernel+bounces-66141-ouuuleilei=gmail.com@vger.kernel.org designates 147.75.199.223 as permitted sender) smtp.mailfrom="linux-kernel+bounces-66141-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=codeweavers.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id 5E23A1C2881D for ; Wed, 14 Feb 2024 23:55:37 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id BACC11474D8; Wed, 14 Feb 2024 23:53:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=codeweavers.com header.i=@codeweavers.com header.b="qRJM8kLa" Received: from mail.codeweavers.com (mail.codeweavers.com [4.36.192.163]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 395D0146014; Wed, 14 Feb 2024 23:53:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=4.36.192.163 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707954810; cv=none; b=OfJi8VVGClYF9cvCX/NuHQUYDWoWc9R7mFnUCX5YQ9iG2HjyRAYQuXd+Kb1yTccKCQq/45BmiQ724pGGleIVaiNgYeMEm87L9b+zjyb3jNHnk+W99UEHbTEBXt59rmwl+cKwTCEvxqAItT7aa2Z7TLwF0kbDroOr18VP3A9q724= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1707954810; c=relaxed/simple; bh=1yN9eC0/UnvZjEFI1nohqxO7chrSrAzybvm5pljn4sA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lDUxZBH44QeIFCeXiumgd47kqUHAVubQWbvDQah0oYzMVd+sd9J+/MpL9TRulVfqX+polKmy1XU5nz0JOakdbR6sDEVMhQbs0BsDl0xoc1N70tj+r0zHX8tPrRSDTrnJlyaaZJLir/93d+6j7juySTg+2k20I1ORw1j1osQeBIk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=codeweavers.com; spf=pass smtp.mailfrom=codeweavers.com; dkim=pass (2048-bit key) header.d=codeweavers.com header.i=@codeweavers.com header.b=qRJM8kLa; arc=none smtp.client-ip=4.36.192.163 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=codeweavers.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=codeweavers.com DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=codeweavers.com; s=s1; h=Message-ID:Date:Subject:Cc:To:From:Sender; bh=swZMnGhvO/1HH3tk0ma3xvTm3heYbVsiMB5nX61+XbM=; b=qRJM8kLa2WKN6uaLzH5777EwdZ nrTJyv8B1vGlQ5zAQ2e/zbbPHYv2F+4Bwj20JJ2mdddcEYh5SZLUdQpX5U6S7MaR1+88ceIRHeCPu hDPCVG5tKeXkTsTOTC2p05TopsQLatjIbO95x3LqL2ELjUPiicORzzOeKmb/UbJV28MwofnFycIGb +IWfr8J066CC4YPbdvn4jIIoeeRZO1o0/DB9CWwt/rOGyyTIJhlQekVPvsyBXB4Jr0kuyHqsSlHUN 38uy2y/RJ7j/EaZ9iK9g6c/21RGtKq6pvpLGOUR04kYxbmxRJ6fEYuVRth6FKzkaJ5vrtOQsLceZA QbapUMfw==; Received: from cw137ip160.mn.codeweavers.com ([10.69.137.160] helo=camazotz.mn.codeweavers.com) by mail.codeweavers.com with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1raOoY-00GbiL-0V; Wed, 14 Feb 2024 17:37:18 -0600 From: Elizabeth Figura To: Arnd Bergmann , Greg Kroah-Hartman , Jonathan Corbet , Shuah Khan Cc: linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, wine-devel@winehq.org, =?utf-8?q?Andr=C3=A9_Almeida?= , Wolfram Sang , Arkadiusz Hiler , Peter Zijlstra , Andy Lutomirski , linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, Elizabeth Figura Subject: [PATCH 04/31] ntsync: Introduce NTSYNC_IOC_WAIT_ANY. Date: Wed, 14 Feb 2024 17:36:40 -0600 Message-ID: <20240214233645.9273-5-zfigura@codeweavers.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240214233645.9273-1-zfigura@codeweavers.com> References: <20240214233645.9273-1-zfigura@codeweavers.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1790920597117666072 X-GMAIL-MSGID: 1790920597117666072 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 the MONOTONIC clock. 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. Signed-off-by: Elizabeth Figura --- drivers/misc/ntsync.c | 237 ++++++++++++++++++++++++++++++++++++ include/uapi/linux/ntsync.h | 12 ++ 2 files changed, 249 insertions(+) diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c index 01556475f5cc..123af239f52e 100644 --- a/drivers/misc/ntsync.c +++ b/drivers/misc/ntsync.c @@ -6,11 +6,16 @@ */ #include +#include #include #include +#include +#include #include #include #include +#include +#include #include #include #include @@ -36,12 +41,55 @@ 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_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; + + if (!sem->u.sem.count) + break; + + if (atomic_cmpxchg(&q->signaled, -1, entry->index) == -1) { + sem->u.sem.count--; + wake_up_process(q->task); + } + } +} + /* * Actually change the semaphore state, returning -EOVERFLOW if it is made * invalid. @@ -77,6 +125,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); @@ -130,6 +180,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; } @@ -180,6 +231,190 @@ 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->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); + ktime_t *timeout_ptr; + int ret = 0; + + timeout_ptr = (args->timeout == U64_MAX ? NULL : &timeout); + + 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(timeout_ptr, HRTIMER_MODE_ABS); + } 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 || args->pad) + 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; @@ -211,6 +446,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 878ec4f0f2e8..9cd1dd05d971 100644 --- a/include/uapi/linux/ntsync.h +++ b/include/uapi/linux/ntsync.h @@ -16,7 +16,19 @@ struct ntsync_sem_args { __u32 max; }; +struct ntsync_wait_args { + __u64 timeout; + __u64 objs; + __u32 count; + __u32 owner; + __u32 index; + __u32 pad; +}; + +#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)