On 04/05/21(Tue) 01:10, Scott Cheloha wrote:
> [...] 
> I want to run softclock() without the kernel lock.  The way to go, I
> think, is to first push the kernel lock down into timeout_run(), and
> then to remove the kernel lock from each timeout, one by one.

Grabbing and releasing the KERNEL_LOCk() on a per-timeout basis creates
more latency than running all timeouts in a batch after having grabbed
the KERNEL_LOCK().  I doubt this is the best way forward.

> Before we can push the kernel lock down into timeout_run() we need to
> remove the kernel lock from timeout_del_barrier(9).

Seems worth it on its own.

> The kernel lock is used in timeout_del_barrier(9) to determine whether
> the given timeout has stopped running.  Because the softclock() runs
> with the kernel lock we currently assume that once the calling thread
> has taken the kernel lock any onging softclock() must have returned
> and relinquished the lock, so the timeout in question has returned.

So you want to stop using the KERNEL_LOCK() to do the serialization?  
 
> The simplest replacement I can think of is a volatile pointer to the
> running timeout that we set before leaving the timeout_mutex and clear
> after reentering the same during timeout_run().

Sounds like a condition variable protected by this mutex.  Interesting
that cond_wait(9) doesn't work with a mutex. 

> So in the non-TIMEOUT_PROC case the timeout_del_barrier(9) caller just
> spins until the timeout function returns and the timeout_running
> pointer is changed.  Not every caller can sleep during
> timeout_del_barrier(9).  I think spinning is the simplest thing that
> will definitely work here.

This keeps the current semantic indeed.

> -void
> -timeout_barrier(struct timeout *to)
> +int
> +timeout_del_barrier(struct timeout *to)
>  {
> +     struct timeout barrier;
> +     struct cond c = COND_INITIALIZER();
>       int needsproc = ISSET(to->to_flags, TIMEOUT_PROC);
>  
>       timeout_sync_order(needsproc);
>  
> -     if (!needsproc) {
> -             KERNEL_LOCK();
> -             splx(splsoftclock());
> -             KERNEL_UNLOCK();
> -     } else {
> -             struct cond c = COND_INITIALIZER();
> -             struct timeout barrier;
> +     mtx_enter(&timeout_mutex);
> +
> +     if (timeout_del_locked(to)) {
> +             mtx_leave(&timeout_mutex);
> +             return 1;
> +     }
>  
> +     if (needsproc) {
>               timeout_set_proc(&barrier, timeout_proc_barrier, &c);
>               barrier.to_process = curproc->p_p;
> -
> -             mtx_enter(&timeout_mutex);
>               SET(barrier.to_flags, TIMEOUT_ONQUEUE);
>               CIRCQ_INSERT_TAIL(&timeout_proc, &barrier.to_list);
>               mtx_leave(&timeout_mutex);
> -
>               wakeup_one(&timeout_proc);
> -
>               cond_wait(&c, "tmobar");
> +     } else {
> +             mtx_leave(&timeout_mutex);
> +             /* XXX Is this in the right spot? */
> +             splx(splsoftclock());
> +             while (timeout_running == to)
> +                     CPU_BUSY_CYCLE();

Won't splx() will execute the soft-interrupt if there's any pending?
Shouldn't the barrier be before?  Could you add `spc->spc_spinning++'
around the spinning loop?  What happen if two threads call
timeout_del_barrier(9) with the same argument at the same time?  Is
it possible and/or supported?

Reply via email to