1 /* 2 * Copyright (c) 2003 Matthew Dillon <dillon@backplane.com> 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, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $DragonFly: src/sys/kern/lwkt_msgport.c,v 1.4 2003/07/25 05:26:50 dillon Exp $ 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/kernel.h> 32 #include <sys/proc.h> 33 #include <sys/rtprio.h> 34 #include <sys/queue.h> 35 #include <sys/sysctl.h> 36 #include <sys/kthread.h> 37 #include <machine/cpu.h> 38 #include <sys/lock.h> 39 40 #include <vm/vm.h> 41 #include <vm/vm_param.h> 42 #include <vm/vm_kern.h> 43 #include <vm/vm_object.h> 44 #include <vm/vm_page.h> 45 #include <vm/vm_map.h> 46 #include <vm/vm_pager.h> 47 #include <vm/vm_extern.h> 48 #include <vm/vm_zone.h> 49 50 #include <sys/thread2.h> 51 #include <sys/msgport2.h> 52 53 #include <machine/stdarg.h> 54 #include <machine/ipl.h> 55 #ifdef SMP 56 #include <machine/smp.h> 57 #endif 58 59 60 /************************************************************************ 61 * MESSAGE FUNCTIONS * 62 ************************************************************************/ 63 64 static void lwkt_replyport_remote(lwkt_msg_t msg); 65 static void lwkt_putport_remote(lwkt_msg_t msg); 66 static void lwkt_abortport_remote(lwkt_port_t port); 67 68 void 69 lwkt_initmsg_td(lwkt_msg_t msg, thread_t td) 70 { 71 lwkt_initmsg(msg, 0); 72 msg->ms_reply_port = &td->td_msgport; 73 } 74 75 /* 76 * lwkt_sendmsg() 77 * 78 * Send a message asynchronously. This function requests asynchronous 79 * completion and calls lwkt_beginmsg(). If the target port decides to 80 * run the message synchronously this function will automatically queue 81 * the message to the current thread's message queue to present a 82 * consistent interface to the caller. 83 * 84 * The message's ms_cmd must be initialized and its ms_flags must be 85 * at least zero'd out. lwkt_sendmsg() will initialize the message's 86 * reply port to the current thread's built-in reply port. 87 */ 88 void 89 lwkt_sendmsg(lwkt_port_t port, lwkt_msg_t msg) 90 { 91 int error; 92 93 msg->ms_flags |= MSGF_ASYNC; 94 msg->ms_flags &= ~(MSGF_REPLY | MSGF_QUEUED); 95 msg->ms_reply_port = &curthread->td_msgport; 96 msg->ms_abortreq = 0; 97 if ((error = lwkt_beginmsg(port, msg)) != EASYNC) { 98 lwkt_replymsg(msg, error); 99 } 100 } 101 102 /* 103 * lwkt_domsg() 104 * 105 * Send a message synchronously. This function requests synchronous 106 * completion and calls lwkt_beginmsg(). If the target port decides to 107 * run the message asynchronously this function will block waiting for 108 * the message to complete. Since MSGF_ASYNC is not set the target 109 * will not attempt to queue the reply to a reply port but will simply 110 * wake up anyone waiting on the message. 111 * 112 * A synchronous error code is always returned. 113 * 114 * The message's ms_cmd must be initialized and its ms_flags must be 115 * at least zero'd out. lwkt_domsg() will initialize the message's 116 * reply port to the current thread's built-in reply port. 117 */ 118 int 119 lwkt_domsg(lwkt_port_t port, lwkt_msg_t msg) 120 { 121 int error; 122 123 msg->ms_flags &= ~(MSGF_ASYNC | MSGF_REPLY | MSGF_QUEUED); 124 msg->ms_reply_port = &curthread->td_msgport; 125 msg->ms_abortreq = 0; 126 if ((error = lwkt_beginmsg(port, msg)) == EASYNC) { 127 error = lwkt_waitmsg(msg); 128 } 129 return(error); 130 } 131 132 /* 133 * lwkt_waitmsg() 134 * 135 * Wait for a message that we originated to complete, remove it from 136 * the reply queue if necessary, and return its error code. 137 * 138 * This call may be used in virtually any situation, including for the 139 * case where you used lwkt_sendmsg() to initiate the message. 140 * 141 * Note that we don't own the message any more so we cannot safely 142 * modify ms_flags, meaning we can't clear MSGF_ASYNC as an optimization. 143 * However, since any remote cpu replying to the message will IPI the 144 * message over to us for action, a critical section is sufficient to 145 * protect td_msgq. 146 */ 147 int 148 lwkt_waitmsg(lwkt_msg_t msg) 149 { 150 lwkt_port_t port; 151 152 /* 153 * Done but not queued case (message was originally a synchronous request) 154 */ 155 if ((msg->ms_flags & (MSGF_DONE|MSGF_REPLY)) == MSGF_DONE) 156 return(msg->ms_error); 157 158 port = msg->ms_reply_port; 159 KKASSERT(port->mp_td == curthread); /* for now */ 160 crit_enter(); 161 if ((msg->ms_flags & MSGF_DONE) == 0) { 162 port->mp_flags |= MSGPORTF_WAITING; 163 do { 164 lwkt_deschedule_self(); 165 lwkt_switch(); 166 } while ((msg->ms_flags & MSGF_DONE) == 0); 167 port->mp_flags &= ~MSGPORTF_WAITING; 168 } 169 /* 170 * We own the message now. 171 */ 172 if (msg->ms_flags & MSGF_QUEUED) { 173 msg->ms_flags &= ~MSGF_QUEUED; 174 TAILQ_REMOVE(&port->mp_msgq, msg, ms_node); 175 } 176 crit_exit(); 177 return(msg->ms_error); 178 } 179 180 181 /************************************************************************ 182 * PORT FUNCTIONS * 183 ************************************************************************/ 184 185 /* 186 * lwkt_initport() 187 * 188 * Initialize a port for use and assign it to the specified thread. 189 */ 190 void 191 lwkt_init_port(lwkt_port_t port, thread_t td) 192 { 193 bzero(port, sizeof(*port)); 194 TAILQ_INIT(&port->mp_msgq); 195 port->mp_td = td; 196 port->mp_beginmsg = lwkt_putport; 197 port->mp_abortmsg = lwkt_abortport; 198 port->mp_returnmsg = lwkt_replyport; 199 } 200 201 /* 202 * lwkt_replyport() 203 * 204 * This function is typically assigned to the mp_replymsg port vector. 205 * 206 * The message is being returned to the specified port. The port is 207 * owned by the mp_td thread. If we are on the same cpu as the mp_td 208 * thread we can trivially queue the message to the messageq, otherwise 209 * we have to send an ipi message to the correct cpu. We then schedule 210 * the target thread. 211 * 212 * If MSGF_ASYNC is not set we do not bother queueing the message, we 213 * just set the DONE bit. 214 * 215 * Note that the IPIQ callback function (*_remote) is entered with a 216 * critical section already held. 217 */ 218 219 static __inline 220 void 221 _lwkt_replyport(lwkt_port_t port, lwkt_msg_t msg) 222 { 223 thread_t td = port->mp_td; 224 225 if (td->td_gd == mycpu) { 226 TAILQ_INSERT_TAIL(&port->mp_msgq, msg, ms_node); 227 msg->ms_flags |= MSGF_DONE | MSGF_REPLY | MSGF_QUEUED; 228 if (port->mp_flags & MSGPORTF_WAITING) 229 lwkt_schedule(td); 230 } else { 231 lwkt_send_ipiq(td->td_gd->gd_cpuid, (ipifunc_t)lwkt_replyport_remote, msg); 232 } 233 } 234 235 static 236 void 237 lwkt_replyport_remote(lwkt_msg_t msg) 238 { 239 _lwkt_replyport(msg->ms_reply_port, msg); 240 } 241 242 243 void 244 lwkt_replyport(lwkt_port_t port, lwkt_msg_t msg) 245 { 246 crit_enter(); 247 if (msg->ms_flags & MSGF_ASYNC) { 248 _lwkt_replyport(port, msg); 249 } else { 250 msg->ms_flags |= MSGF_DONE; 251 if (port->mp_flags & MSGPORTF_WAITING) 252 lwkt_schedule(port->mp_td); 253 } 254 crit_exit(); 255 } 256 257 /* 258 * lwkt_putport() 259 * 260 * This function is typically assigned to the mp_beginmsg port vector. 261 * 262 * Queue a message to the target port and wakeup the thread owning it. 263 * This function always returns EASYNC and may be assigned to a 264 * message port's mp_beginmsg function vector. 265 */ 266 static 267 __inline 268 void 269 _lwkt_putport(lwkt_port_t port, lwkt_msg_t msg) 270 { 271 thread_t td = port->mp_td; 272 273 if (td->td_gd == mycpu) { 274 TAILQ_INSERT_TAIL(&port->mp_msgq, msg, ms_node); 275 msg->ms_flags |= MSGF_QUEUED; 276 if (port->mp_flags & MSGPORTF_WAITING) 277 lwkt_schedule(td); 278 } else { 279 msg->ms_target_port = port; 280 lwkt_send_ipiq(td->td_gd->gd_cpuid, (ipifunc_t)lwkt_putport_remote, msg); 281 } 282 } 283 284 static 285 void 286 lwkt_putport_remote(lwkt_msg_t msg) 287 { 288 _lwkt_putport(msg->ms_target_port, msg); 289 } 290 291 int 292 lwkt_putport(lwkt_port_t port, lwkt_msg_t msg) 293 { 294 crit_enter(); 295 _lwkt_putport(port, msg); 296 crit_exit(); 297 return(EASYNC); 298 } 299 300 /* 301 * lwkt_abortport() 302 * 303 * This function is typically assigned to the mp_abortmsg port vector. 304 * 305 * This function attempts to abort a message. Aborts are always 306 * optional, so we could just do nothing if we wanted. We get onto the 307 * cpu owning the port, check to see if the message is queued on the 308 * port's message queue, and remove and abort it if it is. Otherwise 309 * we do nothing. 310 * 311 * Note that we cannot safely use ms_target_port because the port might 312 * have forwarded the message on to some other port and we could race 313 * against that use of ms_target_port. 314 */ 315 static 316 __inline 317 void 318 _lwkt_abortport(lwkt_port_t port) 319 { 320 thread_t td = port->mp_td; 321 lwkt_msg_t msg; 322 323 if (td->td_gd == mycpu) { 324 again: 325 msg = TAILQ_FIRST(&port->mp_msgq); 326 while (msg) { 327 if (msg->ms_abortreq) { 328 TAILQ_REMOVE(&port->mp_msgq, msg, ms_node); 329 msg->ms_flags &= ~MSGF_QUEUED; 330 lwkt_replymsg(msg, ECONNABORTED); /* YYY dangerous from IPI? */ 331 goto again; 332 } 333 msg = TAILQ_NEXT(msg, ms_node); 334 } 335 } else { 336 lwkt_send_ipiq(td->td_gd->gd_cpuid, (ipifunc_t)lwkt_abortport_remote, port); 337 } 338 } 339 340 static 341 void 342 lwkt_abortport_remote(lwkt_port_t port) 343 { 344 _lwkt_abortport(port); 345 } 346 347 void 348 lwkt_abortport(lwkt_port_t port, lwkt_msg_t msg) 349 { 350 msg->ms_abortreq = 1; 351 crit_enter(); 352 _lwkt_abortport(port); 353 crit_exit(); 354 } 355 356 /* 357 * lwkt_getport() 358 * 359 * Retrieve the next message from the port's message queue, return NULL 360 * if no messages are pending. 361 * 362 * The calling thread MUST own the port. 363 */ 364 void * 365 lwkt_getport(lwkt_port_t port) 366 { 367 lwkt_msg_t msg; 368 369 KKASSERT(port->mp_td == curthread); 370 371 crit_enter(); 372 if ((msg = TAILQ_FIRST(&port->mp_msgq)) != NULL) { 373 TAILQ_REMOVE(&port->mp_msgq, msg, ms_node); 374 msg->ms_flags &= ~MSGF_QUEUED; 375 } 376 crit_exit(); 377 return(msg); 378 } 379 380 /* 381 * lwkt_waitport() 382 * 383 * Retrieve the next message from the port's message queue, block until 384 * a message is ready. This function never returns NULL. 385 */ 386 void * 387 lwkt_waitport(lwkt_port_t port) 388 { 389 lwkt_msg_t msg; 390 391 KKASSERT(port->mp_td == curthread); 392 393 crit_enter(); 394 if ((msg = TAILQ_FIRST(&port->mp_msgq)) == NULL) { 395 port->mp_flags |= MSGPORTF_WAITING; 396 do { 397 lwkt_deschedule_self(); 398 lwkt_switch(); 399 } while ((msg = TAILQ_FIRST(&port->mp_msgq)) == NULL); 400 port->mp_flags &= ~MSGPORTF_WAITING; 401 } 402 TAILQ_REMOVE(&port->mp_msgq, msg, ms_node); 403 msg->ms_flags &= ~MSGF_QUEUED; 404 crit_exit(); 405 return(msg); 406 } 407 408