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.6 2003/10/22 01:01:16 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, &td->td_msgport, 0); 72 } 73 74 /* 75 * lwkt_sendmsg() 76 * 77 * Send a message asynchronously. This function requests asynchronous 78 * completion and calls lwkt_beginmsg(). If the target port decides to 79 * run the message synchronously this function will automatically queue 80 * the message to the current thread's message queue to present a 81 * consistent interface to the caller. 82 * 83 * The message's ms_cmd must be initialized and its ms_flags must be 84 * at least zero'd out. lwkt_sendmsg() will initialize the message's 85 * reply port to the current thread's built-in reply port. 86 */ 87 void 88 lwkt_sendmsg(lwkt_port_t port, lwkt_msg_t msg) 89 { 90 int error; 91 92 msg->ms_flags |= MSGF_ASYNC; 93 msg->ms_flags &= ~(MSGF_REPLY | MSGF_QUEUED); 94 msg->ms_reply_port = &curthread->td_msgport; 95 msg->ms_abortreq = 0; 96 if ((error = lwkt_beginmsg(port, msg)) != EASYNC) { 97 lwkt_replymsg(msg, error); 98 } 99 } 100 101 /* 102 * lwkt_domsg() 103 * 104 * Send a message synchronously. This function requests synchronous 105 * completion and calls lwkt_beginmsg(). If the target port decides to 106 * run the message asynchronously this function will block waiting for 107 * the message to complete. Since MSGF_ASYNC is not set the target 108 * will not attempt to queue the reply to a reply port but will simply 109 * wake up anyone waiting on the message. 110 * 111 * A synchronous error code is always returned. 112 * 113 * The message's ms_cmd must be initialized and its ms_flags must be 114 * at least zero'd out. lwkt_domsg() will initialize the message's 115 * reply port to the current thread's built-in reply port. 116 */ 117 int 118 lwkt_domsg(lwkt_port_t port, lwkt_msg_t msg) 119 { 120 int error; 121 122 msg->ms_flags &= ~(MSGF_ASYNC | MSGF_REPLY | MSGF_QUEUED); 123 msg->ms_reply_port = &curthread->td_msgport; 124 msg->ms_abortreq = 0; 125 if ((error = lwkt_beginmsg(port, msg)) == EASYNC) { 126 error = lwkt_waitmsg(msg); 127 } 128 return(error); 129 } 130 131 /* 132 * lwkt_waitmsg() 133 * 134 * Wait for a message that we originated to complete, remove it from 135 * the reply queue if necessary, and return its error code. 136 * 137 * This call may be used in virtually any situation, including for the 138 * case where you used lwkt_sendmsg() to initiate the message. 139 * 140 * Note that we don't own the message any more so we cannot safely 141 * modify ms_flags, meaning we can't clear MSGF_ASYNC as an optimization. 142 * However, since any remote cpu replying to the message will IPI the 143 * message over to us for action, a critical section is sufficient to 144 * protect td_msgq. 145 */ 146 int 147 lwkt_waitmsg(lwkt_msg_t msg) 148 { 149 lwkt_port_t port; 150 151 /* 152 * Done but not queued case (message was originally a synchronous request) 153 */ 154 if ((msg->ms_flags & (MSGF_DONE|MSGF_REPLY)) == MSGF_DONE) 155 return(msg->ms_error); 156 157 port = msg->ms_reply_port; 158 KKASSERT(port->mp_td == curthread); /* for now */ 159 crit_enter(); 160 if ((msg->ms_flags & MSGF_DONE) == 0) { 161 port->mp_flags |= MSGPORTF_WAITING; 162 do { 163 lwkt_deschedule_self(); 164 lwkt_switch(); 165 } while ((msg->ms_flags & MSGF_DONE) == 0); 166 port->mp_flags &= ~MSGPORTF_WAITING; 167 } 168 /* 169 * We own the message now. 170 */ 171 if (msg->ms_flags & MSGF_QUEUED) { 172 msg->ms_flags &= ~MSGF_QUEUED; 173 TAILQ_REMOVE(&port->mp_msgq, msg, ms_node); 174 } 175 crit_exit(); 176 return(msg->ms_error); 177 } 178 179 180 /************************************************************************ 181 * PORT FUNCTIONS * 182 ************************************************************************/ 183 184 /* 185 * lwkt_initport() 186 * 187 * Initialize a port for use and assign it to the specified thread. 188 */ 189 void 190 lwkt_init_port(lwkt_port_t port, thread_t td) 191 { 192 bzero(port, sizeof(*port)); 193 TAILQ_INIT(&port->mp_msgq); 194 port->mp_td = td; 195 port->mp_beginmsg = lwkt_putport; 196 port->mp_abortmsg = lwkt_abortport; 197 port->mp_returnmsg = lwkt_replyport; 198 } 199 200 /* 201 * lwkt_replyport() 202 * 203 * This function is typically assigned to the mp_replymsg port vector. 204 * 205 * The message is being returned to the specified port. The port is 206 * owned by the mp_td thread. If we are on the same cpu as the mp_td 207 * thread we can trivially queue the message to the messageq, otherwise 208 * we have to send an ipi message to the correct cpu. We then schedule 209 * the target thread. 210 * 211 * If MSGF_ASYNC is not set we do not bother queueing the message, we 212 * just set the DONE bit. 213 * 214 * Note that the IPIQ callback function (*_remote) is entered with a 215 * critical section already held. 216 */ 217 218 static __inline 219 void 220 _lwkt_replyport(lwkt_port_t port, lwkt_msg_t msg) 221 { 222 thread_t td = port->mp_td; 223 224 if (td->td_gd == mycpu) { 225 TAILQ_INSERT_TAIL(&port->mp_msgq, msg, ms_node); 226 msg->ms_flags |= MSGF_DONE | MSGF_REPLY | MSGF_QUEUED; 227 if (port->mp_flags & MSGPORTF_WAITING) 228 lwkt_schedule(td); 229 } else { 230 lwkt_send_ipiq(td->td_gd->gd_cpuid, (ipifunc_t)lwkt_replyport_remote, msg); 231 } 232 } 233 234 static 235 void 236 lwkt_replyport_remote(lwkt_msg_t msg) 237 { 238 _lwkt_replyport(msg->ms_reply_port, msg); 239 } 240 241 242 void 243 lwkt_replyport(lwkt_port_t port, lwkt_msg_t msg) 244 { 245 crit_enter(); 246 if (msg->ms_flags & MSGF_ASYNC) { 247 _lwkt_replyport(port, msg); 248 } else { 249 msg->ms_flags |= MSGF_DONE; 250 if (port->mp_flags & MSGPORTF_WAITING) 251 lwkt_schedule(port->mp_td); 252 } 253 crit_exit(); 254 } 255 256 /* 257 * lwkt_putport() 258 * 259 * This function is typically assigned to the mp_beginmsg port vector. 260 * 261 * Queue a message to the target port and wakeup the thread owning it. 262 * This function always returns EASYNC and may be assigned to a 263 * message port's mp_beginmsg function vector. 264 */ 265 static 266 __inline 267 void 268 _lwkt_putport(lwkt_port_t port, lwkt_msg_t msg) 269 { 270 thread_t td = port->mp_td; 271 272 if (td->td_gd == mycpu) { 273 TAILQ_INSERT_TAIL(&port->mp_msgq, msg, ms_node); 274 msg->ms_flags |= MSGF_QUEUED; 275 if (port->mp_flags & MSGPORTF_WAITING) 276 lwkt_schedule(td); 277 } else { 278 msg->ms_target_port = port; 279 lwkt_send_ipiq(td->td_gd->gd_cpuid, (ipifunc_t)lwkt_putport_remote, msg); 280 } 281 } 282 283 static 284 void 285 lwkt_putport_remote(lwkt_msg_t msg) 286 { 287 _lwkt_putport(msg->ms_target_port, msg); 288 } 289 290 int 291 lwkt_putport(lwkt_port_t port, lwkt_msg_t msg) 292 { 293 crit_enter(); 294 msg->ms_flags &= ~MSGF_DONE; 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