Changeset View
Standalone View
sys/kern/kern_umtx.c
| Show First 20 Lines • Show All 169 Lines • ▼ Show 20 Lines | |||||
| static long max_length; | static long max_length; | ||||
| SYSCTL_LONG(_debug_umtx, OID_AUTO, max_length, CTLFLAG_RD, &max_length, 0, "max_length"); | SYSCTL_LONG(_debug_umtx, OID_AUTO, max_length, CTLFLAG_RD, &max_length, 0, "max_length"); | ||||
| static SYSCTL_NODE(_debug_umtx, OID_AUTO, chains, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, | static SYSCTL_NODE(_debug_umtx, OID_AUTO, chains, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, | ||||
| "umtx chain stats"); | "umtx chain stats"); | ||||
| #endif | #endif | ||||
| static inline void umtx_abs_timeout_init2(struct umtx_abs_timeout *timo, | static inline void umtx_abs_timeout_init2(struct umtx_abs_timeout *timo, | ||||
| const struct _umtx_time *umtxtime); | const struct _umtx_time *umtxtime); | ||||
| static int umtx_abs_timeout_gethz(struct umtx_abs_timeout *timo); | |||||
| static inline void umtx_abs_timeout_update(struct umtx_abs_timeout *timo); | |||||
| static void umtx_shm_init(void); | static void umtx_shm_init(void); | ||||
| static void umtxq_sysinit(void *); | static void umtxq_sysinit(void *); | ||||
| static void umtxq_hash(struct umtx_key *key); | static void umtxq_hash(struct umtx_key *key); | ||||
| static int do_unlock_pp(struct thread *td, struct umutex *m, uint32_t flags, | static int do_unlock_pp(struct thread *td, struct umutex *m, uint32_t flags, | ||||
| bool rb); | bool rb); | ||||
| static void umtx_thread_cleanup(struct thread *td); | static void umtx_thread_cleanup(struct thread *td); | ||||
| SYSINIT(umtx, SI_SUB_EVENTHANDLER+1, SI_ORDER_MIDDLE, umtxq_sysinit, NULL); | SYSINIT(umtx, SI_SUB_EVENTHANDLER+1, SI_ORDER_MIDDLE, umtxq_sysinit, NULL); | ||||
| ▲ Show 20 Lines • Show All 490 Lines • ▼ Show 20 Lines | |||||
| void | void | ||||
| umtx_abs_timeout_init(struct umtx_abs_timeout *timo, int clockid, | umtx_abs_timeout_init(struct umtx_abs_timeout *timo, int clockid, | ||||
| int absolute, const struct timespec *timeout) | int absolute, const struct timespec *timeout) | ||||
| { | { | ||||
| timo->clockid = clockid; | timo->clockid = clockid; | ||||
| if (!absolute) { | if (!absolute) { | ||||
| timo->is_abs_real = false; | timo->is_abs_real = false; | ||||
| umtx_abs_timeout_update(timo); | kern_clock_gettime(curthread, timo->clockid, &timo->cur); | ||||
| timespecadd(&timo->cur, timeout, &timo->end); | timespecadd(&timo->cur, timeout, &timo->end); | ||||
| } else { | } else { | ||||
| timo->end = *timeout; | timo->end = *timeout; | ||||
| timo->is_abs_real = clockid == CLOCK_REALTIME || | timo->is_abs_real = clockid == CLOCK_REALTIME || | ||||
| clockid == CLOCK_REALTIME_FAST || | clockid == CLOCK_REALTIME_FAST || | ||||
| clockid == CLOCK_REALTIME_PRECISE; | clockid == CLOCK_REALTIME_PRECISE || | ||||
| /* | clockid == CLOCK_SECOND; | ||||
| * If is_abs_real, umtxq_sleep will read the clock | |||||
| * after setting td_rtcgen; otherwise, read it here. | |||||
| */ | |||||
| if (!timo->is_abs_real) { | |||||
| umtx_abs_timeout_update(timo); | |||||
| } | } | ||||
| } | } | ||||
| } | |||||
| static void | static void | ||||
| umtx_abs_timeout_init2(struct umtx_abs_timeout *timo, | umtx_abs_timeout_init2(struct umtx_abs_timeout *timo, | ||||
| const struct _umtx_time *umtxtime) | const struct _umtx_time *umtxtime) | ||||
| { | { | ||||
| umtx_abs_timeout_init(timo, umtxtime->_clockid, | umtx_abs_timeout_init(timo, umtxtime->_clockid, | ||||
| (umtxtime->_flags & UMTX_ABSTIME) != 0, &umtxtime->_timeout); | (umtxtime->_flags & UMTX_ABSTIME) != 0, &umtxtime->_timeout); | ||||
| } | } | ||||
| static void | |||||
| umtx_abs_timeout_update(struct umtx_abs_timeout *timo) | |||||
| { | |||||
| kern_clock_gettime(curthread, timo->clockid, &timo->cur); | |||||
| } | |||||
| static int | static int | ||||
| umtx_abs_timeout_gethz(struct umtx_abs_timeout *timo) | umtx_abs_timeout_getsbt(struct umtx_abs_timeout *timo, sbintime_t *sbt, | ||||
| int *flags) | |||||
| { | { | ||||
| struct bintime bt, bbt; | |||||
| struct timespec tts; | struct timespec tts; | ||||
| switch (timo->clockid) { | |||||
| /* Clocks that can be converted into absolute time. */ | |||||
| case CLOCK_REALTIME: | |||||
| case CLOCK_REALTIME_PRECISE: | |||||
| case CLOCK_REALTIME_FAST: | |||||
| case CLOCK_MONOTONIC: | |||||
| case CLOCK_MONOTONIC_PRECISE: | |||||
| case CLOCK_MONOTONIC_FAST: | |||||
| case CLOCK_UPTIME: | |||||
| case CLOCK_UPTIME_PRECISE: | |||||
| case CLOCK_UPTIME_FAST: | |||||
| case CLOCK_SECOND: | |||||
| timespec2bintime(&timo->end, &bt); | |||||
kib: Can we have the explicit list there, instead of 'default'? | |||||
Done Inline ActionsI suppose just to increase visibility? Because as I was able to find here should appear only 4 different values (see _pthread_condattr_setclock()). Even those values I listed already is a bit overkill. mav: I suppose just to increase visibility? Because as I was able to find here should appear only 4… | |||||
Done Inline ActionsIt is it make life easier for anybody reading the code after you, and for code clarity. For instance, it is not clear to me without a lot of code reading which cases go to C_HARDCLOCK. I would expect that at least everything wall-clock related with _PRECISE would, but apparently you did not do it this way. kib: It is it make life easier for anybody reading the code after you, and for code clarity.
Ideally… | |||||
| switch (timo->clockid) { | |||||
| case CLOCK_REALTIME: | |||||
Done Inline ActionsYou initialize *flags in umtxq_sleep() anyway. I suggest to to *flags |= C_HARDCLOCK there instead (and remove *flags = 0 above), to make the interface somewhat more flexible. I think e.g. about using C_ABSOLUTE or C_PRECALC there, since right now we do too many conversions for is_abs_real case. kib: You initialize *flags in umtxq_sleep() anyway. I suggest to to `*flags |= C_HARDCLOCK` there… | |||||
Done Inline Actions
I don't feel like it would make it better. It would make result depending on previous calls, that while would change nothing is more dirty.
I thought about it briefly, but C_ABSOLUTE require having binuptime, while none of clocks use that. MONOTONIC and UPTIME are close, but require backward conversion from nanoseconds. REALTIME I guess would also require correction on getboottimebin()? In case of hardclock I haven't actually added any more conversions, just not removed any. If we assume that the only clocks of that kind that may be there are profiles, then relation to hardclock is probably not required (statclock may be even closer for multi-threaded processes) and I could possibly just limit minimal time instead, but unlike the other cases in case of profiles the code has no current binuptime at all, so it can pass only relative sbintime. mav: > You initialize *flags in umtxq_sleep() anyway. I suggest to to `*flags |= C_HARDCLOCK` there… | |||||
Done Inline Actions
There is already a mechanism to reschedule umtx sleeps on abs time change. This what umtx_abs_time_update() stuff is about, see 9dbdf2a16998ee49011f874b94754e2d046045f3.
I am not claiming that you added any new conversions, I am saying that we probably can remove some if this code is better structured. kib: > > You initialize *flags in umtxq_sleep() anyway. I suggest to to `*flags |= C_HARDCLOCK`… | |||||
| case CLOCK_REALTIME_PRECISE: | |||||
| case CLOCK_REALTIME_FAST: | |||||
| case CLOCK_SECOND: | |||||
| getboottimebin(&bbt); | |||||
| bintime_sub(&bt, &bbt); | |||||
| break; | |||||
| } | |||||
| if (bt.sec < 0) | |||||
| return (ETIMEDOUT); | |||||
| if (bt.sec >= (SBT_MAX >> 32)) { | |||||
| *sbt = 0; | |||||
| *flags = 0; | |||||
| return (0); | |||||
| } | |||||
| *sbt = bttosbt(bt); | |||||
| switch (timo->clockid) { | |||||
| case CLOCK_REALTIME_FAST: | |||||
| case CLOCK_MONOTONIC_FAST: | |||||
| case CLOCK_UPTIME_FAST: | |||||
| *sbt += tc_tick_sbt; | |||||
| break; | |||||
| case CLOCK_SECOND: | |||||
| *sbt += SBT_1S; | |||||
| break; | |||||
| } | |||||
| *flags = C_ABSOLUTE; | |||||
| return (0); | |||||
| /* Clocks that has to be periodically polled. */ | |||||
| case CLOCK_VIRTUAL: | |||||
| case CLOCK_PROF: | |||||
| case CLOCK_THREAD_CPUTIME_ID: | |||||
| case CLOCK_PROCESS_CPUTIME_ID: | |||||
| default: | |||||
| kern_clock_gettime(curthread, timo->clockid, &timo->cur); | |||||
| if (timespeccmp(&timo->end, &timo->cur, <=)) | if (timespeccmp(&timo->end, &timo->cur, <=)) | ||||
| return (-1); | return (ETIMEDOUT); | ||||
| timespecsub(&timo->end, &timo->cur, &tts); | timespecsub(&timo->end, &timo->cur, &tts); | ||||
| return (tstohz(&tts)); | *sbt = tick_sbt * tstohz(&tts); | ||||
| *flags = C_HARDCLOCK; | |||||
| return (0); | |||||
| } | } | ||||
| } | |||||
| static uint32_t | static uint32_t | ||||
| umtx_unlock_val(uint32_t flags, bool rb) | umtx_unlock_val(uint32_t flags, bool rb) | ||||
| { | { | ||||
| if (rb) | if (rb) | ||||
| return (UMUTEX_RB_OWNERDEAD); | return (UMUTEX_RB_OWNERDEAD); | ||||
| else if ((flags & UMUTEX_NONCONSISTENT) != 0) | else if ((flags & UMUTEX_NONCONSISTENT) != 0) | ||||
| return (UMUTEX_RB_NOTRECOV); | return (UMUTEX_RB_NOTRECOV); | ||||
| else | else | ||||
| return (UMUTEX_UNOWNED); | return (UMUTEX_UNOWNED); | ||||
| } | } | ||||
| /* | /* | ||||
| * Put thread into sleep state, before sleeping, check if | * Put thread into sleep state, before sleeping, check if | ||||
| * thread was removed from umtx queue. | * thread was removed from umtx queue. | ||||
| */ | */ | ||||
| int | int | ||||
| umtxq_sleep(struct umtx_q *uq, const char *wmesg, | umtxq_sleep(struct umtx_q *uq, const char *wmesg, | ||||
| struct umtx_abs_timeout *abstime) | struct umtx_abs_timeout *timo) | ||||
| { | { | ||||
| struct umtxq_chain *uc; | struct umtxq_chain *uc; | ||||
| int error, timo; | sbintime_t sbt = 0; | ||||
| int error, flags = 0; | |||||
| if (abstime != NULL && abstime->is_abs_real) { | |||||
| curthread->td_rtcgen = atomic_load_acq_int(&rtc_generation); | |||||
| umtx_abs_timeout_update(abstime); | |||||
| } | |||||
| uc = umtxq_getchain(&uq->uq_key); | uc = umtxq_getchain(&uq->uq_key); | ||||
| UMTXQ_LOCKED_ASSERT(uc); | UMTXQ_LOCKED_ASSERT(uc); | ||||
| for (;;) { | for (;;) { | ||||
| if (!(uq->uq_flags & UQF_UMTXQ)) { | if (!(uq->uq_flags & UQF_UMTXQ)) { | ||||
| error = 0; | error = 0; | ||||
| break; | break; | ||||
| } | } | ||||
| if (abstime != NULL) { | if (timo != NULL) { | ||||
| timo = umtx_abs_timeout_gethz(abstime); | if (timo->is_abs_real) | ||||
| if (timo < 0) { | curthread->td_rtcgen = | ||||
| error = ETIMEDOUT; | atomic_load_acq_int(&rtc_generation); | ||||
| error = umtx_abs_timeout_getsbt(timo, &sbt, &flags); | |||||
| if (error != 0) | |||||
| break; | break; | ||||
| } | } | ||||
| } else | error = msleep_sbt(uq, &uc->uc_lock, PCATCH, wmesg, | ||||
| timo = 0; | sbt, 0, flags); | ||||
| error = msleep(uq, &uc->uc_lock, PCATCH | PDROP, wmesg, timo); | if (error == EINTR || error == ERESTART) | ||||
| if (error == EINTR || error == ERESTART) { | |||||
| umtxq_lock(&uq->uq_key); | |||||
| break; | break; | ||||
| if (error == EWOULDBLOCK && (flags & C_ABSOLUTE) != 0) { | |||||
| error = ETIMEDOUT; | |||||
| break; | |||||
| } | } | ||||
| if (abstime != NULL) { | |||||
| if (abstime->is_abs_real) | |||||
| curthread->td_rtcgen = | |||||
| atomic_load_acq_int(&rtc_generation); | |||||
| umtx_abs_timeout_update(abstime); | |||||
| } | } | ||||
| umtxq_lock(&uq->uq_key); | |||||
| } | |||||
| curthread->td_rtcgen = 0; | curthread->td_rtcgen = 0; | ||||
| return (error); | return (error); | ||||
| } | } | ||||
| /* | /* | ||||
| * Convert userspace address into unique logical address. | * Convert userspace address into unique logical address. | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 2,852 Lines • ▼ Show 20 Lines | if ((uq->uq_flags & UQF_UMTXQ) == 0) | ||||
| error = 0; | error = 0; | ||||
| else { | else { | ||||
| umtxq_remove(uq); | umtxq_remove(uq); | ||||
| if (timeout != NULL && (timeout->_flags & UMTX_ABSTIME) == 0) { | if (timeout != NULL && (timeout->_flags & UMTX_ABSTIME) == 0) { | ||||
| /* A relative timeout cannot be restarted. */ | /* A relative timeout cannot be restarted. */ | ||||
| if (error == ERESTART) | if (error == ERESTART) | ||||
| error = EINTR; | error = EINTR; | ||||
| if (error == EINTR) { | if (error == EINTR) { | ||||
| umtx_abs_timeout_update(&timo); | kern_clock_gettime(curthread, timo.clockid, | ||||
| &timo.cur); | |||||
| timespecsub(&timo.end, &timo.cur, | timespecsub(&timo.end, &timo.cur, | ||||
| &timeout->_timeout); | &timeout->_timeout); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| umtxq_unlock(&uq->uq_key); | umtxq_unlock(&uq->uq_key); | ||||
| umtx_key_release(&uq->uq_key); | umtx_key_release(&uq->uq_key); | ||||
| return (error); | return (error); | ||||
| ▲ Show 20 Lines • Show All 1,422 Lines • Show Last 20 Lines | |||||
Can we have the explicit list there, instead of 'default'?