xref: /dragonfly/sys/dev/drm/linux_wait.c (revision 3f2dd94a569761201b5b0a18b2f697f97fe1b9dc)
1 /*
2  * Copyright (c) 2019-2020 François Tigeot <ftigeot@wolfpond.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <linux/wait.h>
28 #include <linux/wait_bit.h>
29 #include <linux/sched.h>
30 
31 int
default_wake_function(wait_queue_entry_t * q,unsigned mode,int wake_flags,void * key)32 default_wake_function(wait_queue_entry_t *q, unsigned mode, int wake_flags, void *key)
33 {
34           return wake_up_process(q->private);
35 }
36 
37 int
autoremove_wake_function(wait_queue_entry_t * wait,unsigned mode,int sync,void * key)38 autoremove_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync, void *key)
39 {
40           int ret = default_wake_function(wait, mode, sync, key);
41 
42           /* Was the process woken up ? */
43           if (ret)
44                     list_del_init(&wait->entry);
45 
46           return ret;
47 }
48 
49 void
__wake_up_core(wait_queue_head_t * q,int num_to_wake_up)50 __wake_up_core(wait_queue_head_t *q, int num_to_wake_up)
51 {
52           wait_queue_entry_t *curr, *next;
53           int mode = TASK_NORMAL;
54 
55           list_for_each_entry_safe(curr, next, &q->head, entry) {
56                     if (curr->func(curr, mode, 0, NULL))
57                               num_to_wake_up--;
58 
59                     if (num_to_wake_up == 0)
60                               break;
61           }
62 }
63 
64 void
__wait_event_prefix(wait_queue_head_t * wq,int flags)65 __wait_event_prefix(wait_queue_head_t *wq, int flags)
66 {
67           lockmgr(&wq->lock, LK_EXCLUSIVE);
68           if (flags & PCATCH) {
69                     set_current_state(TASK_INTERRUPTIBLE);
70           } else {
71                     set_current_state(TASK_UNINTERRUPTIBLE);
72           }
73           lockmgr(&wq->lock, LK_RELEASE);
74 }
75 
76 void
prepare_to_wait(wait_queue_head_t * q,wait_queue_entry_t * wait,int state)77 prepare_to_wait(wait_queue_head_t *q, wait_queue_entry_t *wait, int state)
78 {
79           lockmgr(&q->lock, LK_EXCLUSIVE);
80           if (list_empty(&wait->entry))
81                     __add_wait_queue(q, wait);
82           set_current_state(state);
83           lockmgr(&q->lock, LK_RELEASE);
84 }
85 
86 void
finish_wait(wait_queue_head_t * q,wait_queue_entry_t * wait)87 finish_wait(wait_queue_head_t *q, wait_queue_entry_t *wait)
88 {
89           set_current_state(TASK_RUNNING);
90 
91           lockmgr(&q->lock, LK_EXCLUSIVE);
92           if (!list_empty(&wait->entry))
93                     list_del_init(&wait->entry);
94           lockmgr(&q->lock, LK_RELEASE);
95 }
96 
97 void
wake_up_bit(void * addr,int bit)98 wake_up_bit(void *addr, int bit)
99 {
100           wakeup_one(addr);
101 }
102 
103 /* Wait for a bit to be cleared or a timeout to expire */
104 int
wait_on_bit_timeout(unsigned long * word,int bit,unsigned mode,unsigned long timeout)105 wait_on_bit_timeout(unsigned long *word, int bit, unsigned mode,
106                         unsigned long timeout)
107 {
108           int rv, awakened = 0, timeout_expired = 0;
109           long start_time;
110 
111           if (!test_bit(bit, word))
112                     return 0;
113 
114           start_time = ticks;
115           set_current_state(mode);
116 
117           do {
118                     rv = tsleep(word, mode, "lwobt", timeout);
119                     if (rv == 0)
120                               awakened = 1;
121                     if (time_after_eq(start_time, timeout))
122                               timeout_expired = 1;
123           } while (test_bit(bit, word) && !timeout_expired);
124 
125           set_current_state(TASK_RUNNING);
126 
127           if (awakened)
128                     return 0;
129 
130           return 1;
131 }
132 
__init_waitqueue_head(wait_queue_head_t * q,const char * name,struct lock_class_key * key)133 void __init_waitqueue_head(wait_queue_head_t *q,
134                                  const char *name, struct lock_class_key *key)
135 {
136           lockinit(&q->lock, "lwq", 0, 0);
137           INIT_LIST_HEAD(&q->head);
138 }
139 
140 int
wait_on_bit(unsigned long * word,int bit,unsigned mode)141 wait_on_bit(unsigned long *word, int bit, unsigned mode)
142 {
143           return wait_on_bit_timeout(word, bit, mode, MAX_SCHEDULE_TIMEOUT);
144 }
145 
146 void
init_wait_entry(struct wait_queue_entry * wq_entry,int flags)147 init_wait_entry(struct wait_queue_entry *wq_entry, int flags)
148 {
149           INIT_LIST_HEAD(&wq_entry->entry);
150           wq_entry->flags = flags;
151           wq_entry->private = current;
152           wq_entry->func = autoremove_wake_function;
153 }
154