⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sched.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * linux/net/sunrpc/sched.c * * Scheduling for synchronous and asynchronous RPC requests. * * Copyright (C) 1996 Olaf Kirch, <okir@monad.swb.de> * * TCP NFS related read + write fixes * (C) 1999 Dave Airlie, University of Limerick, Ireland <airlied@linux.ie> */#include <linux/module.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/mempool.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/spinlock.h>#include <linux/mutex.h>#include <linux/sunrpc/clnt.h>#ifdef RPC_DEBUG#define RPCDBG_FACILITY		RPCDBG_SCHED#define RPC_TASK_MAGIC_ID	0xf00baa#endif/* * RPC slabs and memory pools */#define RPC_BUFFER_MAXSIZE	(2048)#define RPC_BUFFER_POOLSIZE	(8)#define RPC_TASK_POOLSIZE	(8)static struct kmem_cache	*rpc_task_slabp __read_mostly;static struct kmem_cache	*rpc_buffer_slabp __read_mostly;static mempool_t	*rpc_task_mempool __read_mostly;static mempool_t	*rpc_buffer_mempool __read_mostly;static void			__rpc_default_timer(struct rpc_task *task);static void			rpc_async_schedule(struct work_struct *);static void			 rpc_release_task(struct rpc_task *task);/* * RPC tasks sit here while waiting for conditions to improve. */static RPC_WAITQ(delay_queue, "delayq");/* * rpciod-related stuff */struct workqueue_struct *rpciod_workqueue;/* * Disable the timer for a given RPC task. Should be called with * queue->lock and bh_disabled in order to avoid races within * rpc_run_timer(). */static inline void__rpc_disable_timer(struct rpc_task *task){	dprintk("RPC: %5u disabling timer\n", task->tk_pid);	task->tk_timeout_fn = NULL;	task->tk_timeout = 0;}/* * Run a timeout function. * We use the callback in order to allow __rpc_wake_up_task() * and friends to disable the timer synchronously on SMP systems * without calling del_timer_sync(). The latter could cause a * deadlock if called while we're holding spinlocks... */static void rpc_run_timer(struct rpc_task *task){	void (*callback)(struct rpc_task *);	callback = task->tk_timeout_fn;	task->tk_timeout_fn = NULL;	if (callback && RPC_IS_QUEUED(task)) {		dprintk("RPC: %5u running timer\n", task->tk_pid);		callback(task);	}	smp_mb__before_clear_bit();	clear_bit(RPC_TASK_HAS_TIMER, &task->tk_runstate);	smp_mb__after_clear_bit();}/* * Set up a timer for the current task. */static inline void__rpc_add_timer(struct rpc_task *task, rpc_action timer){	if (!task->tk_timeout)		return;	dprintk("RPC: %5u setting alarm for %lu ms\n",			task->tk_pid, task->tk_timeout * 1000 / HZ);	if (timer)		task->tk_timeout_fn = timer;	else		task->tk_timeout_fn = __rpc_default_timer;	set_bit(RPC_TASK_HAS_TIMER, &task->tk_runstate);	mod_timer(&task->tk_timer, jiffies + task->tk_timeout);}/* * Delete any timer for the current task. Because we use del_timer_sync(), * this function should never be called while holding queue->lock. */static voidrpc_delete_timer(struct rpc_task *task){	if (RPC_IS_QUEUED(task))		return;	if (test_and_clear_bit(RPC_TASK_HAS_TIMER, &task->tk_runstate)) {		del_singleshot_timer_sync(&task->tk_timer);		dprintk("RPC: %5u deleting timer\n", task->tk_pid);	}}/* * Add new request to a priority queue. */static void __rpc_add_wait_queue_priority(struct rpc_wait_queue *queue, struct rpc_task *task){	struct list_head *q;	struct rpc_task *t;	INIT_LIST_HEAD(&task->u.tk_wait.links);	q = &queue->tasks[task->tk_priority];	if (unlikely(task->tk_priority > queue->maxpriority))		q = &queue->tasks[queue->maxpriority];	list_for_each_entry(t, q, u.tk_wait.list) {		if (t->tk_cookie == task->tk_cookie) {			list_add_tail(&task->u.tk_wait.list, &t->u.tk_wait.links);			return;		}	}	list_add_tail(&task->u.tk_wait.list, q);}/* * Add new request to wait queue. * * Swapper tasks always get inserted at the head of the queue. * This should avoid many nasty memory deadlocks and hopefully * improve overall performance. * Everyone else gets appended to the queue to ensure proper FIFO behavior. */static void __rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task){	BUG_ON (RPC_IS_QUEUED(task));	if (RPC_IS_PRIORITY(queue))		__rpc_add_wait_queue_priority(queue, task);	else if (RPC_IS_SWAPPER(task))		list_add(&task->u.tk_wait.list, &queue->tasks[0]);	else		list_add_tail(&task->u.tk_wait.list, &queue->tasks[0]);	task->u.tk_wait.rpc_waitq = queue;	queue->qlen++;	rpc_set_queued(task);	dprintk("RPC: %5u added to queue %p \"%s\"\n",			task->tk_pid, queue, rpc_qname(queue));}/* * Remove request from a priority queue. */static void __rpc_remove_wait_queue_priority(struct rpc_task *task){	struct rpc_task *t;	if (!list_empty(&task->u.tk_wait.links)) {		t = list_entry(task->u.tk_wait.links.next, struct rpc_task, u.tk_wait.list);		list_move(&t->u.tk_wait.list, &task->u.tk_wait.list);		list_splice_init(&task->u.tk_wait.links, &t->u.tk_wait.links);	}	list_del(&task->u.tk_wait.list);}/* * Remove request from queue. * Note: must be called with spin lock held. */static void __rpc_remove_wait_queue(struct rpc_task *task){	struct rpc_wait_queue *queue;	queue = task->u.tk_wait.rpc_waitq;	if (RPC_IS_PRIORITY(queue))		__rpc_remove_wait_queue_priority(task);	else		list_del(&task->u.tk_wait.list);	queue->qlen--;	dprintk("RPC: %5u removed from queue %p \"%s\"\n",			task->tk_pid, queue, rpc_qname(queue));}static inline void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority){	queue->priority = priority;	queue->count = 1 << (priority * 2);}static inline void rpc_set_waitqueue_cookie(struct rpc_wait_queue *queue, unsigned long cookie){	queue->cookie = cookie;	queue->nr = RPC_BATCH_COUNT;}static inline void rpc_reset_waitqueue_priority(struct rpc_wait_queue *queue){	rpc_set_waitqueue_priority(queue, queue->maxpriority);	rpc_set_waitqueue_cookie(queue, 0);}static void __rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const char *qname, int maxprio){	int i;	spin_lock_init(&queue->lock);	for (i = 0; i < ARRAY_SIZE(queue->tasks); i++)		INIT_LIST_HEAD(&queue->tasks[i]);	queue->maxpriority = maxprio;	rpc_reset_waitqueue_priority(queue);#ifdef RPC_DEBUG	queue->name = qname;#endif}void rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const char *qname){	__rpc_init_priority_wait_queue(queue, qname, RPC_PRIORITY_HIGH);}void rpc_init_wait_queue(struct rpc_wait_queue *queue, const char *qname){	__rpc_init_priority_wait_queue(queue, qname, 0);}EXPORT_SYMBOL(rpc_init_wait_queue);static int rpc_wait_bit_interruptible(void *word){	if (signal_pending(current))		return -ERESTARTSYS;	schedule();	return 0;}#ifdef RPC_DEBUGstatic void rpc_task_set_debuginfo(struct rpc_task *task){	static atomic_t rpc_pid;	task->tk_magic = RPC_TASK_MAGIC_ID;	task->tk_pid = atomic_inc_return(&rpc_pid);}#elsestatic inline void rpc_task_set_debuginfo(struct rpc_task *task){}#endifstatic void rpc_set_active(struct rpc_task *task){	struct rpc_clnt *clnt;	if (test_and_set_bit(RPC_TASK_ACTIVE, &task->tk_runstate) != 0)		return;	rpc_task_set_debuginfo(task);	/* Add to global list of all tasks */	clnt = task->tk_client;	if (clnt != NULL) {		spin_lock(&clnt->cl_lock);		list_add_tail(&task->tk_task, &clnt->cl_tasks);		spin_unlock(&clnt->cl_lock);	}}/* * Mark an RPC call as having completed by clearing the 'active' bit */static void rpc_mark_complete_task(struct rpc_task *task){	smp_mb__before_clear_bit();	clear_bit(RPC_TASK_ACTIVE, &task->tk_runstate);	smp_mb__after_clear_bit();	wake_up_bit(&task->tk_runstate, RPC_TASK_ACTIVE);}/* * Allow callers to wait for completion of an RPC call */int __rpc_wait_for_completion_task(struct rpc_task *task, int (*action)(void *)){	if (action == NULL)		action = rpc_wait_bit_interruptible;	return wait_on_bit(&task->tk_runstate, RPC_TASK_ACTIVE,			action, TASK_INTERRUPTIBLE);}EXPORT_SYMBOL(__rpc_wait_for_completion_task);/* * Make an RPC task runnable. * * Note: If the task is ASYNC, this must be called with * the spinlock held to protect the wait queue operation. */static void rpc_make_runnable(struct rpc_task *task){	BUG_ON(task->tk_timeout_fn);	rpc_clear_queued(task);	if (rpc_test_and_set_running(task))		return;	/* We might have raced */	if (RPC_IS_QUEUED(task)) {		rpc_clear_running(task);		return;	}	if (RPC_IS_ASYNC(task)) {		int status;		INIT_WORK(&task->u.tk_work, rpc_async_schedule);		status = queue_work(task->tk_workqueue, &task->u.tk_work);		if (status < 0) {			printk(KERN_WARNING "RPC: failed to add task to queue: error: %d!\n", status);			task->tk_status = status;			return;		}	} else		wake_up_bit(&task->tk_runstate, RPC_TASK_QUEUED);}/* * Prepare for sleeping on a wait queue. * By always appending tasks to the list we ensure FIFO behavior. * NB: An RPC task will only receive interrupt-driven events as long * as it's on a wait queue. */static void __rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,			rpc_action action, rpc_action timer){	dprintk("RPC: %5u sleep_on(queue \"%s\" time %lu)\n",			task->tk_pid, rpc_qname(q), jiffies);	if (!RPC_IS_ASYNC(task) && !RPC_IS_ACTIVATED(task)) {		printk(KERN_ERR "RPC: Inactive synchronous task put to sleep!\n");		return;	}	__rpc_add_wait_queue(q, task);	BUG_ON(task->tk_callback != NULL);	task->tk_callback = action;	__rpc_add_timer(task, timer);}void rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,				rpc_action action, rpc_action timer){	/* Mark the task as being activated if so needed */	rpc_set_active(task);	/*	 * Protect the queue operations.	 */	spin_lock_bh(&q->lock);	__rpc_sleep_on(q, task, action, timer);	spin_unlock_bh(&q->lock);}/** * __rpc_do_wake_up_task - wake up a single rpc_task * @task: task to be woken up * * Caller must hold queue->lock, and have cleared the task queued flag. */static void __rpc_do_wake_up_task(struct rpc_task *task){	dprintk("RPC: %5u __rpc_wake_up_task (now %lu)\n",			task->tk_pid, jiffies);#ifdef RPC_DEBUG	BUG_ON(task->tk_magic != RPC_TASK_MAGIC_ID);#endif	/* Has the task been executed yet? If not, we cannot wake it up! */	if (!RPC_IS_ACTIVATED(task)) {		printk(KERN_ERR "RPC: Inactive task (%p) being woken up!\n", task);		return;	}	__rpc_disable_timer(task);	__rpc_remove_wait_queue(task);	rpc_make_runnable(task);	dprintk("RPC:       __rpc_wake_up_task done\n");}/* * Wake up the specified task */static void __rpc_wake_up_task(struct rpc_task *task){	if (rpc_start_wakeup(task)) {		if (RPC_IS_QUEUED(task))			__rpc_do_wake_up_task(task);		rpc_finish_wakeup(task);	}}/* * Default timeout handler if none specified by user */static void__rpc_default_timer(struct rpc_task *task){	dprintk("RPC: %5u timeout (default timer)\n", task->tk_pid);	task->tk_status = -ETIMEDOUT;	rpc_wake_up_task(task);}/* * Wake up the specified task */void rpc_wake_up_task(struct rpc_task *task){	rcu_read_lock_bh();	if (rpc_start_wakeup(task)) {		if (RPC_IS_QUEUED(task)) {			struct rpc_wait_queue *queue = task->u.tk_wait.rpc_waitq;			/* Note: we're already in a bh-safe context */			spin_lock(&queue->lock);			__rpc_do_wake_up_task(task);			spin_unlock(&queue->lock);		}		rpc_finish_wakeup(task);	}	rcu_read_unlock_bh();}/* * Wake up the next task on a priority queue. */static struct rpc_task * __rpc_wake_up_next_priority(struct rpc_wait_queue *queue){	struct list_head *q;	struct rpc_task *task;	/*	 * Service a batch of tasks from a single cookie.	 */	q = &queue->tasks[queue->priority];	if (!list_empty(q)) {		task = list_entry(q->next, struct rpc_task, u.tk_wait.list);		if (queue->cookie == task->tk_cookie) {			if (--queue->nr)				goto out;			list_move_tail(&task->u.tk_wait.list, q);		}		/*		 * Check if we need to switch queues.		 */		if (--queue->count)			goto new_cookie;	}	/*	 * Service the next queue.	 */	do {		if (q == &queue->tasks[0])			q = &queue->tasks[queue->maxpriority];		else			q = q - 1;		if (!list_empty(q)) {			task = list_entry(q->next, struct rpc_task, u.tk_wait.list);			goto new_queue;		}	} while (q != &queue->tasks[queue->priority]);	rpc_reset_waitqueue_priority(queue);	return NULL;new_queue:	rpc_set_waitqueue_priority(queue, (unsigned int)(q - &queue->tasks[0]));new_cookie:	rpc_set_waitqueue_cookie(queue, task->tk_cookie);out:	__rpc_wake_up_task(task);	return task;}/* * Wake up the next task on the wait queue. */struct rpc_task * rpc_wake_up_next(struct rpc_wait_queue *queue){	struct rpc_task	*task = NULL;	dprintk("RPC:       wake_up_next(%p \"%s\")\n",			queue, rpc_qname(queue));	rcu_read_lock_bh();	spin_lock(&queue->lock);	if (RPC_IS_PRIORITY(queue))		task = __rpc_wake_up_next_priority(queue);	else {		task_for_first(task, &queue->tasks[0])			__rpc_wake_up_task(task);	}	spin_unlock(&queue->lock);	rcu_read_unlock_bh();	return task;}/** * rpc_wake_up - wake up all rpc_tasks

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -