1 /*
2 * Copyright 2007-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Philippe Houdoin, philippe.houdoin@free.fr
7 */
8
9
10 //! Deferred Procedure Call support kernel module
11
12
13 #include <KernelExport.h>
14
15 #include <stdio.h>
16 #include <stdlib.h>
17
18 #include <dpc.h>
19
20
21 // Private DPC queue structures
22 typedef struct {
23 dpc_func function;
24 void *arg;
25 } dpc_slot;
26
27
28 typedef struct {
29 thread_id thread;
30 sem_id wakeup_sem;
31 spinlock lock;
32 int size;
33 int count;
34 int head;
35 int tail;
36 dpc_slot slots[0];
37 // size * slots follow
38 } dpc_queue;
39
40 #define DPC_QUEUE_SIZE 64
41
42 static int32
dpc_thread(void * arg)43 dpc_thread(void *arg)
44 {
45 dpc_queue *queue = arg;
46 dpc_slot dpc;
47
48 // Let's wait forever/until semaphore death for new DPC slot to show up
49 while (acquire_sem(queue->wakeup_sem) == B_OK) {
50 cpu_status former;
51
52 // grab the next dpc slot
53 former = disable_interrupts();
54 acquire_spinlock(&queue->lock);
55
56 dpc = queue->slots[queue->head];
57 queue->head = (queue->head + 1) % queue->size;
58 queue->count--;
59
60 release_spinlock(&queue->lock);
61 restore_interrupts(former);
62
63 dpc.function(dpc.arg);
64 }
65
66 // Let's finish the pending DPCs, if any.
67 // Otherwise, resource could leak...
68 while (queue->count--) {
69 dpc = queue->slots[queue->head];
70 queue->head = (queue->head + 1) % queue->size;
71 dpc.function(dpc.arg);
72 }
73
74 // Now, let's die quietly, ignored by all... sigh.
75 return 0;
76 }
77
78
79 // #pragma mark - public API
80
81
82 static status_t
new_dpc_queue(void ** handle,const char * name,int32 priority)83 new_dpc_queue(void **handle, const char *name, int32 priority)
84 {
85 char str[64];
86 dpc_queue *queue;
87
88 if (!handle)
89 return B_BAD_VALUE;
90
91 queue = malloc(sizeof(dpc_queue) + DPC_QUEUE_SIZE * sizeof(dpc_slot));
92 if (!queue)
93 return B_NO_MEMORY;
94
95 queue->head = queue->tail = 0;
96 queue->size = DPC_QUEUE_SIZE;
97 queue->count = 0;
98 B_INITIALIZE_SPINLOCK(&queue->lock); // Init the spinlock
99
100 snprintf(str, sizeof(str), "%.*s_wakeup_sem",
101 (int) sizeof(str) - 11, name);
102
103 queue->wakeup_sem = create_sem(0, str);
104 if (queue->wakeup_sem < B_OK) {
105 status_t status = queue->wakeup_sem;
106 free(queue);
107 return status;
108 }
109
110 // Fire a kernel thread to actually handle (aka call them!)
111 // the queued/deferred procedure calls
112 queue->thread = spawn_kernel_thread(dpc_thread, name, priority, queue);
113 if (queue->thread < 0) {
114 status_t status = queue->thread;
115 delete_sem(queue->wakeup_sem);
116 free(queue);
117 return status;
118 }
119 resume_thread(queue->thread);
120
121 *handle = queue;
122
123 return B_OK;
124 }
125
126
127 static status_t
delete_dpc_queue(void * handle)128 delete_dpc_queue(void *handle)
129 {
130 dpc_queue *queue = handle;
131 thread_id thread;
132 status_t exit_value;
133 cpu_status former;
134
135 if (!queue)
136 return B_BAD_VALUE;
137
138 // Close the queue: queue_dpc() should know we're closing:
139 former = disable_interrupts();
140 acquire_spinlock(&queue->lock);
141
142 thread = queue->thread;
143 queue->thread = -1;
144
145 release_spinlock(&queue->lock);
146 restore_interrupts(former);
147
148 // Wakeup the thread by murdering its favorite semaphore
149 delete_sem(queue->wakeup_sem);
150 wait_for_thread(thread, &exit_value);
151
152 free(queue);
153
154 return B_OK;
155 }
156
157
158 static status_t
queue_dpc(void * handle,dpc_func function,void * arg)159 queue_dpc(void *handle, dpc_func function, void *arg)
160 {
161 dpc_queue *queue = handle;
162 cpu_status former;
163 status_t status = B_OK;
164
165 if (!queue || !function)
166 return B_BAD_VALUE;
167
168 // Try to be safe being called from interrupt handlers:
169 former = disable_interrupts();
170 acquire_spinlock(&queue->lock);
171
172 if (queue->thread < 0) {
173 // Queue thread is dying...
174 status = B_CANCELED;
175 } else if (queue->count == queue->size)
176 // This DPC queue is full, sorry
177 status = B_NO_MEMORY;
178 else {
179 queue->slots[queue->tail].function = function;
180 queue->slots[queue->tail].arg = arg;
181 queue->tail = (queue->tail + 1) % queue->size;
182 queue->count++;
183 }
184
185 release_spinlock(&queue->lock);
186 restore_interrupts(former);
187
188 if (status == B_OK)
189 // Wake up the corresponding dpc thread
190 // Notice that interrupt handlers should return B_INVOKE_SCHEDULER to
191 // shorten DPC latency as much as possible...
192 status = release_sem_etc(queue->wakeup_sem, 1, B_DO_NOT_RESCHEDULE);
193
194 return status;
195 }
196
197
198 static status_t
std_ops(int32 op,...)199 std_ops(int32 op, ...)
200 {
201 switch (op) {
202 case B_MODULE_INIT:
203 return B_OK;
204 case B_MODULE_UNINIT:
205 return B_OK;
206
207 default:
208 return B_ERROR;
209 }
210 }
211
212
213 static dpc_module_info sDPCModule = {
214 {
215 B_DPC_MODULE_NAME,
216 0,
217 std_ops
218 },
219
220 new_dpc_queue,
221 delete_dpc_queue,
222 queue_dpc
223 };
224
225
226 module_info *modules[] = {
227 (module_info *) &sDPCModule,
228 NULL
229 };
230
231