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;
70a0699ebfSHans Petter Selasky 	atomic_set(&ts->usage, 1);
711e3db1deSHans Petter Selasky 	ts->state = TASK_RUNNING;
728504aa98SMark Johnston 	init_completion(&ts->parked);
738504aa98SMark Johnston 	init_completion(&ts->exited);
7405d4f501SHans Petter Selasky 
75e54b103eSHans Petter Selasky 	proc = td->td_proc;
76e54b103eSHans Petter Selasky 
77e54b103eSHans Petter Selasky 	/* check if another thread already has a mm_struct */
78e54b103eSHans Petter Selasky 	PROC_LOCK(proc);
79e54b103eSHans Petter Selasky 	FOREACH_THREAD_IN_PROC(proc, td_other) {
80e54b103eSHans Petter Selasky 		ts_other = td_other->td_lkpi_task;
81e54b103eSHans Petter Selasky 		if (ts_other == NULL)
82e54b103eSHans Petter Selasky 			continue;
83e54b103eSHans Petter Selasky 
84e54b103eSHans Petter Selasky 		mm_other = ts_other->mm;
85e54b103eSHans Petter Selasky 		if (mm_other == NULL)
86e54b103eSHans Petter Selasky 			continue;
87e54b103eSHans Petter Selasky 
88e54b103eSHans Petter Selasky 		/* try to share other mm_struct */
89e54b103eSHans Petter Selasky 		if (atomic_inc_not_zero(&mm_other->mm_users)) {
90e54b103eSHans Petter Selasky 			/* set mm_struct pointer */
91e54b103eSHans Petter Selasky 			ts->mm = mm_other;
92e54b103eSHans Petter Selasky 			break;
93e54b103eSHans Petter Selasky 		}
94e54b103eSHans Petter Selasky 	}
95e54b103eSHans Petter Selasky 
96e54b103eSHans Petter Selasky 	/* use allocated mm_struct as a fallback */
97e54b103eSHans Petter Selasky 	if (ts->mm == NULL) {
98e54b103eSHans Petter Selasky 		/* setup new mm_struct */
9905d4f501SHans Petter Selasky 		init_rwsem(&mm->mmap_sem);
10005d4f501SHans Petter Selasky 		atomic_set(&mm->mm_count, 1);
10105d4f501SHans Petter Selasky 		atomic_set(&mm->mm_users, 1);
102e54b103eSHans Petter Selasky 		/* set mm_struct pointer */
103e54b103eSHans Petter Selasky 		ts->mm = mm;
104e54b103eSHans Petter Selasky 		/* clear pointer to not free memory */
105e54b103eSHans Petter Selasky 		mm = NULL;
106e54b103eSHans Petter Selasky 	}
10705d4f501SHans Petter Selasky 
10805d4f501SHans Petter Selasky 	/* store pointer to task struct */
1091e3db1deSHans Petter Selasky 	td->td_lkpi_task = ts;
110e54b103eSHans Petter Selasky 	PROC_UNLOCK(proc);
111e54b103eSHans Petter Selasky 
112e54b103eSHans Petter Selasky 	/* free mm_struct pointer, if any */
113e54b103eSHans Petter Selasky 	free(mm, M_LINUX_CURRENT);
114e54b103eSHans Petter Selasky 
1151e3db1deSHans Petter Selasky 	return (0);
1161e3db1deSHans Petter Selasky }
1171e3db1deSHans Petter Selasky 
11805d4f501SHans Petter Selasky struct mm_struct *
11905d4f501SHans Petter Selasky linux_get_task_mm(struct task_struct *task)
12005d4f501SHans Petter Selasky {
12105d4f501SHans Petter Selasky 	struct mm_struct *mm;
12205d4f501SHans Petter Selasky 
12305d4f501SHans Petter Selasky 	mm = task->mm;
12468b9f2f0SHans Petter Selasky 	if (mm != NULL) {
12505d4f501SHans Petter Selasky 		atomic_inc(&mm->mm_users);
12605d4f501SHans Petter Selasky 		return (mm);
12705d4f501SHans Petter Selasky 	}
12805d4f501SHans Petter Selasky 	return (NULL);
12905d4f501SHans Petter Selasky }
13005d4f501SHans Petter Selasky 
13105d4f501SHans Petter Selasky void
13205d4f501SHans Petter Selasky linux_mm_dtor(struct mm_struct *mm)
13305d4f501SHans Petter Selasky {
13405d4f501SHans Petter Selasky 	free(mm, M_LINUX_CURRENT);
13505d4f501SHans Petter Selasky }
13605d4f501SHans Petter Selasky 
1371e3db1deSHans Petter Selasky void
1381e3db1deSHans Petter Selasky linux_free_current(struct task_struct *ts)
1391e3db1deSHans Petter Selasky {
14005d4f501SHans Petter Selasky 	mmput(ts->mm);
1411e3db1deSHans Petter Selasky 	free(ts, M_LINUX_CURRENT);
1421e3db1deSHans Petter Selasky }
1431e3db1deSHans Petter Selasky 
1441e3db1deSHans Petter Selasky static void
1451e3db1deSHans Petter Selasky linuxkpi_thread_dtor(void *arg __unused, struct thread *td)
1461e3db1deSHans Petter Selasky {
1471e3db1deSHans Petter Selasky 	struct task_struct *ts;
1481e3db1deSHans Petter Selasky 
1491e3db1deSHans Petter Selasky 	ts = td->td_lkpi_task;
1501e3db1deSHans Petter Selasky 	if (ts == NULL)
1511e3db1deSHans Petter Selasky 		return;
1521e3db1deSHans Petter Selasky 
1531e3db1deSHans Petter Selasky 	td->td_lkpi_task = NULL;
154a0699ebfSHans Petter Selasky 	put_task_struct(ts);
155a0699ebfSHans Petter Selasky }
156a0699ebfSHans Petter Selasky 
157a0699ebfSHans Petter Selasky struct task_struct *
158a0699ebfSHans Petter Selasky linux_pid_task(pid_t pid)
159a0699ebfSHans Petter Selasky {
160a0699ebfSHans Petter Selasky 	struct thread *td;
1618402f058SHans Petter Selasky 	struct proc *p;
162a0699ebfSHans Petter Selasky 
1638402f058SHans Petter Selasky 	/* try to find corresponding thread */
164a0699ebfSHans Petter Selasky 	td = tdfind(pid, -1);
165a0699ebfSHans Petter Selasky 	if (td != NULL) {
166a0699ebfSHans Petter Selasky 		struct task_struct *ts = td->td_lkpi_task;
167a0699ebfSHans Petter Selasky 		PROC_UNLOCK(td->td_proc);
168a0699ebfSHans Petter Selasky 		return (ts);
169a0699ebfSHans Petter Selasky 	}
1708402f058SHans Petter Selasky 
1718402f058SHans Petter Selasky 	/* try to find corresponding procedure */
1728402f058SHans Petter Selasky 	p = pfind(pid);
1738402f058SHans Petter Selasky 	if (p != NULL) {
1748402f058SHans Petter Selasky 		FOREACH_THREAD_IN_PROC(p, td) {
1758402f058SHans Petter Selasky 			struct task_struct *ts = td->td_lkpi_task;
1768402f058SHans Petter Selasky 			if (ts != NULL) {
1778402f058SHans Petter Selasky 				PROC_UNLOCK(p);
1788402f058SHans Petter Selasky 				return (ts);
1798402f058SHans Petter Selasky 			}
1808402f058SHans Petter Selasky 		}
1818402f058SHans Petter Selasky 		PROC_UNLOCK(p);
1828402f058SHans Petter Selasky 	}
183a0699ebfSHans Petter Selasky 	return (NULL);
184a0699ebfSHans Petter Selasky }
185a0699ebfSHans Petter Selasky 
186a0699ebfSHans Petter Selasky struct task_struct *
187a0699ebfSHans Petter Selasky linux_get_pid_task(pid_t pid)
188a0699ebfSHans Petter Selasky {
189a0699ebfSHans Petter Selasky 	struct thread *td;
1908402f058SHans Petter Selasky 	struct proc *p;
191a0699ebfSHans Petter Selasky 
1928402f058SHans Petter Selasky 	/* try to find corresponding thread */
193a0699ebfSHans Petter Selasky 	td = tdfind(pid, -1);
194a0699ebfSHans Petter Selasky 	if (td != NULL) {
195a0699ebfSHans Petter Selasky 		struct task_struct *ts = td->td_lkpi_task;
196a0699ebfSHans Petter Selasky 		if (ts != NULL)
197a0699ebfSHans Petter Selasky 			get_task_struct(ts);
198a0699ebfSHans Petter Selasky 		PROC_UNLOCK(td->td_proc);
199a0699ebfSHans Petter Selasky 		return (ts);
200a0699ebfSHans Petter Selasky 	}
2018402f058SHans Petter Selasky 
2028402f058SHans Petter Selasky 	/* try to find corresponding procedure */
2038402f058SHans Petter Selasky 	p = pfind(pid);
2048402f058SHans Petter Selasky 	if (p != NULL) {
2058402f058SHans Petter Selasky 		FOREACH_THREAD_IN_PROC(p, td) {
2068402f058SHans Petter Selasky 			struct task_struct *ts = td->td_lkpi_task;
2078402f058SHans Petter Selasky 			if (ts != NULL) {
2088402f058SHans Petter Selasky 				get_task_struct(ts);
2098402f058SHans Petter Selasky 				PROC_UNLOCK(p);
2108402f058SHans Petter Selasky 				return (ts);
2118402f058SHans Petter Selasky 			}
2128402f058SHans Petter Selasky 		}
2138402f058SHans Petter Selasky 		PROC_UNLOCK(p);
2148402f058SHans Petter Selasky 	}
215a0699ebfSHans Petter Selasky 	return (NULL);
2161e3db1deSHans Petter Selasky }
2171e3db1deSHans Petter Selasky 
2181e3db1deSHans Petter Selasky static void
2191e3db1deSHans Petter Selasky linux_current_init(void *arg __unused)
2201e3db1deSHans Petter Selasky {
2211e3db1deSHans Petter Selasky 	linuxkpi_thread_dtor_tag = EVENTHANDLER_REGISTER(thread_dtor,
2221e3db1deSHans Petter Selasky 	    linuxkpi_thread_dtor, NULL, EVENTHANDLER_PRI_ANY);
2231e3db1deSHans Petter Selasky }
2241e3db1deSHans Petter Selasky SYSINIT(linux_current, SI_SUB_EVENTHANDLER, SI_ORDER_SECOND, linux_current_init, NULL);
2251e3db1deSHans Petter Selasky 
2261e3db1deSHans Petter Selasky static void
2271e3db1deSHans Petter Selasky linux_current_uninit(void *arg __unused)
2281e3db1deSHans Petter Selasky {
2291e3db1deSHans Petter Selasky 	EVENTHANDLER_DEREGISTER(thread_dtor, linuxkpi_thread_dtor_tag);
2301e3db1deSHans Petter Selasky }
2311e3db1deSHans Petter Selasky SYSUNINIT(linux_current, SI_SUB_EVENTHANDLER, SI_ORDER_SECOND, linux_current_uninit, NULL);
232