1 /* 2 * QEMU System Emulator 3 * 4 * Copyright (c) 2003-2008 Fabrice Bellard 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 #include "qemu/main-loop.h" 27 #include "qemu/timer.h" 28 #include "qemu/lockable.h" 29 #include "sysemu/cpu-timers.h" 30 #include "sysemu/replay.h" 31 #include "sysemu/cpus.h" 32 33 #ifdef CONFIG_POSIX 34 #include <pthread.h> 35 #endif 36 37 #ifdef CONFIG_PPOLL 38 #include <poll.h> 39 #endif 40 41 #ifdef CONFIG_PRCTL_PR_SET_TIMERSLACK 42 #include <sys/prctl.h> 43 #endif 44 45 /***********************************************************/ 46 /* timers */ 47 48 typedef struct QEMUClock { 49 /* We rely on BQL to protect the timerlists */ 50 QLIST_HEAD(, QEMUTimerList) timerlists; 51 52 QEMUClockType type; 53 bool enabled; 54 } QEMUClock; 55 56 QEMUTimerListGroup main_loop_tlg; 57 static QEMUClock qemu_clocks[QEMU_CLOCK_MAX]; 58 59 /* A QEMUTimerList is a list of timers attached to a clock. More 60 * than one QEMUTimerList can be attached to each clock, for instance 61 * used by different AioContexts / threads. Each clock also has 62 * a list of the QEMUTimerLists associated with it, in order that 63 * reenabling the clock can call all the notifiers. 64 */ 65 66 struct QEMUTimerList { 67 QEMUClock *clock; 68 QemuMutex active_timers_lock; 69 QEMUTimer *active_timers; 70 QLIST_ENTRY(QEMUTimerList) list; 71 QEMUTimerListNotifyCB *notify_cb; 72 void *notify_opaque; 73 74 /* lightweight method to mark the end of timerlist's running */ 75 QemuEvent timers_done_ev; 76 }; 77 78 /** 79 * qemu_clock_ptr: 80 * @type: type of clock 81 * 82 * Translate a clock type into a pointer to QEMUClock object. 83 * 84 * Returns: a pointer to the QEMUClock object 85 */ 86 static inline QEMUClock *qemu_clock_ptr(QEMUClockType type) 87 { 88 return &qemu_clocks[type]; 89 } 90 91 static bool timer_expired_ns(QEMUTimer *timer_head, int64_t current_time) 92 { 93 return timer_head && (timer_head->expire_time <= current_time); 94 } 95 96 QEMUTimerList *timerlist_new(QEMUClockType type, 97 QEMUTimerListNotifyCB *cb, 98 void *opaque) 99 { 100 QEMUTimerList *timer_list; 101 QEMUClock *clock = qemu_clock_ptr(type); 102 103 timer_list = g_new0(QEMUTimerList, 1); 104 qemu_event_init(&timer_list->timers_done_ev, true); 105 timer_list->clock = clock; 106 timer_list->notify_cb = cb; 107 timer_list->notify_opaque = opaque; 108 qemu_mutex_init(&timer_list->active_timers_lock); 109 QLIST_INSERT_HEAD(&clock->timerlists, timer_list, list); 110 return timer_list; 111 } 112 113 void timerlist_free(QEMUTimerList *timer_list) 114 { 115 assert(!timerlist_has_timers(timer_list)); 116 if (timer_list->clock) { 117 QLIST_REMOVE(timer_list, list); 118 } 119 qemu_mutex_destroy(&timer_list->active_timers_lock); 120 g_free(timer_list); 121 } 122 123 static void qemu_clock_init(QEMUClockType type, QEMUTimerListNotifyCB *notify_cb) 124 { 125 QEMUClock *clock = qemu_clock_ptr(type); 126 127 /* Assert that the clock of type TYPE has not been initialized yet. */ 128 assert(main_loop_tlg.tl[type] == NULL); 129 130 clock->type = type; 131 clock->enabled = (type == QEMU_CLOCK_VIRTUAL ? false : true); 132 QLIST_INIT(&clock->timerlists); 133 main_loop_tlg.tl[type] = timerlist_new(type, notify_cb, NULL); 134 } 135 136 bool qemu_clock_use_for_deadline(QEMUClockType type) 137 { 138 return !(icount_enabled() && (type == QEMU_CLOCK_VIRTUAL)); 139 } 140 141 void qemu_clock_notify(QEMUClockType type) 142 { 143 QEMUTimerList *timer_list; 144 QEMUClock *clock = qemu_clock_ptr(type); 145 QLIST_FOREACH(timer_list, &clock->timerlists, list) { 146 timerlist_notify(timer_list); 147 } 148 } 149 150 /* Disabling the clock will wait for related timerlists to stop 151 * executing qemu_run_timers. Thus, this functions should not 152 * be used from the callback of a timer that is based on @clock. 153 * Doing so would cause a deadlock. 154 * 155 * Caller should hold BQL. 156 */ 157 void qemu_clock_enable(QEMUClockType type, bool enabled) 158 { 159 QEMUClock *clock = qemu_clock_ptr(type); 160 QEMUTimerList *tl; 161 bool old = clock->enabled; 162 clock->enabled = enabled; 163 if (enabled && !old) { 164 qemu_clock_notify(type); 165 } else if (!enabled && old) { 166 QLIST_FOREACH(tl, &clock->timerlists, list) { 167 qemu_event_wait(&tl->timers_done_ev); 168 } 169 } 170 } 171 172 bool timerlist_has_timers(QEMUTimerList *timer_list) 173 { 174 return !!qatomic_read(&timer_list->active_timers); 175 } 176 177 bool qemu_clock_has_timers(QEMUClockType type) 178 { 179 return timerlist_has_timers( 180 main_loop_tlg.tl[type]); 181 } 182 183 bool timerlist_expired(QEMUTimerList *timer_list) 184 { 185 int64_t expire_time; 186 187 if (!qatomic_read(&timer_list->active_timers)) { 188 return false; 189 } 190 191 WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { 192 if (!timer_list->active_timers) { 193 return false; 194 } 195 expire_time = timer_list->active_timers->expire_time; 196 } 197 198 return expire_time <= qemu_clock_get_ns(timer_list->clock->type); 199 } 200 201 bool qemu_clock_expired(QEMUClockType type) 202 { 203 return timerlist_expired( 204 main_loop_tlg.tl[type]); 205 } 206 207 /* 208 * As above, but return -1 for no deadline, and do not cap to 2^32 209 * as we know the result is always positive. 210 */ 211 212 int64_t timerlist_deadline_ns(QEMUTimerList *timer_list) 213 { 214 int64_t delta; 215 int64_t expire_time; 216 217 if (!qatomic_read(&timer_list->active_timers)) { 218 return -1; 219 } 220 221 if (!timer_list->clock->enabled) { 222 return -1; 223 } 224 225 /* The active timers list may be modified before the caller uses our return 226 * value but ->notify_cb() is called when the deadline changes. Therefore 227 * the caller should notice the change and there is no race condition. 228 */ 229 WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { 230 if (!timer_list->active_timers) { 231 return -1; 232 } 233 expire_time = timer_list->active_timers->expire_time; 234 } 235 236 delta = expire_time - qemu_clock_get_ns(timer_list->clock->type); 237 238 if (delta <= 0) { 239 return 0; 240 } 241 242 return delta; 243 } 244 245 /* Calculate the soonest deadline across all timerlists attached 246 * to the clock. This is used for the icount timeout so we 247 * ignore whether or not the clock should be used in deadline 248 * calculations. 249 */ 250 int64_t qemu_clock_deadline_ns_all(QEMUClockType type, int attr_mask) 251 { 252 int64_t deadline = -1; 253 int64_t delta; 254 int64_t expire_time; 255 QEMUTimer *ts; 256 QEMUTimerList *timer_list; 257 QEMUClock *clock = qemu_clock_ptr(type); 258 259 if (!clock->enabled) { 260 return -1; 261 } 262 263 QLIST_FOREACH(timer_list, &clock->timerlists, list) { 264 if (!qatomic_read(&timer_list->active_timers)) { 265 continue; 266 } 267 qemu_mutex_lock(&timer_list->active_timers_lock); 268 ts = timer_list->active_timers; 269 /* Skip all external timers */ 270 while (ts && (ts->attributes & ~attr_mask)) { 271 ts = ts->next; 272 } 273 if (!ts) { 274 qemu_mutex_unlock(&timer_list->active_timers_lock); 275 continue; 276 } 277 expire_time = ts->expire_time; 278 qemu_mutex_unlock(&timer_list->active_timers_lock); 279 280 delta = expire_time - qemu_clock_get_ns(type); 281 if (delta <= 0) { 282 delta = 0; 283 } 284 deadline = qemu_soonest_timeout(deadline, delta); 285 } 286 return deadline; 287 } 288 289 QEMUClockType timerlist_get_clock(QEMUTimerList *timer_list) 290 { 291 return timer_list->clock->type; 292 } 293 294 QEMUTimerList *qemu_clock_get_main_loop_timerlist(QEMUClockType type) 295 { 296 return main_loop_tlg.tl[type]; 297 } 298 299 void timerlist_notify(QEMUTimerList *timer_list) 300 { 301 if (timer_list->notify_cb) { 302 timer_list->notify_cb(timer_list->notify_opaque, timer_list->clock->type); 303 } else { 304 qemu_notify_event(); 305 } 306 } 307 308 /* Transition function to convert a nanosecond timeout to ms 309 * This is used where a system does not support ppoll 310 */ 311 int qemu_timeout_ns_to_ms(int64_t ns) 312 { 313 int64_t ms; 314 if (ns < 0) { 315 return -1; 316 } 317 318 if (!ns) { 319 return 0; 320 } 321 322 /* Always round up, because it's better to wait too long than to wait too 323 * little and effectively busy-wait 324 */ 325 ms = DIV_ROUND_UP(ns, SCALE_MS); 326 327 /* To avoid overflow problems, limit this to 2^31, i.e. approx 25 days */ 328 return MIN(ms, INT32_MAX); 329 } 330 331 332 /* qemu implementation of g_poll which uses a nanosecond timeout but is 333 * otherwise identical to g_poll 334 */ 335 int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout) 336 { 337 #ifdef CONFIG_PPOLL 338 if (timeout < 0) { 339 return ppoll((struct pollfd *)fds, nfds, NULL, NULL); 340 } else { 341 struct timespec ts; 342 int64_t tvsec = timeout / 1000000000LL; 343 /* Avoid possibly overflowing and specifying a negative number of 344 * seconds, which would turn a very long timeout into a busy-wait. 345 */ 346 if (tvsec > (int64_t)INT32_MAX) { 347 tvsec = INT32_MAX; 348 } 349 ts.tv_sec = tvsec; 350 ts.tv_nsec = timeout % 1000000000LL; 351 return ppoll((struct pollfd *)fds, nfds, &ts, NULL); 352 } 353 #else 354 return g_poll(fds, nfds, qemu_timeout_ns_to_ms(timeout)); 355 #endif 356 } 357 358 359 void timer_init_full(QEMUTimer *ts, 360 QEMUTimerListGroup *timer_list_group, QEMUClockType type, 361 int scale, int attributes, 362 QEMUTimerCB *cb, void *opaque) 363 { 364 if (!timer_list_group) { 365 timer_list_group = &main_loop_tlg; 366 } 367 ts->timer_list = timer_list_group->tl[type]; 368 ts->cb = cb; 369 ts->opaque = opaque; 370 ts->scale = scale; 371 ts->attributes = attributes; 372 ts->expire_time = -1; 373 } 374 375 void timer_deinit(QEMUTimer *ts) 376 { 377 assert(ts->expire_time == -1); 378 ts->timer_list = NULL; 379 } 380 381 static void timer_del_locked(QEMUTimerList *timer_list, QEMUTimer *ts) 382 { 383 QEMUTimer **pt, *t; 384 385 ts->expire_time = -1; 386 pt = &timer_list->active_timers; 387 for(;;) { 388 t = *pt; 389 if (!t) 390 break; 391 if (t == ts) { 392 qatomic_set(pt, t->next); 393 break; 394 } 395 pt = &t->next; 396 } 397 } 398 399 static bool timer_mod_ns_locked(QEMUTimerList *timer_list, 400 QEMUTimer *ts, int64_t expire_time) 401 { 402 QEMUTimer **pt, *t; 403 404 /* add the timer in the sorted list */ 405 pt = &timer_list->active_timers; 406 for (;;) { 407 t = *pt; 408 if (!timer_expired_ns(t, expire_time)) { 409 break; 410 } 411 pt = &t->next; 412 } 413 ts->expire_time = MAX(expire_time, 0); 414 ts->next = *pt; 415 qatomic_set(pt, ts); 416 417 return pt == &timer_list->active_timers; 418 } 419 420 static void timerlist_rearm(QEMUTimerList *timer_list) 421 { 422 /* Interrupt execution to force deadline recalculation. */ 423 if (icount_enabled() && timer_list->clock->type == QEMU_CLOCK_VIRTUAL) { 424 icount_start_warp_timer(); 425 } 426 timerlist_notify(timer_list); 427 } 428 429 /* stop a timer, but do not dealloc it */ 430 void timer_del(QEMUTimer *ts) 431 { 432 QEMUTimerList *timer_list = ts->timer_list; 433 434 if (timer_list) { 435 qemu_mutex_lock(&timer_list->active_timers_lock); 436 timer_del_locked(timer_list, ts); 437 qemu_mutex_unlock(&timer_list->active_timers_lock); 438 } 439 } 440 441 /* modify the current timer so that it will be fired when current_time 442 >= expire_time. The corresponding callback will be called. */ 443 void timer_mod_ns(QEMUTimer *ts, int64_t expire_time) 444 { 445 QEMUTimerList *timer_list = ts->timer_list; 446 bool rearm; 447 448 qemu_mutex_lock(&timer_list->active_timers_lock); 449 timer_del_locked(timer_list, ts); 450 rearm = timer_mod_ns_locked(timer_list, ts, expire_time); 451 qemu_mutex_unlock(&timer_list->active_timers_lock); 452 453 if (rearm) { 454 timerlist_rearm(timer_list); 455 } 456 } 457 458 /* modify the current timer so that it will be fired when current_time 459 >= expire_time or the current deadline, whichever comes earlier. 460 The corresponding callback will be called. */ 461 void timer_mod_anticipate_ns(QEMUTimer *ts, int64_t expire_time) 462 { 463 QEMUTimerList *timer_list = ts->timer_list; 464 bool rearm; 465 466 WITH_QEMU_LOCK_GUARD(&timer_list->active_timers_lock) { 467 if (ts->expire_time == -1 || ts->expire_time > expire_time) { 468 if (ts->expire_time != -1) { 469 timer_del_locked(timer_list, ts); 470 } 471 rearm = timer_mod_ns_locked(timer_list, ts, expire_time); 472 } else { 473 rearm = false; 474 } 475 } 476 if (rearm) { 477 timerlist_rearm(timer_list); 478 } 479 } 480 481 void timer_mod(QEMUTimer *ts, int64_t expire_time) 482 { 483 timer_mod_ns(ts, expire_time * ts->scale); 484 } 485 486 void timer_mod_anticipate(QEMUTimer *ts, int64_t expire_time) 487 { 488 timer_mod_anticipate_ns(ts, expire_time * ts->scale); 489 } 490 491 bool timer_pending(QEMUTimer *ts) 492 { 493 return ts->expire_time >= 0; 494 } 495 496 bool timer_expired(QEMUTimer *timer_head, int64_t current_time) 497 { 498 return timer_expired_ns(timer_head, current_time * timer_head->scale); 499 } 500 501 bool timerlist_run_timers(QEMUTimerList *timer_list) 502 { 503 QEMUTimer *ts; 504 int64_t current_time; 505 bool progress = false; 506 QEMUTimerCB *cb; 507 void *opaque; 508 509 if (!qatomic_read(&timer_list->active_timers)) { 510 return false; 511 } 512 513 qemu_event_reset(&timer_list->timers_done_ev); 514 if (!timer_list->clock->enabled) { 515 goto out; 516 } 517 518 switch (timer_list->clock->type) { 519 case QEMU_CLOCK_REALTIME: 520 break; 521 default: 522 case QEMU_CLOCK_VIRTUAL: 523 break; 524 case QEMU_CLOCK_HOST: 525 if (!replay_checkpoint(CHECKPOINT_CLOCK_HOST)) { 526 goto out; 527 } 528 break; 529 case QEMU_CLOCK_VIRTUAL_RT: 530 if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL_RT)) { 531 goto out; 532 } 533 break; 534 } 535 536 /* 537 * Extract expired timers from active timers list and process them. 538 * 539 * In rr mode we need "filtered" checkpointing for virtual clock. The 540 * checkpoint must be recorded/replayed before processing any non-EXTERNAL timer, 541 * and that must only be done once since the clock value stays the same. Because 542 * non-EXTERNAL timers may appear in the timers list while it being processed, 543 * the checkpoint can be issued at a time until no timers are left and we are 544 * done". 545 */ 546 current_time = qemu_clock_get_ns(timer_list->clock->type); 547 qemu_mutex_lock(&timer_list->active_timers_lock); 548 while ((ts = timer_list->active_timers)) { 549 if (!timer_expired_ns(ts, current_time)) { 550 /* No expired timers left. The checkpoint can be skipped 551 * if no timers fired or they were all external. 552 */ 553 break; 554 } 555 /* Checkpoint for virtual clock is redundant in cases where 556 * it's being triggered with only non-EXTERNAL timers, because 557 * these timers don't change guest state directly. 558 */ 559 if (replay_mode != REPLAY_MODE_NONE 560 && timer_list->clock->type == QEMU_CLOCK_VIRTUAL 561 && !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL) 562 && !replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) { 563 qemu_mutex_unlock(&timer_list->active_timers_lock); 564 goto out; 565 } 566 567 /* remove timer from the list before calling the callback */ 568 timer_list->active_timers = ts->next; 569 ts->next = NULL; 570 ts->expire_time = -1; 571 cb = ts->cb; 572 opaque = ts->opaque; 573 574 /* run the callback (the timer list can be modified) */ 575 qemu_mutex_unlock(&timer_list->active_timers_lock); 576 cb(opaque); 577 qemu_mutex_lock(&timer_list->active_timers_lock); 578 579 progress = true; 580 } 581 qemu_mutex_unlock(&timer_list->active_timers_lock); 582 583 out: 584 qemu_event_set(&timer_list->timers_done_ev); 585 return progress; 586 } 587 588 bool qemu_clock_run_timers(QEMUClockType type) 589 { 590 return timerlist_run_timers(main_loop_tlg.tl[type]); 591 } 592 593 void timerlistgroup_init(QEMUTimerListGroup *tlg, 594 QEMUTimerListNotifyCB *cb, void *opaque) 595 { 596 QEMUClockType type; 597 for (type = 0; type < QEMU_CLOCK_MAX; type++) { 598 tlg->tl[type] = timerlist_new(type, cb, opaque); 599 } 600 } 601 602 void timerlistgroup_deinit(QEMUTimerListGroup *tlg) 603 { 604 QEMUClockType type; 605 for (type = 0; type < QEMU_CLOCK_MAX; type++) { 606 timerlist_free(tlg->tl[type]); 607 } 608 } 609 610 bool timerlistgroup_run_timers(QEMUTimerListGroup *tlg) 611 { 612 QEMUClockType type; 613 bool progress = false; 614 for (type = 0; type < QEMU_CLOCK_MAX; type++) { 615 progress |= timerlist_run_timers(tlg->tl[type]); 616 } 617 return progress; 618 } 619 620 int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg) 621 { 622 int64_t deadline = -1; 623 QEMUClockType type; 624 for (type = 0; type < QEMU_CLOCK_MAX; type++) { 625 if (qemu_clock_use_for_deadline(type)) { 626 deadline = qemu_soonest_timeout(deadline, 627 timerlist_deadline_ns(tlg->tl[type])); 628 } 629 } 630 return deadline; 631 } 632 633 int64_t qemu_clock_get_ns(QEMUClockType type) 634 { 635 switch (type) { 636 case QEMU_CLOCK_REALTIME: 637 return get_clock(); 638 default: 639 case QEMU_CLOCK_VIRTUAL: 640 return cpus_get_virtual_clock(); 641 case QEMU_CLOCK_HOST: 642 return REPLAY_CLOCK(REPLAY_CLOCK_HOST, get_clock_realtime()); 643 case QEMU_CLOCK_VIRTUAL_RT: 644 return REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, cpu_get_clock()); 645 } 646 } 647 648 static void qemu_virtual_clock_set_ns(int64_t time) 649 { 650 return cpus_set_virtual_clock(time); 651 } 652 653 void init_clocks(QEMUTimerListNotifyCB *notify_cb) 654 { 655 QEMUClockType type; 656 for (type = 0; type < QEMU_CLOCK_MAX; type++) { 657 qemu_clock_init(type, notify_cb); 658 } 659 660 #ifdef CONFIG_PRCTL_PR_SET_TIMERSLACK 661 prctl(PR_SET_TIMERSLACK, 1, 0, 0, 0); 662 #endif 663 } 664 665 uint64_t timer_expire_time_ns(QEMUTimer *ts) 666 { 667 return timer_pending(ts) ? ts->expire_time : -1; 668 } 669 670 bool qemu_clock_run_all_timers(void) 671 { 672 bool progress = false; 673 QEMUClockType type; 674 675 for (type = 0; type < QEMU_CLOCK_MAX; type++) { 676 if (qemu_clock_use_for_deadline(type)) { 677 progress |= qemu_clock_run_timers(type); 678 } 679 } 680 681 return progress; 682 } 683 684 int64_t qemu_clock_advance_virtual_time(int64_t dest) 685 { 686 int64_t clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 687 AioContext *aio_context; 688 int64_t deadline; 689 690 aio_context = qemu_get_aio_context(); 691 692 deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, 693 QEMU_TIMER_ATTR_ALL); 694 /* 695 * A deadline of < 0 indicates this timer is not enabled, so we 696 * won't get far trying to run it forward. 697 */ 698 while (deadline >= 0 && clock < dest) { 699 int64_t warp = qemu_soonest_timeout(dest - clock, deadline); 700 701 qemu_virtual_clock_set_ns(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + warp); 702 703 qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); 704 timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); 705 clock = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 706 707 deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, 708 QEMU_TIMER_ATTR_ALL); 709 } 710 qemu_clock_notify(QEMU_CLOCK_VIRTUAL); 711 712 return clock; 713 } 714