blob: 13c8498839c430276b13cc28d15648219afe6184 [file] [log] [blame]
/*
* Copyright (c) [2009-2013] Marvell International Ltd. and its affiliates.
* All rights reserved.
* This software file (the "File") is owned and distributed by Marvell
* International Ltd. and/or its affiliates ("Marvell") under the following
* licensing terms.
* If you received this File from Marvell, you may opt to use, redistribute
* and/or modify this File in accordance with the terms and conditions of
* the General Public License Version 2, June 1991 (the "GPL License"), a
* copy of which is available along with the File in the license.txt file
* or by writing to the Free Software Foundation, Inc., 59 Temple Place,
* Suite 330, Boston, MA 02111-1307 or on the worldwide web at
* http://www.gnu.org/licenses/gpl.txt. THE FILE IS DISTRIBUTED AS-IS,
* WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED WARRANTIES OF
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
* DISCLAIMED. The GPL License provides additional details about this
* warranty disclaimer.
* Filename : osa_thread.c
* Author : Dafu Lv
* Date Created : 21/03/08
* Description : This is the source code of thread-related functions in osa.
*
*/
/*
******************************
* HEADERS
******************************
*/
#include <osa.h>
/*
******************************
* MACROS
******************************
*/
#define INIT_THREAD_MAGIC(t) \
do { \
((struct _thread *)t)->magic[0] = 'T'; \
((struct _thread *)t)->magic[1] = 'h'; \
((struct _thread *)t)->magic[2] = 'R'; \
((struct _thread *)t)->magic[3] = 'd'; \
} while (0)
#define IS_THREAD_VALID(t) \
('T' == ((struct _thread *)t)->magic[0] && \
'h' == ((struct _thread *)t)->magic[1] && \
'R' == ((struct _thread *)t)->magic[2] && \
'd' == ((struct _thread *)t)->magic[3])
#define CLEAN_THREAD_MAGIC(t) \
do { \
((struct _thread *)t)->magic[0] = 0; \
((struct _thread *)t)->magic[1] = 0; \
((struct _thread *)t)->magic[2] = 0; \
((struct _thread *)t)->magic[3] = 0; \
} while (0)
/*
******************************
* TYPES
******************************
*/
enum _thread_status {
_THREAD_UNKNOWN,
_THREAD_READY,
_THREAD_RUNNING,
_THREAD_STOPPED
};
struct _thread {
uint8_t magic[4];
void (*func) (void *);
void *arg;
long_t nice;
struct task_struct *task;
enum _thread_status status;
spinlock_t lock;
};
/*
******************************
* FUNCTIONS
******************************
*/
/*
* local functions
*/
static void _stop_thread(struct _thread *_thrd);
static void _stop_thread_lock(struct _thread *_thrd);
/*
* Name: _thread_func
*
* Description: the shell function of thread
*
* Params: param - the member "arg" in struct _thread
*
* Returns: int - 0 always
*
* Notes: none
*/
static int _thread_func(void *param)
{
struct _thread *_thrd = (struct _thread *)param;
OSA_ASSERT(_thrd);
OSA_ASSERT(IS_THREAD_VALID(_thrd));
/* 0~7 --> -20~19 */
set_user_nice(current, (_thrd->nice - 3) << 2);
spin_lock(&(_thrd->lock));
if (_THREAD_STOPPED != _thrd->status) {
_thrd->status = _THREAD_READY;
spin_unlock(&(_thrd->lock));
} else {
spin_unlock(&(_thrd->lock));
return 0;
}
/* further initialization here */
spin_lock(&(_thrd->lock));
if (_THREAD_STOPPED != _thrd->status) {
_thrd->status = _THREAD_RUNNING;
spin_unlock(&(_thrd->lock));
} else {
spin_unlock(&(_thrd->lock));
return 0;
}
if (_THREAD_STOPPED != _thrd->status) {
_thrd->func(_thrd->arg);
spin_lock(&(_thrd->lock));
_thrd->status = _THREAD_STOPPED;
spin_unlock(&(_thrd->lock));
}
/*
* kthread_stop is a sync way to stop thread.
* so here we need not send any event to show my stopping status.
*/
return 0;
}
/*
* Name: osa_create_thread
*
* Description: create a kernel thread
*
* Params: func - the function will be called in the kernel thread created
* arg - the argument of the function when called
* attr - os-specific attributes of the kernel thread
* attr->name - the name of the kernel thread, default "osa_thread"
* attr->prio - the static priority of the thread, will be changed
* dynamically.
* range: 0~7 (mapping from -20~19 in linux kernel)
* the default is the lowest priority (7)
* attr->stack_addr - no use in linux kernel
* attr->stack_size - no use in linux kernel
*
* Returns: osa_thread_t - handle of the kernel thread to create
*
* Notes: none
*/
osa_thread_t osa_create_thread(void (*func) (void *), void *arg,
struct osa_thread_attr *attr)
{
struct _thread *ret = NULL;
OSA_ASSERT(func);
OSA_ASSERT(attr && (attr->prio >= 0) && (attr->prio <= 7));
ret = kmalloc(sizeof(struct _thread), GFP_KERNEL);
if (!ret) {
osa_dbg_print(DBG_ERR,
"ERROR - failed to kmalloc in osa_create_thread\n");
return ret;
}
INIT_THREAD_MAGIC(ret);
ret->func = func;
ret->arg = arg;
ret->nice = (long_t) attr->prio;
spin_lock_init(&(ret->lock));
ret->status = _THREAD_UNKNOWN;
if (NULL == attr->name)
ret->task =
kthread_run(_thread_func, (void *)ret, "osa_kthread");
else
ret->task = kthread_run(_thread_func, (void *)ret, attr->name);
if (!(ret->task)) {
osa_dbg_print(DBG_ERR,
"ERROR - failed to create a thread in osa_create_thread\n");
CLEAN_THREAD_MAGIC(ret);
kfree(ret);
ret = NULL;
return ret;
}
while (_THREAD_UNKNOWN == ret->status)
yield();
return (osa_thread_t) ret;
}
OSA_EXPORT_SYMBOL(osa_create_thread);
/*
* Name: osa_destroy_thread
*
* Description: destroy the thread handle
*
* Params: handle - handle of the thread to destroy
*
* Returns: none
*
* Notes: if the thread is not stopped,
the function will call _stop_thread_lock
*/
void osa_destroy_thread(osa_thread_t handle)
{
struct _thread *_thrd = (struct _thread *)handle;
OSA_ASSERT(_thrd && IS_THREAD_VALID(_thrd));
if (_THREAD_STOPPED != _thrd->status)
_stop_thread_lock(_thrd);
CLEAN_THREAD_MAGIC(_thrd);
kfree(_thrd);
}
OSA_EXPORT_SYMBOL(osa_destroy_thread);
/*
* Name: _stop_thread
*
* Description: stop the thread synchronously
*
* Params: _thrd - the thread structure
*
* Returns: none
*
* Notes: caller must NOT hold the spinlock in thread struct
*/
static void _stop_thread(struct _thread *_thrd)
{
/* kthread_stop is a sync way to stop thread */
kthread_stop(_thrd->task);
/* so here we need not wait for any stopped event */
}
/*
* Name: osa_stop_thread
*
* Description: stop the thread synchronously
*
* Params: handle - handle of the kernel thread to stop
*
* Returns: status code
* OSA_OK - no error
* OSA_THREAD_BAD_STATUS - invalid status of the thread
*
* Notes: none
*/
osa_err_t osa_stop_thread(osa_thread_t handle)
{
struct _thread *_thrd = (struct _thread *)handle;
OSA_ASSERT(_thrd && IS_THREAD_VALID(_thrd));
spin_lock(&(_thrd->lock));
if (_THREAD_UNKNOWN == _thrd->status) {
_thrd->status = _THREAD_STOPPED;
spin_unlock(&(_thrd->lock));
return OSA_OK;
} else if ((_THREAD_READY != _thrd->status)
&& (_THREAD_RUNNING != _thrd->status)) {
spin_unlock(&(_thrd->lock));
osa_dbg_print(DBG_ERR,
"ERROR - the status of thread is invalid when stopping\n");
return OSA_THREAD_BAD_STATUS;
} else {
spin_unlock(&(_thrd->lock));
_stop_thread(_thrd);
return OSA_OK;
}
}
OSA_EXPORT_SYMBOL(osa_stop_thread);
/*
* Name: _stop_thread_lock
*
* Description: stop the thread synchronously with holding lock
*
* Params: _thrd - the thread structure
*
* Returns: none
*
* Notes: none
*/
static void _stop_thread_lock(struct _thread *_thrd)
{
spin_lock(&(_thrd->lock));
if (_THREAD_UNKNOWN == _thrd->status) {
_thrd->status = _THREAD_STOPPED;
spin_unlock(&(_thrd->lock));
} else {
spin_unlock(&(_thrd->lock));
_stop_thread(_thrd);
}
}
/*
* Name: osa_stop_thread_async
*
* Description: stop the thread asynchronously
*
* Params: handle - handle of the kernel thread to stop
*
* Returns: status code
* OSA_OK - no error
* OSA_THREAD_BAD_STATUS - invalid status of the thread
* OSA_THREAD_STOP_FAILED - failed to invoke the stopping thread
*
* Notes: no support in linux kernel
*/
osa_err_t osa_stop_thread_async(osa_thread_t handle)
{
struct _thread *_thrd = (struct _thread *)handle;
OSA_ASSERT(_thrd && IS_THREAD_VALID(_thrd));
if ((_THREAD_UNKNOWN != _thrd->status)
&& (_THREAD_READY != _thrd->status)
&& (_THREAD_RUNNING != _thrd->status)) {
return OSA_THREAD_BAD_STATUS;
}
if (kthread_run
((int (*)(void *))_stop_thread_lock, _thrd,
"osa_thread_stop_func"))
return OSA_OK;
else
return OSA_THREAD_STOP_FAILED;
}
OSA_EXPORT_SYMBOL(osa_stop_thread_async);
/*
* Name: osa_thread_should_stop
*
* Description: the function returns the flag to define
whether the thread should be stopped
*
* Params: none
*
* Returns: bool
* true - the thread should be stopped as soon as possible
* false - vice versa
*
* Notes: the function should be called in the main loop
* of the core function in kernel thread
*/
bool osa_thread_should_stop(void)
{
return kthread_should_stop();
}
OSA_EXPORT_SYMBOL(osa_thread_should_stop);
/*
* Name: osa_get_thread_id
*
* Description: get the id of the kernel thread
*
* Params: handle - handle of the kernel thread
*
* Returns: osa_thread_id_t - the id number of the kernel thread
*
* Notes: none
*/
osa_thread_id_t osa_get_thread_id(osa_thread_t handle)
{
struct _thread *_thrd = (struct _thread *)handle;
OSA_ASSERT(_thrd && IS_THREAD_VALID(_thrd));
return (osa_thread_id_t) (_thrd->task->pid);
}
OSA_EXPORT_SYMBOL(osa_get_thread_id);
/*
* Name: osa_get_thread_group_id
*
* Description: get the thread group id of the kernel thread
*
* Params: handle - handle of the kernel thread
*
* Returns: osa_thread_id_t -
the thread group id number of the kernel thread
*
* Notes: none
*/
osa_thread_id_t osa_get_thread_group_id(osa_thread_t handle)
{
struct _thread *_thrd = (struct _thread *)handle;
OSA_ASSERT(_thrd && IS_THREAD_VALID(_thrd));
return (osa_thread_id_t) (_thrd->task->tgid);
}
OSA_EXPORT_SYMBOL(osa_get_thread_group_id);
/*
* Name: osa_get_current_thread_id
*
* Description: get the current thread id
*
* Params: none
*
* Returns: osa_thread_id_t - the thread id
*
* Notes: none
*/
osa_thread_id_t osa_get_current_thread_id(void)
{
return (osa_thread_id_t) (current->pid);
}
OSA_EXPORT_SYMBOL(osa_get_current_thread_id);
/*
* Name: osa_get_current_thread_group_id
*
* Description: get the current thread group id
*
* Params: none
*
* Returns: osa_thread_id_t - the thread group id
*
* Notes: none
*/
osa_thread_id_t osa_get_current_thread_group_id(void)
{
return (osa_thread_id_t) (current->tgid);
}
OSA_EXPORT_SYMBOL(osa_get_current_thread_group_id);