11e3db1deSHans Petter Selasky /*-
21e3db1deSHans Petter Selasky  * Copyright (c) 2017 Hans Petter Selasky
31e3db1deSHans Petter Selasky  * All rights reserved.
41e3db1deSHans Petter Selasky  *
51e3db1deSHans Petter Selasky  * Redistribution and use in source and binary forms, with or without
61e3db1deSHans Petter Selasky  * modification, are permitted provided that the following conditions
71e3db1deSHans Petter Selasky  * are met:
81e3db1deSHans Petter Selasky  * 1. Redistributions of source code must retain the above copyright
91e3db1deSHans Petter Selasky  *    notice unmodified, this list of conditions, and the following
101e3db1deSHans Petter Selasky  *    disclaimer.
111e3db1deSHans Petter Selasky  * 2. Redistributions in binary form must reproduce the above copyright
121e3db1deSHans Petter Selasky  *    notice, this list of conditions and the following disclaimer in the
131e3db1deSHans Petter Selasky  *    documentation and/or other materials provided with the distribution.
141e3db1deSHans Petter Selasky  *
151e3db1deSHans Petter Selasky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
161e3db1deSHans Petter Selasky  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
171e3db1deSHans Petter Selasky  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
181e3db1deSHans Petter Selasky  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
191e3db1deSHans Petter Selasky  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
201e3db1deSHans Petter Selasky  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
211e3db1deSHans Petter Selasky  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
221e3db1deSHans Petter Selasky  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
231e3db1deSHans Petter Selasky  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
241e3db1deSHans Petter Selasky  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
251e3db1deSHans Petter Selasky  */
261e3db1deSHans Petter Selasky 
271e3db1deSHans Petter Selasky #include <sys/cdefs.h>
281e3db1deSHans Petter Selasky __FBSDID("$FreeBSD$");
291e3db1deSHans Petter Selasky 
301e3db1deSHans Petter Selasky #include <linux/compat.h>
318504aa98SMark Johnston #include <linux/completion.h>
321e3db1deSHans Petter Selasky #include <linux/mm.h>
331e3db1deSHans Petter Selasky #include <linux/kthread.h>
341e3db1deSHans Petter Selasky 
351e3db1deSHans Petter Selasky #include <sys/kernel.h>
361e3db1deSHans Petter Selasky #include <sys/eventhandler.h>
371e3db1deSHans Petter Selasky #include <sys/malloc.h>
381e3db1deSHans Petter Selasky 
391e3db1deSHans Petter Selasky static eventhandler_tag linuxkpi_thread_dtor_tag;
401e3db1deSHans Petter Selasky 
411e3db1deSHans Petter Selasky static MALLOC_DEFINE(M_LINUX_CURRENT, "linuxcurrent", "LinuxKPI task structure");
421e3db1deSHans Petter Selasky 
431e3db1deSHans Petter Selasky int
441e3db1deSHans Petter Selasky linux_alloc_current(struct thread *td, int flags)
451e3db1deSHans Petter Selasky {
46e54b103eSHans Petter Selasky 	struct proc *proc;
47e54b103eSHans Petter Selasky 	struct thread *td_other;
481e3db1deSHans Petter Selasky 	struct task_struct *ts;
49e54b103eSHans Petter Selasky 	struct task_struct *ts_other;
5005d4f501SHans Petter Selasky 	struct mm_struct *mm;
51e54b103eSHans Petter Selasky 	struct mm_struct *mm_other;
521e3db1deSHans Petter Selasky 
531e3db1deSHans Petter Selasky 	MPASS(td->td_lkpi_task == NULL);
541e3db1deSHans Petter Selasky 
551e3db1deSHans Petter Selasky 	ts = malloc(sizeof(*ts), M_LINUX_CURRENT, flags | M_ZERO);
561e3db1deSHans Petter Selasky 	if (ts == NULL)
571e3db1deSHans Petter Selasky 		return (ENOMEM);
581e3db1deSHans Petter Selasky 
5905d4f501SHans Petter Selasky 	mm = malloc(sizeof(*mm), M_LINUX_CURRENT, flags | M_ZERO);
6005d4f501SHans Petter Selasky 	if (mm == NULL) {
6105d4f501SHans Petter Selasky 		free(ts, M_LINUX_CURRENT);
6205d4f501SHans Petter Selasky 		return (ENOMEM);
6305d4f501SHans Petter Selasky 	}
6405d4f501SHans Petter Selasky 
65e54b103eSHans Petter Selasky 	/* setup new task structure */
661e3db1deSHans Petter Selasky 	atomic_set(&ts->kthread_flags, 0);
671e3db1deSHans Petter Selasky 	ts->task_thread = td;
681e3db1deSHans Petter Selasky 	ts->comm = td->td_name;
691e3db1deSHans Petter Selasky 	ts->pid = td->td_tid;
7007e0a3caSJohannes Lundberg 	ts->group_leader = ts;
71a0699ebfSHans Petter Selasky 	atomic_set(&ts->usage, 1);
72ef925749SHans Petter Selasky 	atomic_set(&ts->state, TASK_RUNNING);
738504aa98SMark Johnston 	init_completion(&ts->parked);
748504aa98SMark Johnston 	init_completion(&ts->exited);
7505d4f501SHans Petter Selasky 
76e54b103eSHans Petter Selasky 	proc = td->td_proc;
77e54b103eSHans Petter Selasky 
78e54b103eSHans Petter Selasky 	/* check if another thread already has a mm_struct */
79e54b103eSHans Petter Selasky 	PROC_LOCK(proc);
80e54b103eSHans Petter Selasky 	FOREACH_THREAD_IN_PROC(proc, td_other) {
81e54b103eSHans Petter Selasky 		ts_other = td_other->td_lkpi_task;
82e54b103eSHans Petter Selasky 		if (ts_other == NULL)
83e54b103eSHans Petter Selasky 			continue;
84e54b103eSHans Petter Selasky 
85e54b103eSHans Petter Selasky 		mm_other = ts_other->mm;
86e54b103eSHans Petter Selasky 		if (mm_other == NULL)
87e54b103eSHans Petter Selasky 			continue;
88e54b103eSHans Petter Selasky 
89e54b103eSHans Petter Selasky 		/* try to share other mm_struct */
90e54b103eSHans Petter Selasky 		if (atomic_inc_not_zero(&mm_other->mm_users)) {
91e54b103eSHans Petter Selasky 			/* set mm_struct pointer */
92e54b103eSHans Petter Selasky 			ts->mm = mm_other;
93e54b103eSHans Petter Selasky 			break;
94e54b103eSHans Petter Selasky 		}
95e54b103eSHans Petter Selasky 	}
96e54b103eSHans Petter Selasky 
97e54b103eSHans Petter Selasky 	/* use allocated mm_struct as a fallback */
98e54b103eSHans Petter Selasky 	if (ts->mm == NULL) {
99e54b103eSHans Petter Selasky 		/* setup new mm_struct */
10005d4f501SHans Petter Selasky 		init_rwsem(&mm->mmap_sem);
10105d4f501SHans Petter Selasky 		atomic_set(&mm->mm_count, 1);
10205d4f501SHans Petter Selasky 		atomic_set(&mm->mm_users, 1);
103e54b103eSHans Petter Selasky 		/* set mm_struct pointer */
104e54b103eSHans Petter Selasky 		ts->mm = mm;
105e54b103eSHans Petter Selasky 		/* clear pointer to not free memory */
106e54b103eSHans Petter Selasky 		mm = NULL;
107e54b103eSHans Petter Selasky 	}
10805d4f501SHans Petter Selasky 
10905d4f501SHans Petter Selasky 	/* store pointer to task struct */
1101e3db1deSHans Petter Selasky 	td->td_lkpi_task = ts;
111e54b103eSHans Petter Selasky 	PROC_UNLOCK(proc);
112e54b103eSHans Petter Selasky 
113e54b103eSHans Petter Selasky 	/* free mm_struct pointer, if any */
114e54b103eSHans Petter Selasky 	free(mm, M_LINUX_CURRENT);
115e54b103eSHans Petter Selasky 
1161e3db1deSHans Petter Selasky 	return (0);
1171e3db1deSHans Petter Selasky }
1181e3db1deSHans Petter Selasky 
11905d4f501SHans Petter Selasky struct mm_struct *
12005d4f501SHans Petter Selasky linux_get_task_mm(struct task_struct *task)
12105d4f501SHans Petter Selasky {
12205d4f501SHans Petter Selasky 	struct mm_struct *mm;
12305d4f501SHans Petter Selasky 
12405d4f501SHans Petter Selasky 	mm = task->mm;
12568b9f2f0SHans Petter Selasky 	if (mm != NULL) {
12605d4f501SHans Petter Selasky 		atomic_inc(&mm->mm_users);
12705d4f501SHans Petter Selasky 		return (mm);
12805d4f501SHans Petter Selasky 	}
12905d4f501SHans Petter Selasky 	return (NULL);
13005d4f501SHans Petter Selasky }
13105d4f501SHans Petter Selasky 
13205d4f501SHans Petter Selasky void
13305d4f501SHans Petter Selasky linux_mm_dtor(struct mm_struct *mm)
13405d4f501SHans Petter Selasky {
13505d4f501SHans Petter Selasky 	free(mm, M_LINUX_CURRENT);
13605d4f501SHans Petter Selasky }
13705d4f501SHans Petter Selasky 
1381e3db1deSHans Petter Selasky void
1391e3db1deSHans Petter Selasky linux_free_current(struct task_struct *ts)
1401e3db1deSHans Petter Selasky {
14105d4f501SHans Petter Selasky 	mmput(ts->mm);
1421e3db1deSHans Petter Selasky 	free(ts, M_LINUX_CURRENT);
1431e3db1deSHans Petter Selasky }
1441e3db1deSHans Petter Selasky 
1451e3db1deSHans Petter Selasky static void
1461e3db1deSHans Petter Selasky linuxkpi_thread_dtor(void *arg __unused, struct thread *td)
1471e3db1deSHans Petter Selasky {
1481e3db1deSHans Petter Selasky 	struct task_struct *ts;
1491e3db1deSHans Petter Selasky 
1501e3db1deSHans Petter Selasky 	ts = td->td_lkpi_task;
1511e3db1deSHans Petter Selasky 	if (ts == NULL)
1521e3db1deSHans Petter Selasky 		return;
1531e3db1deSHans Petter Selasky 
1541e3db1deSHans Petter Selasky 	td->td_lkpi_task = NULL;
155a0699ebfSHans Petter Selasky 	put_task_struct(ts);
156a0699ebfSHans Petter Selasky }
157a0699ebfSHans Petter Selasky 
158a0699ebfSHans Petter Selasky struct task_struct *
159a0699ebfSHans Petter Selasky linux_pid_task(pid_t pid)
160a0699ebfSHans Petter Selasky {
161a0699ebfSHans Petter Selasky 	struct thread *td;
1628402f058SHans Petter Selasky 	struct proc *p;
163a0699ebfSHans Petter Selasky 
1648402f058SHans Petter Selasky 	/* try to find corresponding thread */
165a0699ebfSHans Petter Selasky 	td = tdfind(pid, -1);
166a0699ebfSHans Petter Selasky 	if (td != NULL) {
167a0699ebfSHans Petter Selasky 		struct task_struct *ts = td->td_lkpi_task;
168a0699ebfSHans Petter Selasky 		PROC_UNLOCK(td->td_proc);
169a0699ebfSHans Petter Selasky 		return (ts);
170a0699ebfSHans Petter Selasky 	}
1718402f058SHans Petter Selasky 
1728402f058SHans Petter Selasky 	/* try to find corresponding procedure */
1738402f058SHans Petter Selasky 	p = pfind(pid);
1748402f058SHans Petter Selasky 	if (p != NULL) {
1758402f058SHans Petter Selasky 		FOREACH_THREAD_IN_PROC(p, td) {
1768402f058SHans Petter Selasky 			struct task_struct *ts = td->td_lkpi_task;
1778402f058SHans Petter Selasky 			if (ts != NULL) {
1788402f058SHans Petter Selasky 				PROC_UNLOCK(p);
1798402f058SHans Petter Selasky 				return (ts);
1808402f058SHans Petter Selasky 			}
1818402f058SHans Petter Selasky 		}
1828402f058SHans Petter Selasky 		PROC_UNLOCK(p);
1838402f058SHans Petter Selasky 	}
184a0699ebfSHans Petter Selasky 	return (NULL);
185a0699ebfSHans Petter Selasky }
186a0699ebfSHans Petter Selasky 
187a0699ebfSHans Petter Selasky struct task_struct *
188a0699ebfSHans Petter Selasky linux_get_pid_task(pid_t pid)
189a0699ebfSHans Petter Selasky {
190a0699ebfSHans Petter Selasky 	struct thread *td;
1918402f058SHans Petter Selasky 	struct proc *p;
192a0699ebfSHans Petter Selasky 
1938402f058SHans Petter Selasky 	/* try to find corresponding thread */
194a0699ebfSHans Petter Selasky 	td = tdfind(pid, -1);
195a0699ebfSHans Petter Selasky 	if (td != NULL) {
196a0699ebfSHans Petter Selasky 		struct task_struct *ts = td->td_lkpi_task;
197a0699ebfSHans Petter Selasky 		if (ts != NULL)
198a0699ebfSHans Petter Selasky 			get_task_struct(ts);
199a0699ebfSHans Petter Selasky 		PROC_UNLOCK(td->td_proc);
200a0699ebfSHans Petter Selasky 		return (ts);
201a0699ebfSHans Petter Selasky 	}
2028402f058SHans Petter Selasky 
2038402f058SHans Petter Selasky 	/* try to find corresponding procedure */
2048402f058SHans Petter Selasky 	p = pfind(pid);
2058402f058SHans Petter Selasky 	if (p != NULL) {
2068402f058SHans Petter Selasky 		FOREACH_THREAD_IN_PROC(p, td) {
2078402f058SHans Petter Selasky 			struct task_struct *ts = td->td_lkpi_task;
2088402f058SHans Petter Selasky 			if (ts != NULL) {
2098402f058SHans Petter Selasky 				get_task_struct(ts);
2108402f058SHans Petter Selasky 				PROC_UNLOCK(p);
2118402f058SHans Petter Selasky 				return (ts);
2128402f058SHans Petter Selasky 			}
2138402f058SHans Petter Selasky 		}
2148402f058SHans Petter Selasky 		PROC_UNLOCK(p);
2158402f058SHans Petter Selasky 	}
216a0699ebfSHans Petter Selasky 	return (NULL);
2171e3db1deSHans Petter Selasky }
2181e3db1deSHans Petter Selasky 
219638fa5a3SHans Petter Selasky bool
220638fa5a3SHans Petter Selasky linux_task_exiting(struct task_struct *task)
221638fa5a3SHans Petter Selasky {
222638fa5a3SHans Petter Selasky 	struct proc *p;
223638fa5a3SHans Petter Selasky 	bool ret;
224638fa5a3SHans Petter Selasky 
225638fa5a3SHans Petter Selasky 	ret = false;
226638fa5a3SHans Petter Selasky 	p = pfind(task->pid);
227638fa5a3SHans Petter Selasky 	if (p != NULL) {
228638fa5a3SHans Petter Selasky 		if ((p->p_flag & P_WEXIT) != 0)
229638fa5a3SHans Petter Selasky 			ret = true;
230638fa5a3SHans Petter Selasky 		PROC_UNLOCK(p);
231638fa5a3SHans Petter Selasky 	}
232638fa5a3SHans Petter Selasky 	return (ret);
233638fa5a3SHans Petter Selasky }
234638fa5a3SHans Petter Selasky 
2351e3db1deSHans Petter Selasky static void
2361e3db1deSHans Petter Selasky linux_current_init(void *arg __unused)
2371e3db1deSHans Petter Selasky {
238983ed4f9SMatt Macy 	lkpi_alloc_current = linux_alloc_current;
2391e3db1deSHans Petter Selasky 	linuxkpi_thread_dtor_tag = EVENTHANDLER_REGISTER(thread_dtor,
2401e3db1deSHans Petter Selasky 	    linuxkpi_thread_dtor, NULL, EVENTHANDLER_PRI_ANY);
2411e3db1deSHans Petter Selasky }
2421e3db1deSHans Petter Selasky SYSINIT(linux_current, SI_SUB_EVENTHANDLER, SI_ORDER_SECOND, linux_current_init, NULL);
2431e3db1deSHans Petter Selasky 
2441e3db1deSHans Petter Selasky static void
2451e3db1deSHans Petter Selasky linux_current_uninit(void *arg __unused)
2461e3db1deSHans Petter Selasky {
247abf5c031SMark Johnston 	struct proc *p;
248abf5c031SMark Johnston 	struct task_struct *ts;
249abf5c031SMark Johnston 	struct thread *td;
250abf5c031SMark Johnston 
251abf5c031SMark Johnston 	sx_slock(&allproc_lock);
252abf5c031SMark Johnston 	FOREACH_PROC_IN_SYSTEM(p) {
253abf5c031SMark Johnston 		PROC_LOCK(p);
254abf5c031SMark Johnston 		FOREACH_THREAD_IN_PROC(p, td) {
255abf5c031SMark Johnston 			if ((ts = td->td_lkpi_task) != NULL) {
256abf5c031SMark Johnston 				td->td_lkpi_task = NULL;
257abf5c031SMark Johnston 				put_task_struct(ts);
258abf5c031SMark Johnston 			}
259abf5c031SMark Johnston 		}
260abf5c031SMark Johnston 		PROC_UNLOCK(p);
261abf5c031SMark Johnston 	}
262abf5c031SMark Johnston 	sx_sunlock(&allproc_lock);
2631e3db1deSHans Petter Selasky 	EVENTHANDLER_DEREGISTER(thread_dtor, linuxkpi_thread_dtor_tag);
264983ed4f9SMatt Macy 	lkpi_alloc_current = linux_alloc_current_noop;
2651e3db1deSHans Petter Selasky }
2661e3db1deSHans Petter Selasky SYSUNINIT(linux_current, SI_SUB_EVENTHANDLER, SI_ORDER_SECOND, linux_current_uninit, NULL);
267