From patchwork Wed Jan 31 02:13:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elizabeth Figura X-Patchwork-Id: 194472 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7301:2087:b0:106:209c:c626 with SMTP id gs7csp1641272dyb; Tue, 30 Jan 2024 19:07:59 -0800 (PST) X-Google-Smtp-Source: AGHT+IHZwQQ7pCS12pGIE2KY4vYB3wbCCNi4EOXKhohTpVy+SKIrZMytZ4hBKdplquXtG3jmLISQ X-Received: by 2002:a17:902:7408:b0:1d5:8bf4:c79b with SMTP id g8-20020a170902740800b001d58bf4c79bmr534289pll.39.1706670478903; Tue, 30 Jan 2024 19:07:58 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1706670478; cv=pass; d=google.com; s=arc-20160816; b=j+BiXhk4Er4mdhmpDbeiclzOm2DdGpqF/9/+mc8Zox8pcifWsQjRdRT0rTYgDUfWiQ OOK2eNJ5eT/2kJ2ryBOpwBPx/wBkW3fDb2FuDscS73pQzTer4XJD/n+HBbEZQFlywsnH x8hxESUuNElo1YzgQ/ZpcvzH8BrKDME2PBKxEboN9ymbVElUigm0snp1BkLDFsJL3OMN sA8v5Jv3gwQ3bDWUf/J/Ta9WYXGS5tiGjU9Pi69Kln+MCh+9Wm3ruCnEWIv5zv0vddSs TnFaztwNNBE7iyQ20tAJCPqsLg6Wpfv3inI6HYpqmyZQLlyRaa8fvMb8VVmxjoVxul9B Mvyw== 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=dBqQoWEccHAFufDAEgUe6Vd0qbJ3tQL100kMCYoxJh0=; fh=8pc+3q7VJQGnAQ3QEgKStypEW/8z72QglvOaUbyD/2o=; b=VG2KkCQ2qWNXR4kLqtiKST4BmuuOLHo8pwlM3s0Wou89oPvfN6DRUjBUBeS5synlaM o0SQA7rBtXZWf6uFjsvAEsuYg1ry+c5pjqr6iGTY592cL4Ys8+7CILXGEVmv2WExcAD4 aLTeEVbRD7Tb6goYxkmp6qqeubDMu1y/LBGk+cXjf9R3sdmLPtKqa7P9JCjTFjAGGFts ck5YQZ//oZ488IZAnWb47Fn7p3PTRHCzExO7BlT3JG1RvFS32SpQuO8dsQio7D3ad6LW tQzfOnXUkKO98mthLHnXP4PTmQapwtuLdzhPgqEbNNK6zmz6T1SRWhC3X/mzH6DTe9c2 YCEw==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@codeweavers.com header.s=s1 header.b=CxRMRmK8; 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-45639-ouuuleilei=gmail.com@vger.kernel.org designates 2604:1380:45e3:2400::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-45639-ouuuleilei=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=codeweavers.com X-Forwarded-Encrypted: i=1; AJvYcCVzzNRZZjsNRcNo9n1JNFXbZgkPouY8+R93yfAjPvxRJGEY+Ffe+743Gnuo1IptMBjQJDCd0EEssEGhYakyhIxQgPAbJQ== Received: from sv.mirrors.kernel.org (sv.mirrors.kernel.org. [2604:1380:45e3:2400::1]) by mx.google.com with ESMTPS id n4-20020a1709026a8400b001d8d052e9a1si5234160plk.586.2024.01.30.19.07.58 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 30 Jan 2024 19:07:58 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-45639-ouuuleilei=gmail.com@vger.kernel.org designates 2604:1380:45e3:2400::1 as permitted sender) client-ip=2604:1380:45e3:2400::1; Authentication-Results: mx.google.com; dkim=pass header.i=@codeweavers.com header.s=s1 header.b=CxRMRmK8; 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-45639-ouuuleilei=gmail.com@vger.kernel.org designates 2604:1380:45e3:2400::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-45639-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 sv.mirrors.kernel.org (Postfix) with ESMTPS id CB7E228AC6F for ; Wed, 31 Jan 2024 02:20:31 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3AF083A8F2; Wed, 31 Jan 2024 02:16:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=codeweavers.com header.i=@codeweavers.com header.b="CxRMRmK8" 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 961A2538A; Wed, 31 Jan 2024 02:16:03 +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=1706667367; cv=none; b=hvXsB86mjevxkQ4Kc2Vr8Xqkp+QuExkJCbC/bbpxEvN7F7BfC3Pk3gJeLb8d5MUVJYyYSP99T03npkDQrveOqMOU1MPk35cetKDEx1PJLcnduBlNHkZztDQ3bOAvTEtONqGojLaHSq8g0XacYU7V1XpzrmN+ryxtSRcwdhwtv3M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706667367; c=relaxed/simple; bh=blvWdmAlT8N2pI4NeUMKOVsakHhLte/Ud+yLGi+AMhY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uRdxcXwMtEmA5ibkNDPaQhbALqpFNymS+L84AmorWlFYPvdPvtewSLdldIdWI+RLWJ+VlthMxEplUjgK7hSpCKUv8eaEOVuOcP9zT2UIZ+5DP35Tv6sqZlmqTdCE9TphQSE8hjxh8DYLP79//1/0XGfq9PwxRkDK6bLCvgR7dc4= 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=CxRMRmK8; 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=dBqQoWEccHAFufDAEgUe6Vd0qbJ3tQL100kMCYoxJh0=; b=CxRMRmK8l3r4pC2PM/mTZLAsWW VoLkYLIYcBcbRA1oPBu1FMhm4kW7Bm3IaakaJMYUPDNmmnQB/3fx+vmze4diV6CdM504owE1BL3eX OZGuZXfNInLbiulypZ3IJku6m2p3mbCOFtd4lv8DUR14GKnZIS98QkA/W439TUy+W8M0AGLGaQFgZ GyKIfmFUCp7b0/azvcp6NArQZyu81SQbzH4NVbmlPcWd4Bz8YnhdMEHWsO+exAJ9EnA4nFp6/Ns0k XgBrTPfYbAQYToyBqIs7Gv7kdgrbwxnp6asSZrcC4PqU9mltlf47v/2gwUAAav4DzxRyN8+g3czLT rpAwAsOw==; 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 1rV08m-0038Kv-2R; Tue, 30 Jan 2024 20:15:52 -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: [RFC PATCH v2 04/29] ntsync: Introduce NTSYNC_IOC_WAIT_ANY. Date: Tue, 30 Jan 2024 20:13:31 -0600 Message-ID: <20240131021356.10322-5-zfigura@codeweavers.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240131021356.10322-1-zfigura@codeweavers.com> References: <20240131021356.10322-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: 1789573704087718012 X-GMAIL-MSGID: 1789573704087718012 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 | 232 ++++++++++++++++++++++++++++++++++++ include/uapi/linux/ntsync.h | 12 ++ 2 files changed, 244 insertions(+) diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c index 1af38969f9a2..0a0ab755d57f 100644 --- a/drivers/misc/ntsync.c +++ b/drivers/misc/ntsync.c @@ -34,12 +34,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. @@ -73,6 +116,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); @@ -126,6 +171,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; } @@ -176,6 +222,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, ktime_t *timeout) +{ + int ret = 0; + + 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, 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; + ktime_t timeout; + 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 */ + + timeout = ns_to_ktime(args.timeout); + ret = ntsync_schedule(q, args.timeout == U64_MAX ? NULL : &timeout); + + /* 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; @@ -207,6 +437,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)