From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.7 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,MSGID_FROM_MTA_HEADER,SPF_HELO_NONE, SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B1D56C433E0 for ; Fri, 22 Jan 2021 22:58:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6B29823ACA for ; Fri, 22 Jan 2021 22:58:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728384AbhAVW6g (ORCPT ); Fri, 22 Jan 2021 17:58:36 -0500 Received: from aserp2130.oracle.com ([141.146.126.79]:52960 "EHLO aserp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728346AbhAVW4F (ORCPT ); Fri, 22 Jan 2021 17:56:05 -0500 Received: from pps.filterd (aserp2130.oracle.com [127.0.0.1]) by aserp2130.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10MMt3wB154571; Fri, 22 Jan 2021 22:55:10 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : subject : date : message-id : in-reply-to : references : content-type : mime-version; s=corp-2020-01-29; bh=umFdQ+ZSG2n9xrmpohKBxGDuTN0xnsxY8Gh0gawBvOs=; b=GNki/m7CLVaMeNlKNguxdfT9ttJx1mhMOKrEmlfDXfxyWbISt0V1Nlp6pQPx9x3aNOjo nKzHY8aGL/uMupTHmO5QH5jUN0xIdzL3pmyKUxeIQygFKWuwyhFEzkrb+kCNNtDz9kue p0cAKbPh5Fs5hBfycDHHrJSCCtqtlcjek296bMkCUnae4XN6O74ZdsbPoThb9yPjoFbW AcAE8UjI4JxAVVuIJ702639eTcVSfLJR/wcxzd0XTQBqO+RwOX0FuqdLgoucM3AvS0wH bYAWWu7/S7B2wgzW4dzgdkQZERlnulvPCiPOqLcYL7GNsdHpK3cnz5eiwpcdmRUWREdO Uw== Received: from userp3020.oracle.com (userp3020.oracle.com [156.151.31.79]) by aserp2130.oracle.com with ESMTP id 3668qrpj7e-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 22 Jan 2021 22:55:10 +0000 Received: from pps.filterd (userp3020.oracle.com [127.0.0.1]) by userp3020.oracle.com (8.16.0.42/8.16.0.42) with SMTP id 10MMt0Op162490; Fri, 22 Jan 2021 22:55:09 GMT Received: from nam04-co1-obe.outbound.protection.outlook.com (mail-co1nam04lp2054.outbound.protection.outlook.com [104.47.45.54]) by userp3020.oracle.com with ESMTP id 3668r1pqjp-2 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 22 Jan 2021 22:55:09 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=O0ErvXEe2y8ndIXF/NGq9KPc1EsO0+dYom32/kLHLz0B6aQ2uGjmaSPRH/kcE9IyqwiyBV39TOfU7vsr/aJ8zplrSuEfFjNFJS0F8H3teBPMnCg3OYk7e050/FZPxNVBZ7BlNltBCqzkU0x+YmyuG8ziAdw0MIsxDWPhpYCQaP88t9nwx58mske8E4LYWVu4+EUaNSBAIhg7C1jrSJempMjhpN/WyPZ9MsDfHBOGthOztmOA8e8uLE1NVegHPcMfgAggPparkanI+YbU9n7IAJrm2iBKkMM72aEoS7+WnzzS/bm6PRSioCCguZpmPuGVQKjqXITcbZi1IzfjeQ/v5Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=umFdQ+ZSG2n9xrmpohKBxGDuTN0xnsxY8Gh0gawBvOs=; b=MHohmD69TvHg8GS6/7eLrB4s7YWBkWWukTpG5uIqRZow36cRP5Nuo7CNwS5M+eLmDO3YsmkrNv4/xXOaeEvZKFUMMbgBliCNb0fQVUQN8BQclu6ezsVloEVbtw+wIi2Q1b3njWYXhJDExp67NIABm/2e9lmuMVKvEGlkmYJPtqePzRf1bKZyrXkpvOhxVsT0TKNQJDNDVoJ4REJPDu7Oh4RnWk5ggqiXBUGS7aDNJgmF0x7ykZ+zunoIRza1gYfBy7MbrvqHkfe6loQhOUkA+Ry/FWm/bqTmBhldmYOdW+gxSrdhWJCqd6x+fEXo9t3LoksECY5zqicmV0zkOURZyA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=oracle.com; dmarc=pass action=none header.from=oracle.com; dkim=pass header.d=oracle.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.onmicrosoft.com; s=selector2-oracle-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=umFdQ+ZSG2n9xrmpohKBxGDuTN0xnsxY8Gh0gawBvOs=; b=a1Odtw5+vu/ESoqfYxtSUNBfDTdPZ1Hz8dbnUw4xUnep/OdmymCgnS+YfN9stYZUI55YS25Yw1jxQGUhUOnOyqgh7oQv5l6pB2sLnpm6cRSmfzf7TN8fnzrxUyzFnotPW1yIl7jNcP3T+efM9knN1TTLLSfslVFoEdt01LzRva0= Authentication-Results: kernel.dk; dkim=none (message not signed) header.d=none;kernel.dk; dmarc=none action=none header.from=oracle.com; Received: from BYAPR10MB3606.namprd10.prod.outlook.com (2603:10b6:a03:11b::27) by BY5PR10MB3827.namprd10.prod.outlook.com (2603:10b6:a03:1b4::21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3784.15; Fri, 22 Jan 2021 22:55:07 +0000 Received: from BYAPR10MB3606.namprd10.prod.outlook.com ([fe80::359f:18a0:4d25:9978]) by BYAPR10MB3606.namprd10.prod.outlook.com ([fe80::359f:18a0:4d25:9978%6]) with mapi id 15.20.3784.013; Fri, 22 Jan 2021 22:55:07 +0000 From: Bijan Mottahedeh To: axboe@kernel.dk, io-uring@vger.kernel.org Subject: [PATCH v2 05/10] test/buffer-share: add buffer registration sharing test Date: Fri, 22 Jan 2021 14:54:54 -0800 Message-Id: <1611356099-60732-6-git-send-email-bijan.mottahedeh@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1611356099-60732-1-git-send-email-bijan.mottahedeh@oracle.com> References: <1611356099-60732-1-git-send-email-bijan.mottahedeh@oracle.com> Content-Type: text/plain X-Originating-IP: [138.3.200.3] X-ClientProxiedBy: CH0PR04CA0047.namprd04.prod.outlook.com (2603:10b6:610:77::22) To BYAPR10MB3606.namprd10.prod.outlook.com (2603:10b6:a03:11b::27) MIME-Version: 1.0 X-MS-Exchange-MessageSentRepresentingType: 1 Received: from ca-ldom147.us.oracle.com (138.3.200.3) by CH0PR04CA0047.namprd04.prod.outlook.com (2603:10b6:610:77::22) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3784.11 via Frontend Transport; Fri, 22 Jan 2021 22:55:06 +0000 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: 4f1ae0d0-3f69-4ebb-d29a-08d8bf28c367 X-MS-TrafficTypeDiagnostic: BY5PR10MB3827: X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:18; X-MS-Exchange-SenderADCheck: 1 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: Jm0nQyuslsJBitNuZDZr4bLeaH4s1xknJTQHGao5NbNob27xHxXlzMfMmlsMDnastA5vqRajs9TKSnpF7JXpjydVOkxdKGQB5iyqEOnoEx+yrGW4w9AqVGszgu+Ms3K4hwOAAyDTxqzka1TN+avKQI5M+1hIII9iVEuxyNVaLaCCQXMhIQA7n7Dyy2VRtDVFOBJkzl47j9+FbEdWnUp2hjo+bLVRrr+LnuxnCw13PvwGh5IsiNbh9+j+F3MulRqY2fqmhf6BzZgqumgegbUrFKvhcqWONhAbEyo3lQkhhtPpywivwNN+0KWKCWGpQKFgKKOxAcKVy5Z5pcTGdnu7JJVpWqgB58ASQJ1prI6snvb6VhTPO8nKRKzAwBARJ2OrnMxrjxu+pFFYg4uOsH6NRA== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BYAPR10MB3606.namprd10.prod.outlook.com;PTR:;CAT:NONE;SFS:(136003)(396003)(366004)(39860400002)(376002)(346002)(83380400001)(44832011)(316002)(52116002)(36756003)(7696005)(2616005)(956004)(6666004)(66556008)(66946007)(66476007)(2906002)(478600001)(26005)(86362001)(5660300002)(8936002)(8676002)(16526019)(30864003)(6486002)(186003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData: =?us-ascii?Q?4+J74vvrXZuGs/+4uX9kIuCFPZb9wrrfzr0OhgzLPXXKE2ryVsAnaaWVDJ4N?= =?us-ascii?Q?eKJOQAbkuy0rZOqQfi0wDjrGJE4T/2y3d0AWgrjmvxtYHQgZuRd7wJKFL01f?= =?us-ascii?Q?YaT5pu8iZxmdA0ln2H6EaLHG5PpsrAblrSPhUWwcNKXE7ZZHW+ln4LLFZAGG?= =?us-ascii?Q?4fJzUV0mglWS62LuI4EZ4+UYDBREM6uVjynOmXuDcLXn42QDBMxT33sE3uNl?= =?us-ascii?Q?GJ4Zag8KAVFolhH1Y1TBUMnVULhIcUqzX/dIIld21jxHfUEc0Roo/VflTsTl?= =?us-ascii?Q?0fjciB2zhcR8/Jzi6rXBL9zZ0Ep3UWBA5DUCoOgkGP6NeH1UvKjoCTHOiEJT?= =?us-ascii?Q?vT+Lohy4+AdMx6RPcA/cOXWOReZeN1I8pQJX+eGDSlVUoXvX9LzCAWP+htND?= =?us-ascii?Q?8lLSGvlPq8iDGrzMus59AQMx2qkzMUFUkK/YEBDA4I8TxzskRB3IwXo08WT8?= =?us-ascii?Q?Zndjq02uvDN2iOiVuFslvTCPDA/GYdxDyMo1TaHgIxyEMjvIPcrFPQAiU/pi?= =?us-ascii?Q?uafzmepeCHWAdZhY1yu9RgHVF5Ye1RuQJgRnUk9Rv3o4tGmqqZrVeaySFmFg?= =?us-ascii?Q?upKQJRkbYoDlVtThPYsZl6fPMhEZ0WCezig9+dduEYX0+TfU8xMiY38VCQ5f?= =?us-ascii?Q?NWA8pXssBsFZ481XVgxOlW4onaqHmYH+5RthyR1JuZKcnvBav0Nzd7wG2ScJ?= =?us-ascii?Q?cSkcfOvD+OwJPhxpYfsoj0pbf2rgbi7OzZPVYyQK4gr/K3yLe7Em+knxbr9U?= =?us-ascii?Q?0HMUgprOrldgpYqGjVhE4g/SVhUNIaO1R2KRYQ5EKzurMEvDkH1glHovuwSz?= =?us-ascii?Q?GFKSn10Wj+qCqeWLxV+ndtGhbCNr2atcme2O2Mhf4Twjgp0EnUNZwLn44vyA?= =?us-ascii?Q?jeP51Nmucp92B2P10SOHxs8II4g6J2Pfnv3/5sFuHi4e/ttjQ6SLl795pPfG?= =?us-ascii?Q?+hb0vh/uvQK+kEXbBtZMilUVxRBh9DfVYaO+qzOOvDk=3D?= X-OriginatorOrg: oracle.com X-MS-Exchange-CrossTenant-Network-Message-Id: 4f1ae0d0-3f69-4ebb-d29a-08d8bf28c367 X-MS-Exchange-CrossTenant-AuthSource: BYAPR10MB3606.namprd10.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jan 2021 22:55:07.2632 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 4e2c6054-71cb-48f1-bd6c-3a9705aca71b X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: MEGfn/6dFi6jKGcjzwlL/ICBeANJvGTYBpXidRQe4Ov1zs3DoSEc2wPGkpefLBF22fyjacgclgWTkCIfP/gTSlePGsoAeSU4canfLgeUbQ4= X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY5PR10MB3827 X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=9872 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 bulkscore=0 phishscore=0 adultscore=0 malwarescore=0 mlxscore=0 mlxlogscore=999 spamscore=0 suspectscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101220118 X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=9872 signatures=668683 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 mlxlogscore=999 mlxscore=0 suspectscore=0 lowpriorityscore=0 bulkscore=0 adultscore=0 spamscore=0 phishscore=0 priorityscore=1501 impostorscore=0 malwarescore=0 clxscore=1015 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2101220118 Precedence: bulk List-ID: X-Mailing-List: io-uring@vger.kernel.org Signed-off-by: Bijan Mottahedeh --- .gitignore | 1 + test/Makefile | 3 + test/buffer-share.c | 1184 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1188 insertions(+) create mode 100644 test/buffer-share.c diff --git a/.gitignore b/.gitignore index b44560e..4e09f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ /test/b19062a56726-test /test/b5837bd5311d-test /test/buffer-register +/test/buffer-share /test/buffer-update /test/ce593a6c480a-test /test/close-opath diff --git a/test/Makefile b/test/Makefile index 79e3e00..c9db730 100644 --- a/test/Makefile +++ b/test/Makefile @@ -30,6 +30,7 @@ test_targets += \ b19062a56726-test \ b5837bd5311d-test \ buffer-register \ + buffer-share \ buffer-update \ ce593a6c480a-test \ close-opath \ @@ -155,6 +156,7 @@ test_srcs := \ b19062a56726-test.c \ b5837bd5311d-test.c \ buffer-register.c \ + buffer-share.c \ buffer-update.c \ ce593a6c480a-test.c \ close-opath.c \ @@ -255,6 +257,7 @@ wakeup-hang: XCFLAGS = -lpthread pipe-eof: XCFLAGS = -lpthread timeout-new: XCFLAGS = -lpthread thread-exit: XCFLAGS = -lpthread +buffer-share: XCFLAGS = -lrt install: $(test_targets) runtests.sh runtests-loop.sh $(INSTALL) -D -d -m 755 $(datadir)/liburing-test/ diff --git a/test/buffer-share.c b/test/buffer-share.c new file mode 100644 index 0000000..81e0537 --- /dev/null +++ b/test/buffer-share.c @@ -0,0 +1,1184 @@ +/* SPDX-License-Identifier: MIT */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "liburing.h" + +/* + * The main (granparent) process creates a shared memory status segment for + * all tests and then creates a parent process which drives test execution. + * The grandparent then waits to reap the status of granchildren test + * processes, for the final test case which involves running IO tests after + * the parent exits. + * + * The parent process creates a shared memory data segment where all IO + * buffers reside. The parent then runs all test cases. For each test + * the parent creates the specified number of test processes, and waits + * for the children to execute the actual tests. The parent controls + * children's execution with ptrace(). + * + * The tests are organized as follows: + * + * test ring registration test expected + * category sharing type case result + * -------- ------- --- ---- ------ + * registration none none various pass + * IO fd based register/pin failure errno + * attach + * + * The IO test has been adapted largely unchanged from test/read-write.c. + */ + +#define MAXID 128 /* max number of tester processes */ +int pids[MAXID]; +int status[MAXID]; +int nids = 1; + +#define SHM_ST_SZ (MAXID * 4) +#define SHM_ST_NONE -1 + +#define FILE_SIZE (128 * 1024) +#define BS 4096 +#define BUFFERS (FILE_SIZE / BS) + +#define SZ_2M 0x200000 + +#define SQ_THREAD_IDLE 2000 + +char *shmbuf; +char *shmstat; + +struct iovec vecs[UIO_MAXIOV]; +unsigned long buf_cnt = BUFFERS; +unsigned long shm_sz = SZ_2M; + +int no_read; +int warned; +int verbose = 0; + +enum { + REG_NOP = 0, + REG_PIN, + REG_ATT, +}; + +char *r2s[] = { + "nop", + "pin", + "att", +}; + +enum { + TEST_DFLT = 0, + TEST_INVFD, + TEST_BADFD, + TEST_UNREG, + TEST_BUSY, + TEST_EXIT, + TEST_FIN, +}; + +char *t2s[] = { + "dflt", + "invfd", + "badfd", + "unreg", + "busy", + "exit", + "fin", +}; + +enum { + TEST_REG = 0, + TEST_IO, +}; + +char *i2s[] = { + "reg", + "io", +}; + +enum { + NO, + FD, +}; + +char *s2s[] = { + "nos", + "fd", +}; + +enum { + PASS, + FAIL, + NONE, +}; + +enum { + V0 = 0, + V1, + V2, + V3, + V4, +}; + +struct test_desc { + int id; + int idx; + int fd; + int sp[2]; + int share; + int reg; + int preg; + int test; + int expect; +}; +struct test_desc tdcs[MAXID]; + +static int test_reg(struct test_desc *); +static int test_io(struct test_desc *); + +int (*test_fn[])(struct test_desc *) = { + test_reg, + test_io, +}; + +static void +vinfo(int level, char *fmt, ...) +{ + va_list args; + + if (verbose < level) + return; + + fprintf(stderr, "%-5d ", getpid()); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fflush(stderr); +} + +static void +verror(char *msg) +{ + if (!verbose) + return; + + fprintf(stderr, "%-5d %s: %s\n", getpid(), msg, strerror(errno)); + fflush(stderr); +} + +static void +send_fd(int socket, int fd) +{ + char buf[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; + struct msghdr msg; + + memset(buf, 0, sizeof(buf)); + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + + memmove(CMSG_DATA(cmsg), &fd, sizeof(fd)); + + msg.msg_controllen = CMSG_SPACE(sizeof(fd)); + + if (sendmsg(socket, &msg, 0) < 0) { + verror("sendmsg"); + exit(1); + } +} + +static int +recv_fd(int socket) +{ + int *fdp, fd = -1, ret; + char buf[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; + struct msghdr msg; + + memset(buf, 0, sizeof(buf)); + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + + ret = recvmsg(socket, &msg, 0); + if (ret < 0) { + verror("recvmsg"); + exit(1); + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg,cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fdp = (int *)CMSG_DATA(cmsg); + fd = *fdp; + } + } + + return fd; +} + +static int +shm_create(void) +{ + int id; + + id = shmget(2, shm_sz, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W); + if (id < 0) { + fprintf(stderr, "Unable to map a huge page. " + "Increase /proc/sys/vm/nr_hugepages by at least 1.\n"); + verror("shmget"); + exit(1); + } + vinfo(V3, "shm_create shmid: 0x%x\n", id); + + return id; +} + +static char * +shm_attach(int id) +{ + char *addr; + + addr = shmat(id, 0, 0); + if (addr == MAP_FAILED) { + verror("shmat"); + shmctl(id, IPC_RMID, NULL); + } + vinfo(V3, "shm_attach shmid: 0x%x shmbuf: %p\n", id, addr); + + return addr; +} + +static void +shm_destroy(int id, char *addr) +{ + int ret; + + vinfo(V3, "shm_destroy shmid: 0x%x\n", id); + + ret = shmdt((const void *)addr); + if (ret) + verror("shmdt"); + + shmctl(id, IPC_RMID, NULL); + + if (ret) + exit(1); +} + +static void * +shmstat_create(void) +{ + int res; + int fd; + char fname[32]; + void *addr = NULL; + + snprintf(fname, sizeof(fname), "shmstat.%d", getpid()); + + /* get shared memory file descriptor (NOT a file) */ + fd = shm_open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + verror("open"); + goto done; + } + + /* extend shared memory object as by default it's set to size 0 */ + res = ftruncate(fd, SHM_ST_SZ); + if (res == -1) { + verror("ftruncate"); + goto done; + } + + /* map shared memory to process address space */ + addr = mmap(NULL, SHM_ST_SZ, PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + verror("mmap"); + goto done; + } + memset(addr, 0, SHM_ST_SZ); +done: + return addr; +} + +static void +shmstat_store(struct test_desc *tdc, int data) +{ + vinfo(V4, "stat_store %i %d\n", tdc->idx, data); + shmstat[tdc->idx] = data; +} + +static int +shmstat_check(int expect) +{ + int i; + + for (i = 0; i < nids; i++) { + if (shmstat[i] != expect) { + vinfo(V3, "check_shmstat child %d: expect %d got %d\n", + pids[i], expect, shmstat[i]); + return shmstat[i]; + } + } + + return 0; +} + +static int +queue_init(struct test_desc *tdc, int qd, struct io_uring *ring, + struct io_uring_params *p, int reg) +{ + int ret; + + if (tdc->share == FD) { + if (reg == REG_ATT) { + p->flags |= IORING_SETUP_ATTACH_BUF; + if (tdc->test == TEST_BADFD) + p->wq_fd = -1; + else if (tdc->test == TEST_INVFD) + p->wq_fd = 0; + else + p->wq_fd = tdc->fd; + } else + p->flags |= IORING_SETUP_SHARE_BUF; + } + + ret = io_uring_queue_init_params(qd, ring, p); + if (ret) + verror("queue_init"); + vinfo(V2, "queue_init %d\n", ring->ring_fd); + + return ret; +} + +static void +queue_exit(struct io_uring *ring) +{ + vinfo(V2, "queue_exit %d\n", ring->ring_fd); + io_uring_queue_exit(ring); +} + +static void +create_buffers(char *adr) +{ + int i; + + if (buf_cnt > UIO_MAXIOV) { + fprintf(stderr, "invalid buffer count: %ld\n", buf_cnt); + exit(1); + } + + for (i = 0; i < buf_cnt; i++) { + vecs[i].iov_base = adr; + vecs[i].iov_len = BS; + adr += BS; + } +} + +static int +register_buffers(struct test_desc *tdc, struct io_uring *ring) +{ + if (tdc->reg == REG_ATT) + return io_uring_register_buffers(ring, 0, 0); + + return io_uring_register_buffers(ring, vecs, buf_cnt); +} + +static void +stop(void) +{ + vinfo(V2, "raise SIGSTOP\n"); + raise(SIGSTOP); + vinfo(V2, "resume SIGSTOP\n"); +} + +static int +test_reg(struct test_desc *tdc) +{ + int ret; + struct io_uring_params p = {0}; + struct io_uring ring; + + ret = queue_init(tdc, 1, &ring, &p, tdc->reg); + if (ret) + return ret; + + ret = register_buffers(tdc, &ring); + if (ret) + verror("register test_reg"); + + if (tdc->test == TEST_BUSY) { + vinfo(V2, "delay\n"); + sleep(5); + } else if (tdc->test == TEST_EXIT) { + stop(); + } + + queue_exit(&ring); + + return ret; +} + +static int +create_file(const char *file) +{ + ssize_t ret; + char *buf; + int fd; + + buf = malloc(FILE_SIZE); + memset(buf, 0xaa, FILE_SIZE); + + fd = open(file, O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + verror("open"); + return 1; + } + ret = write(fd, buf, FILE_SIZE); + close(fd); + return ret != FILE_SIZE; +} + +static int +do_io(struct test_desc *tdc, const char *file, + struct io_uring *ring, int write, int buffered, + int sqthread, int fixed, int nonvec, int seq, int exp_len) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int open_flags; + int i, fd, ret = 0; + off_t offset; + + vinfo(V1, "%s: %d/%d/%d/%d/%d\n", + __FUNCTION__, write, buffered, sqthread, fixed, nonvec); + + if (sqthread && geteuid()) { + vinfo(V1, "SKIPPED (not root)\n"); + return 0; + } + + if (write) + open_flags = O_WRONLY; + else + open_flags = O_RDONLY; + if (!buffered) + open_flags |= O_DIRECT; + + fd = open(file, open_flags); + if (fd < 0) { + verror("open"); + goto err; + } + + if (fixed) { + ret = register_buffers(tdc, ring); + if (ret) { + vinfo(V1, "buffer reg failed: %d\n", ret); + goto err; + } + } + if (fixed || sqthread) { + ret = io_uring_register_files(ring, &fd, 1); + if (ret) { + vinfo(V1, "file reg failed: %d\n", ret); + goto err; + } + } + + offset = 0; + for (i = 0; i < BUFFERS; i++) { + sqe = io_uring_get_sqe(ring); + if (!sqe) { + vinfo(V1, "sqe get failed\n"); + goto err; + } + if (!seq) + offset = BS * (rand() % BUFFERS); + if (write) { + int use_fd = fd; + + if (fixed || sqthread) + use_fd = 0; + + if (fixed) { + io_uring_prep_write_fixed(sqe, use_fd, + vecs[i].iov_base, + vecs[i].iov_len, + offset, i); + } else if (nonvec) { + io_uring_prep_write(sqe, use_fd, + vecs[i].iov_base, + vecs[i].iov_len, offset); + } else { + io_uring_prep_writev(sqe, use_fd, &vecs[i], 1, + offset); + } + } else { + int use_fd = fd; + + if (fixed || sqthread) + use_fd = 0; + + if (fixed) { + io_uring_prep_read_fixed(sqe, use_fd, + vecs[i].iov_base, + vecs[i].iov_len, + offset, i); + } else if (nonvec) { + io_uring_prep_read(sqe, use_fd, + vecs[i].iov_base, + vecs[i].iov_len, offset); + } else { + io_uring_prep_readv(sqe, use_fd, &vecs[i], 1, + offset); + } + + } + if (fixed || sqthread) + sqe->flags |= IOSQE_FIXED_FILE; + if (seq) + offset += BS; + } + + ret = io_uring_submit(ring); + if (ret != BUFFERS) { + vinfo(V1, "submit got %d, wanted %d\n", ret, BUFFERS); + goto err; + } + + for (i = 0; i < BUFFERS; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + vinfo(V1, "wait_cqe %d\n", ret); + goto err; + } + if (cqe->res == -EINVAL && nonvec) { + if (!warned) { + vinfo(V1, "Non-vectored IO not " + "supported, skipping\n"); + warned = 1; + no_read = 1; + } + } else if (cqe->res != exp_len) { + vinfo(V1, "cqe res %d, wanted %d\n", cqe->res, exp_len); + ret = cqe->res; + goto err; + } + io_uring_cqe_seen(ring, cqe); + } + + if (fixed) { + ret = io_uring_unregister_buffers(ring); + if (ret) { + vinfo(V1, "buffer unreg failed: %d\n", ret); + goto err; + } + } + if (fixed || sqthread) { + ret = io_uring_unregister_files(ring); + if (ret) { + vinfo(V1, "file unreg failed: %d\n", ret); + goto err; + } + } + + close(fd); + vinfo(V1, "%s: %d/%d/%d/%d/%d: PASS\n", + __FUNCTION__, write, buffered, sqthread, fixed, nonvec); + return 0; +err: + (void)io_uring_unregister_buffers(ring); + if (fd != -1) + close(fd); + return ret; +} + +static int +run_test_io(struct test_desc *tdc, const char *file, + int write, int buffered, int sqthread, int fixed, int nonvec) +{ + struct io_uring_params p = {0}; + struct io_uring ring; + int share, ret; + + share = tdc->share; + + if (!fixed) + tdc->share = NO; + + if (sqthread) { + if (geteuid()) { + if (!warned) { + fprintf(stderr, + "SQPOLL requires root, skipping\n"); + warned = 1; + } + return 0; + } + p.flags = IORING_SETUP_SQPOLL; + p.sq_thread_idle = nids * SQ_THREAD_IDLE; + } + ret = queue_init(tdc, 64, &ring, &p, tdc->reg); + + if (ret) + goto done; + + ret = do_io(tdc, file, &ring, write, buffered, sqthread, fixed, + nonvec, 0, BS); + + queue_exit(&ring); +done: + tdc->share = share; + return ret; +} + + +static int +has_nonvec_read(void) +{ + struct io_uring_probe *p; + struct io_uring ring; + int ret; + + ret = io_uring_queue_init(1, &ring, 0); + vinfo(V1, "io_uring_queue_init %d\n", ring.ring_fd); + if (ret) { + verror("io_uring_queue_init"); + exit(ret); + } + + p = calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op)); + ret = io_uring_register_probe(&ring, p, 256); + /* if we don't have PROBE_REGISTER, we don't have OP_READ/WRITE */ + if (ret == -EINVAL) { +out: + queue_exit(&ring); + return 0; + } else if (ret) { + fprintf(stderr, "register_probe: %d\n", ret); + goto out; + } + + if (p->ops_len <= IORING_OP_READ) + goto out; + if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED)) + goto out; + queue_exit(&ring); + return 1; +} + +static int +test_io(struct test_desc *tdc) +{ + int i, ret, nr; + char fname[32]; + + snprintf(fname, sizeof(fname), ".basic-rw.%d", getpid()); + if (create_file(fname)) { + fprintf(stderr, "file creation failed\n"); + ret = 1; + goto err; + } + + if (tdc->test == TEST_EXIT) + stop(); + + /* if we don't have nonvec read, skip testing that */ + if (has_nonvec_read()) + nr = 64; + else + nr = 32; + + for (i = 0; i < nr; i++) { + int v1, v2, v3, v4, v5; + + v1 = (i & 1) != 0; + v2 = (i & 2) != 0; + v3 = (i & 4) != 0; + v4 = (i & 8) != 0; + v5 = (i & 16) != 0; + ret = run_test_io(tdc, fname, v1, v2, v3, v4, v5); + if (ret) { + vinfo(V1, "test_io failed %d/%d/%d/%d/%d\n", + v1, v2, v3, v4, v5); + goto err; + } + } +err: + unlink(fname); + return ret; +} + +static int +get_socketpair(int sp[]) +{ + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) != 0) { + verror("socketpair\n"); + return errno; + } + return 0; +} + +static void +waitstoppid(int pid) +{ + int ret; + int status; + + while (1) { + vinfo(V2, "waitstop %d\n", pid); + + ret = waitpid(pid, &status, WNOHANG | WUNTRACED | WCONTINUED); + if (ret != pid) { + sleep(1); + continue; + } + if (WIFSTOPPED(status)) + return; + + break; + } + + vinfo(V2, "pid %d was not stopped!\n", pid); + exit(1); +} + +static void +waitstop(void) +{ + int i; + + for (i = 0; i < nids; i++) + waitstoppid(pids[i]); +} + +static void +waitexitpid(int pid, int *status) +{ + int ret, xs; + + while (1) { + vinfo(V2, "waitexit %d\n", pid); + + ret = waitpid(pid, status, WNOHANG | WUNTRACED | WCONTINUED); + if (ret != pid) { + sleep(1); + continue; + } + xs = *status; + if (WIFEXITED(xs)) { + vinfo(2, "child %d exited: %d\n", pid, WEXITSTATUS(xs)); + *status = WEXITSTATUS(xs); + return; + } + + vinfo(V2, "waitexit 0x%x e:%d s:%d d:%d t:%d c:%d\n", xs, + WIFEXITED(xs), WIFSIGNALED(xs), WCOREDUMP(xs), + WIFSTOPPED(xs), WIFCONTINUED(xs)); + break; + } + + vinfo(V2, "pid %d didn't exit!\n", pid); + exit(1); +} + +static void +waitexit(void) +{ + int i; + + for (i = 0; i < nids; i++) { + status[i] = 0; + waitexitpid(pids[i], &status[i]); + } +} + +static void +contpid(int pid) +{ + int ret; + + vinfo(V2, "cont %d\n", pid); + + ret = ptrace(PTRACE_CONT, pid, NULL, NULL); + if (ret < 0) { + verror("ptrace"); + exit(1); + } +} + +static void +cont(void) +{ + int i; + + for (i = 0; i < nids; i++) + contpid(pids[i]); +} + +static void +send_ring_fd(int fd) +{ + int i; + struct test_desc *tdc; + + for (i = 0; i < nids; i++) { + tdc = &tdcs[i]; + vinfo(V3, "send_fd %d %d: %d\n", pids[i], tdc->sp[0], fd); + send_fd(tdc->sp[0], fd); + } +} + +static void +close_sockets(void) +{ + int i; + + for (i = 0; i < nids; i++) { + shutdown(tdcs[i].sp[0], SHUT_RDWR); + shutdown(tdcs[i].sp[1], SHUT_RDWR); + } +} + +static int +run_parent(struct test_desc *tdc) +{ + int ret; + struct io_uring_params p = {0}; + struct io_uring ring; + + waitstop(); + + ret = queue_init(tdc, 1, &ring, &p, tdc->preg); + if (ret) + return ret; + + if (tdc->preg == REG_NOP) + goto send; + + ret = io_uring_register_buffers(&ring, vecs, buf_cnt); + if (ret) { + verror("register run_parent"); + goto done; + } + + if (tdc->test == TEST_UNREG) { + ret = io_uring_unregister_buffers(&ring); + if (ret) { + verror("unregister run_parent"); + goto done; + } + } +send: + if (tdc->share == FD) + send_ring_fd(ring.ring_fd); + + cont(); + + if (tdc->test == TEST_BUSY) { + ret = io_uring_unregister_buffers(&ring); + if (ret) + verror("unregister run_parent busy"); + if (-ret == tdc->expect) + ret = -ret; + } else if (tdc->test == TEST_EXIT) { + waitstop(); + vinfo(2, "parent exiting\n"); + exit(2); + } + + waitexit(); + if (!ret && tdc->test == TEST_FIN) { + ret = io_uring_unregister_buffers(&ring); + if (ret) + verror("unregister run_parent fin"); + } + +done: + queue_exit(&ring); + + if (ret) + cont(); + + return ret; +} + +static void +run_child(struct test_desc *tdc) +{ + int from_fd, ret = ECONNABORTED; + int (*fn)(struct test_desc *); + + vinfo(V1, "child stop\n"); + + ptrace(PTRACE_TRACEME, 0, NULL, NULL); + raise(SIGSTOP); + + if (tdc->share == FD) { + vinfo(V3, "recv_fd %d\n", tdc->sp[1]); + from_fd = recv_fd(tdc->sp[1]); + if (from_fd < 0) + goto done; + vinfo(V3, "recv_fd %d: %d\n", tdc->sp[1], from_fd); + tdc->fd = from_fd; + } + + fn = test_fn[tdc->id]; + ret = fn(tdc); + + if (tdc->share == FD) + close_sockets(); + +done: + vinfo(V2, "child exiting %d\n", ret); + /* + * The parent exits for the final test before waiting for children + * so save the test status. + */ + shmstat_store(tdc, -ret); + exit(-ret); +} + +static int +run_test(struct test_desc *proto) +{ + int i, pid, ret; + struct test_desc *tdc = NULL; + + ret = pid = 0; + + for (i = 0; i < nids; i++) { + tdcs[i] = *proto; + tdc = & tdcs[i]; + tdc->idx = i; + if (tdc->share == FD) { + ret = get_socketpair(tdc->sp); + if (ret) + return -ret; + } + pid = fork(); + if (pid) { + pids[i] = pid; + continue; + } + run_child(tdc); + } + + ret = run_parent(proto); + + if (proto->share == FD) + close_sockets(); + + return ret; +} + +static int +check_exitstat(struct test_desc *tdc, int *stat) +{ + int i, ret = 0; + + for (i = 0; i < nids; i++) { + if (status[i] != tdc->expect) { + vinfo(V1, "check_exitstat child %d: expect %d got %d\n", + pids[i], tdc->expect, status[i]); + if (!ret) { + ret = 1; + *stat = status[i]; + } + } + } + + if (!ret) + *stat = status[0]; + + return ret; +} + +static int +test(int id, int share, int preg, int reg, int test, int expect) +{ + int ret, stat; + char *res; + + struct test_desc tdc = { + .id = id, + .fd = -1, + .sp = {-1, -1}, + .share = share, + .preg = preg, + .reg = reg, + .test = test, + .expect = expect, + }; + + vinfo(V1, "test(%s %s %s %s %s)\n", i2s[tdc.id], s2s[tdc.share], + r2s[tdc.preg], r2s[tdc.reg], t2s[tdc.test]); + + memset(tdcs, 0, sizeof(tdcs)); + memset(pids, 0, sizeof(pids)); + memset(status, 0, sizeof(status)); + memset(shmstat, SHM_ST_NONE, SHM_ST_SZ); + + ret = run_test(&tdc); + stat = ret; + + if (ret) { + ret = 1; + res = "FAIL RUN"; + } else { + ret = check_exitstat(&tdc, &stat); + res = ret ? "FAIL" : "PASS"; + } + + vinfo(V1, "test(%s %s %s %s %s: %d %d) %s\n", + i2s[tdc.id], s2s[tdc.share], r2s[tdc.preg], + r2s[tdc.reg], t2s[tdc.test], tdc.expect, stat, res); + + return ret; +} + +static int +test_exit(int expect) +{ + int status, i, ret = 0; + + vinfo(V1, "grandparent pid\n"); + + wait(&status); + + vinfo(V2, "parent exited: %d\n", WEXITSTATUS(status)); + + for (i = 0; i < 60; i++) { + ret = shmstat_check(expect); + if (ret >= 0) + break; + sleep(1); + } + + if (ret) { + vinfo(V1, "Bad test_exit stat %d\n", ret); + ret = 1; + } + + vinfo(V1, "Test exit %d\n", ret); + exit(ret); +} + +/* + * main() + * -> shm_create() + * -> create_buffers() + * parent: + * -> test() + * -> run_test() + * -> run_parent() + * -> io_uring_register_buffers() + * -> run_child() + * -> test_reg() + * -> register_buffers() + * -> test_io() + * -> run_test_io() + * -> do_io() + * -> register_buffers() + * + * grandparent: + * -> test_exit() + */ + +int +main(int argc, char *argv[]) +{ + int shmid, pid, ret = 0; + char c; + + while ((c = getopt(argc, argv, "s:v:")) != -1) + switch (c) { + case 's': + shm_sz = atoi(optarg) * SZ_2M; + break; + case 'v': + verbose = atoi(optarg); + break; + default: + exit(1); + } + + argc -= optind; + argv += optind; + + if (argc) + nids = atoi(argv[0]); + + if (!nids || nids > MAXID) + exit(1); + + shmstat = shmstat_create(); + if (shmstat == MAP_FAILED) + exit(1); + + pid = fork(); + if (pid) + exit(test_exit(PASS)); + + vinfo(V1, "parent pid\n"); + + shmid = shm_create(); + if (shmid < 0) + exit(1); + + shmbuf = shm_attach(shmid); + if (shmbuf == MAP_FAILED) { + shm_destroy(shmid, shmbuf); + exit(1); + } + + create_buffers(shmbuf); + + ret |= test(TEST_REG, NO, REG_PIN, REG_PIN, TEST_DFLT, PASS); + ret |= test(TEST_REG, NO, REG_NOP, REG_NOP, TEST_DFLT, PASS); + ret |= test(TEST_REG, NO, REG_NOP, REG_ATT, TEST_DFLT, EINVAL); + ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_DFLT, PASS); + ret |= test(TEST_REG, FD, REG_NOP, REG_ATT, TEST_DFLT, PASS); + ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_BUSY, PASS); + ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_FIN, PASS); + ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_BADFD, EBADF); + ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_INVFD, EINVAL); + ret |= test(TEST_REG, FD, REG_PIN, REG_ATT, TEST_UNREG, EINVAL); + ret |= test(TEST_IO, FD, REG_PIN, REG_PIN, TEST_DFLT, PASS); + ret |= test(TEST_IO, FD, REG_PIN, REG_ATT, TEST_DFLT, PASS); + ret |= test(TEST_IO, FD, REG_NOP, REG_ATT, TEST_DFLT, EFAULT); + ret |= test(TEST_IO, FD, REG_PIN, REG_ATT, TEST_EXIT, NONE); + + shm_destroy(shmid, shmbuf); + + return ret; +} -- 1.8.3.1