]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/kern/fdgrowtable_test.c
Optionally bind ktls threads to NUMA domains
[FreeBSD/FreeBSD.git] / tests / sys / kern / fdgrowtable_test.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2020 Rob Wing
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/filedesc.h>
33 #include <sys/queue.h>
34 #include <sys/sysctl.h>
35 #include <sys/user.h>
36 #include <sys/wait.h>
37
38 #include <atf-c.h>
39 #include <fcntl.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 /* linked libraries */
47 #include <kvm.h>
48 #include <libutil.h>
49 #include <libprocstat.h>
50 #include <pthread.h>
51
52 /* test-case macro */
53 #define AFILE "afile"
54
55 /*
56  * The following macros, struct freetable, struct fdescenttbl0
57  * and struct filedesc0 are copied from sys/kern/kern_descrip.c
58  */
59 #define NDFILE          20
60 #define NDSLOTSIZE      sizeof(NDSLOTTYPE)
61 #define NDENTRIES       (NDSLOTSIZE * __CHAR_BIT)
62 #define NDSLOT(x)       ((x) / NDENTRIES)
63 #define NDBIT(x)        ((NDSLOTTYPE)1 << ((x) % NDENTRIES))
64 #define NDSLOTS(x)      (((x) + NDENTRIES - 1) / NDENTRIES)
65
66 struct freetable {
67         struct fdescenttbl *ft_table;
68         SLIST_ENTRY(freetable) ft_next;
69 };
70
71 struct fdescenttbl0 {
72         int     fdt_nfiles;
73         struct  filedescent fdt_ofiles[NDFILE];
74 };
75
76 struct filedesc0 {
77         struct filedesc fd_fd;
78         SLIST_HEAD(, freetable) fd_free;
79         struct  fdescenttbl0 fd_dfiles;
80         NDSLOTTYPE fd_dmap[NDSLOTS(NDFILE)];
81 };
82
83 static void
84 openfiles(int n)
85 {
86         int i, fd;
87
88         ATF_REQUIRE((fd = open(AFILE, O_CREAT, 0644)) != -1);
89         close(fd);
90         for (i = 0; i < n; i++)
91                 ATF_REQUIRE((fd = open(AFILE, O_RDONLY, 0644)) != -1);
92 }
93
94 /*
95  * Get a count of the old file descriptor tables on the freelist.
96  */
97 static int
98 old_tables(kvm_t *kd, struct kinfo_proc *kp)
99 {
100         struct filedesc0 fdp0;
101         struct freetable *ft, tft;
102         int counter;
103
104         counter = 0;
105
106         ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp0, sizeof(fdp0)) > 0);
107
108         SLIST_FOREACH(ft, &fdp0.fd_free, ft_next) {
109                 ATF_REQUIRE(kvm_read(kd, (unsigned long) ft, &tft, sizeof(tft)) > 0 );
110                 ft = &tft;
111                 counter++;
112         }
113
114         return (counter);
115 }
116
117 /*
118  *  The returning struct kinfo_proc stores kernel addresses that will be
119  *  used by kvm_read to retrieve information for the current process.
120  */
121 static struct kinfo_proc *
122 read_kinfo(kvm_t *kd)
123 {
124         struct kinfo_proc *kp;
125         int procs_found;
126
127         ATF_REQUIRE((kp = kvm_getprocs(kd, KERN_PROC_PID, (int) getpid(), &procs_found)) != NULL);
128         ATF_REQUIRE(procs_found == 1);
129
130         return (kp);
131 }
132
133 /*
134  * Test a single threaded process that doesn't have a shared
135  * file descriptor table. The old tables should be freed.
136  */
137 ATF_TC(free_oldtables);
138 ATF_TC_HEAD(free_oldtables, tc)
139 {
140         atf_tc_set_md_var(tc, "require.user", "root");
141 }
142
143 ATF_TC_BODY(free_oldtables, tc)
144 {
145         kvm_t *kd;
146         struct kinfo_proc *kp;
147
148         ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
149         openfiles(128);
150         kp = read_kinfo(kd);
151         ATF_CHECK(old_tables(kd,kp) == 0);
152 }
153
154 static _Noreturn void *
155 exec_thread(void *args)
156 {
157         for (;;)
158                 sleep(1);
159 }
160
161 /*
162  * Test a process with two threads that doesn't have a shared file
163  * descriptor table. The old tables should not be freed.
164  */
165 ATF_TC(oldtables_shared_via_threads);
166 ATF_TC_HEAD(oldtables_shared_via_threads, tc)
167 {
168         atf_tc_set_md_var(tc, "require.user", "root");
169 }
170
171 ATF_TC_BODY(oldtables_shared_via_threads, tc)
172 {
173         kvm_t *kd;
174         struct kinfo_proc *kp;
175         pthread_t thread;
176
177         ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
178         ATF_REQUIRE(pthread_create(&thread, NULL, exec_thread, NULL) == 0);
179
180         openfiles(128);
181
182         kp = read_kinfo(kd);
183         ATF_CHECK(kp->ki_numthreads > 1);
184         ATF_CHECK(old_tables(kd,kp) > 1);
185
186         ATF_REQUIRE(pthread_cancel(thread) == 0);
187         ATF_REQUIRE(pthread_join(thread, NULL) == 0);
188 }
189
190 /*
191  * Get the reference count of a file descriptor table.
192  */
193 static int
194 filedesc_refcnt(kvm_t *kd, struct kinfo_proc *kp)
195 {
196         struct filedesc fdp;
197
198         ATF_REQUIRE(kvm_read(kd, (unsigned long) kp->ki_fd, &fdp, sizeof(fdp)) > 0);
199
200         return (fdp.fd_refcnt);
201 }
202
203 /*
204  * Test a single threaded process that shares a file descriptor
205  * table with another process. The old tables should not be freed.
206  */
207 ATF_TC(oldtables_shared_via_process);
208 ATF_TC_HEAD(oldtables_shared_via_process, tc)
209 {
210         atf_tc_set_md_var(tc, "require.user", "root");
211 }
212
213 ATF_TC_BODY(oldtables_shared_via_process, tc)
214 {
215         kvm_t *kd;
216         struct kinfo_proc *kp;
217         int status;
218         pid_t child, wpid;
219
220         ATF_REQUIRE((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) != NULL);
221
222         /* share the file descriptor table */
223         ATF_REQUIRE((child = rfork(RFPROC)) != -1);
224
225         if (child == 0) {
226                 openfiles(128);
227                 raise(SIGSTOP);
228                 exit(127);
229         }
230
231         /* let parent process open some files too */
232         openfiles(128);
233
234         /* get current status of child */
235         wpid = waitpid(child, &status, WUNTRACED);
236
237         /* child should be stopped */
238         ATF_REQUIRE(WIFSTOPPED(status));
239
240         /*
241          * We want to read kernel data
242          * before the child exits
243          * otherwise we'll lose a reference count
244          * to the file descriptor table
245          */
246         if (child != 0) {
247                 kp = read_kinfo(kd);
248
249                 ATF_CHECK(filedesc_refcnt(kd,kp) > 1);
250                 ATF_CHECK(old_tables(kd,kp) > 1);
251
252                 kill(child, SIGCONT);
253         }
254
255         /* child should have exited */
256         wpid = waitpid(child, &status, 0);
257         ATF_REQUIRE(WIFEXITED(status));
258         ATF_REQUIRE(WEXITSTATUS(status) == 127);
259 }
260
261 ATF_TP_ADD_TCS(tp)
262 {
263         ATF_TP_ADD_TC(tp, free_oldtables);
264         ATF_TP_ADD_TC(tp, oldtables_shared_via_threads);
265         ATF_TP_ADD_TC(tp, oldtables_shared_via_process);
266         return (atf_no_error());
267 }