1328653Shselasky/*-
2328653Shselasky * Copyright (c) 2017 Hans Petter Selasky
3328653Shselasky * All rights reserved.
4328653Shselasky *
5328653Shselasky * Redistribution and use in source and binary forms, with or without
6328653Shselasky * modification, are permitted provided that the following conditions
7328653Shselasky * are met:
8328653Shselasky * 1. Redistributions of source code must retain the above copyright
9328653Shselasky *    notice unmodified, this list of conditions, and the following
10328653Shselasky *    disclaimer.
11328653Shselasky * 2. Redistributions in binary form must reproduce the above copyright
12328653Shselasky *    notice, this list of conditions and the following disclaimer in the
13328653Shselasky *    documentation and/or other materials provided with the distribution.
14328653Shselasky *
15328653Shselasky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16328653Shselasky * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17328653Shselasky * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18328653Shselasky * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19328653Shselasky * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20328653Shselasky * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21328653Shselasky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22328653Shselasky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23328653Shselasky * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24328653Shselasky * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25328653Shselasky */
26328653Shselasky
27328653Shselasky#include <sys/cdefs.h>
28328653Shselasky__FBSDID("$FreeBSD: stable/11/sys/compat/linuxkpi/common/src/linux_current.c 363153 2020-07-13 15:37:59Z hselasky $");
29328653Shselasky
30328653Shselasky#include <linux/compat.h>
31328653Shselasky#include <linux/completion.h>
32328653Shselasky#include <linux/mm.h>
33328653Shselasky#include <linux/kthread.h>
34328653Shselasky
35328653Shselasky#include <sys/kernel.h>
36328653Shselasky#include <sys/eventhandler.h>
37328653Shselasky#include <sys/malloc.h>
38328653Shselasky
39328653Shselaskystatic eventhandler_tag linuxkpi_thread_dtor_tag;
40328653Shselasky
41328653Shselaskystatic MALLOC_DEFINE(M_LINUX_CURRENT, "linuxcurrent", "LinuxKPI task structure");
42328653Shselasky
43328653Shselaskyint
44328653Shselaskylinux_alloc_current(struct thread *td, int flags)
45328653Shselasky{
46328653Shselasky	struct proc *proc;
47328653Shselasky	struct thread *td_other;
48328653Shselasky	struct task_struct *ts;
49328653Shselasky	struct task_struct *ts_other;
50328653Shselasky	struct mm_struct *mm;
51328653Shselasky	struct mm_struct *mm_other;
52328653Shselasky
53328653Shselasky	MPASS(td->td_lkpi_task == NULL);
54328653Shselasky
55328653Shselasky	ts = malloc(sizeof(*ts), M_LINUX_CURRENT, flags | M_ZERO);
56328653Shselasky	if (ts == NULL)
57328653Shselasky		return (ENOMEM);
58328653Shselasky
59328653Shselasky	mm = malloc(sizeof(*mm), M_LINUX_CURRENT, flags | M_ZERO);
60328653Shselasky	if (mm == NULL) {
61328653Shselasky		free(ts, M_LINUX_CURRENT);
62328653Shselasky		return (ENOMEM);
63328653Shselasky	}
64328653Shselasky
65328653Shselasky	/* setup new task structure */
66328653Shselasky	atomic_set(&ts->kthread_flags, 0);
67328653Shselasky	ts->task_thread = td;
68328653Shselasky	ts->comm = td->td_name;
69328653Shselasky	ts->pid = td->td_tid;
70328653Shselasky	atomic_set(&ts->usage, 1);
71328653Shselasky	atomic_set(&ts->state, TASK_RUNNING);
72328653Shselasky	init_completion(&ts->parked);
73328653Shselasky	init_completion(&ts->exited);
74328653Shselasky
75328653Shselasky	proc = td->td_proc;
76328653Shselasky
77328653Shselasky	/* check if another thread already has a mm_struct */
78328653Shselasky	PROC_LOCK(proc);
79328653Shselasky	FOREACH_THREAD_IN_PROC(proc, td_other) {
80328653Shselasky		ts_other = td_other->td_lkpi_task;
81328653Shselasky		if (ts_other == NULL)
82328653Shselasky			continue;
83328653Shselasky
84328653Shselasky		mm_other = ts_other->mm;
85328653Shselasky		if (mm_other == NULL)
86328653Shselasky			continue;
87328653Shselasky
88328653Shselasky		/* try to share other mm_struct */
89328653Shselasky		if (atomic_inc_not_zero(&mm_other->mm_users)) {
90328653Shselasky			/* set mm_struct pointer */
91328653Shselasky			ts->mm = mm_other;
92328653Shselasky			break;
93328653Shselasky		}
94328653Shselasky	}
95328653Shselasky
96328653Shselasky	/* use allocated mm_struct as a fallback */
97328653Shselasky	if (ts->mm == NULL) {
98328653Shselasky		/* setup new mm_struct */
99328653Shselasky		init_rwsem(&mm->mmap_sem);
100328653Shselasky		atomic_set(&mm->mm_count, 1);
101328653Shselasky		atomic_set(&mm->mm_users, 1);
102328653Shselasky		/* set mm_struct pointer */
103328653Shselasky		ts->mm = mm;
104328653Shselasky		/* clear pointer to not free memory */
105328653Shselasky		mm = NULL;
106328653Shselasky	}
107328653Shselasky
108328653Shselasky	/* store pointer to task struct */
109328653Shselasky	td->td_lkpi_task = ts;
110328653Shselasky	PROC_UNLOCK(proc);
111328653Shselasky
112328653Shselasky	/* free mm_struct pointer, if any */
113328653Shselasky	free(mm, M_LINUX_CURRENT);
114328653Shselasky
115328653Shselasky	return (0);
116328653Shselasky}
117328653Shselasky
118328653Shselaskystruct mm_struct *
119328653Shselaskylinux_get_task_mm(struct task_struct *task)
120328653Shselasky{
121328653Shselasky	struct mm_struct *mm;
122328653Shselasky
123328653Shselasky	mm = task->mm;
124328653Shselasky	if (mm != NULL) {
125328653Shselasky		atomic_inc(&mm->mm_users);
126328653Shselasky		return (mm);
127328653Shselasky	}
128328653Shselasky	return (NULL);
129328653Shselasky}
130328653Shselasky
131328653Shselaskyvoid
132328653Shselaskylinux_mm_dtor(struct mm_struct *mm)
133328653Shselasky{
134328653Shselasky	free(mm, M_LINUX_CURRENT);
135328653Shselasky}
136328653Shselasky
137328653Shselaskyvoid
138328653Shselaskylinux_free_current(struct task_struct *ts)
139328653Shselasky{
140328653Shselasky	mmput(ts->mm);
141328653Shselasky	free(ts, M_LINUX_CURRENT);
142328653Shselasky}
143328653Shselasky
144328653Shselaskystatic void
145328653Shselaskylinuxkpi_thread_dtor(void *arg __unused, struct thread *td)
146328653Shselasky{
147328653Shselasky	struct task_struct *ts;
148328653Shselasky
149328653Shselasky	ts = td->td_lkpi_task;
150328653Shselasky	if (ts == NULL)
151328653Shselasky		return;
152328653Shselasky
153328653Shselasky	td->td_lkpi_task = NULL;
154328653Shselasky	put_task_struct(ts);
155328653Shselasky}
156328653Shselasky
157328653Shselaskystruct task_struct *
158328653Shselaskylinux_pid_task(pid_t pid)
159328653Shselasky{
160328653Shselasky	struct thread *td;
161328653Shselasky	struct proc *p;
162328653Shselasky
163328653Shselasky	/* try to find corresponding thread */
164328653Shselasky	td = tdfind(pid, -1);
165328653Shselasky	if (td != NULL) {
166328653Shselasky		struct task_struct *ts = td->td_lkpi_task;
167328653Shselasky		PROC_UNLOCK(td->td_proc);
168328653Shselasky		return (ts);
169328653Shselasky	}
170328653Shselasky
171328653Shselasky	/* try to find corresponding procedure */
172328653Shselasky	p = pfind(pid);
173328653Shselasky	if (p != NULL) {
174328653Shselasky		FOREACH_THREAD_IN_PROC(p, td) {
175328653Shselasky			struct task_struct *ts = td->td_lkpi_task;
176328653Shselasky			if (ts != NULL) {
177328653Shselasky				PROC_UNLOCK(p);
178328653Shselasky				return (ts);
179328653Shselasky			}
180328653Shselasky		}
181328653Shselasky		PROC_UNLOCK(p);
182328653Shselasky	}
183328653Shselasky	return (NULL);
184328653Shselasky}
185328653Shselasky
186328653Shselaskystruct task_struct *
187328653Shselaskylinux_get_pid_task(pid_t pid)
188328653Shselasky{
189328653Shselasky	struct thread *td;
190328653Shselasky	struct proc *p;
191328653Shselasky
192328653Shselasky	/* try to find corresponding thread */
193328653Shselasky	td = tdfind(pid, -1);
194328653Shselasky	if (td != NULL) {
195328653Shselasky		struct task_struct *ts = td->td_lkpi_task;
196328653Shselasky		if (ts != NULL)
197328653Shselasky			get_task_struct(ts);
198328653Shselasky		PROC_UNLOCK(td->td_proc);
199328653Shselasky		return (ts);
200328653Shselasky	}
201328653Shselasky
202328653Shselasky	/* try to find corresponding procedure */
203328653Shselasky	p = pfind(pid);
204328653Shselasky	if (p != NULL) {
205328653Shselasky		FOREACH_THREAD_IN_PROC(p, td) {
206328653Shselasky			struct task_struct *ts = td->td_lkpi_task;
207328653Shselasky			if (ts != NULL) {
208328653Shselasky				get_task_struct(ts);
209328653Shselasky				PROC_UNLOCK(p);
210328653Shselasky				return (ts);
211328653Shselasky			}
212328653Shselasky		}
213328653Shselasky		PROC_UNLOCK(p);
214328653Shselasky	}
215328653Shselasky	return (NULL);
216328653Shselasky}
217328653Shselasky
218345924Shselaskybool
219345924Shselaskylinux_task_exiting(struct task_struct *task)
220345924Shselasky{
221363153Shselasky	struct thread *td;
222345924Shselasky	struct proc *p;
223345924Shselasky	bool ret;
224345924Shselasky
225345924Shselasky	ret = false;
226363153Shselasky
227363153Shselasky	/* try to find corresponding thread */
228363153Shselasky	td = tdfind(task->pid, -1);
229363153Shselasky	if (td != NULL) {
230363153Shselasky		p = td->td_proc;
231363153Shselasky	} else {
232363153Shselasky		/* try to find corresponding procedure */
233363153Shselasky		p = pfind(task->pid);
234363153Shselasky	}
235363153Shselasky
236345924Shselasky	if (p != NULL) {
237345924Shselasky		if ((p->p_flag & P_WEXIT) != 0)
238345924Shselasky			ret = true;
239345924Shselasky		PROC_UNLOCK(p);
240345924Shselasky	}
241345924Shselasky	return (ret);
242345924Shselasky}
243345924Shselasky
244328653Shselaskystatic void
245328653Shselaskylinux_current_init(void *arg __unused)
246328653Shselasky{
247328653Shselasky	linuxkpi_thread_dtor_tag = EVENTHANDLER_REGISTER(thread_dtor,
248328653Shselasky	    linuxkpi_thread_dtor, NULL, EVENTHANDLER_PRI_ANY);
249328653Shselasky}
250328653ShselaskySYSINIT(linux_current, SI_SUB_EVENTHANDLER, SI_ORDER_SECOND, linux_current_init, NULL);
251328653Shselasky
252328653Shselaskystatic void
253328653Shselaskylinux_current_uninit(void *arg __unused)
254328653Shselasky{
255328653Shselasky	struct proc *p;
256328653Shselasky	struct task_struct *ts;
257328653Shselasky	struct thread *td;
258328653Shselasky
259328653Shselasky	sx_slock(&allproc_lock);
260328653Shselasky	FOREACH_PROC_IN_SYSTEM(p) {
261328653Shselasky		PROC_LOCK(p);
262328653Shselasky		FOREACH_THREAD_IN_PROC(p, td) {
263328653Shselasky			if ((ts = td->td_lkpi_task) != NULL) {
264328653Shselasky				td->td_lkpi_task = NULL;
265328653Shselasky				put_task_struct(ts);
266328653Shselasky			}
267328653Shselasky		}
268328653Shselasky		PROC_UNLOCK(p);
269328653Shselasky	}
270328653Shselasky	sx_sunlock(&allproc_lock);
271328653Shselasky
272328653Shselasky	EVENTHANDLER_DEREGISTER(thread_dtor, linuxkpi_thread_dtor_tag);
273328653Shselasky}
274328653ShselaskySYSUNINIT(linux_current, SI_SUB_EVENTHANDLER, SI_ORDER_SECOND, linux_current_uninit, NULL);
275