Re: uvm_map_inentry diff for testing

2020-03-18 Thread Martin Pieuchot
On 18/03/20(Wed) 11:55, Mark Kettenis wrote:
> > Date: Wed, 18 Mar 2020 11:34:59 +0100
> > From: Martin Pieuchot 
> > 
> > On 17/03/20(Tue) 20:08, Mark Kettenis wrote:
> > > While playing with dt(4)/btrace(4) flamegraphs on a 32-core arm64
> > > machine, I noticed that the kernel was spending a lot of time (6.84%)
> > > in uvm_map_inentry().  This is caused by kernel lock contention.
> > > Pushing baack the kernel lock further into the slow path reduces the
> > > time to 0.05%.
> > > 
> > > Now mpi@ tried this before in sys/uvm/uvm_map.c rev 1.249.  That was
> > > backed out because it caused issues with golang.  So I had a look
> > > again and identified a potential issue.  When we "fix" the cached
> > > entry, we use the serial number that has been passed down to us.  But
> > > since that serial number was recorded before we grapped the vm_map
> > > lock, it may be stale.  Instead we should be using the current map
> > > serial number.
> > > 
> > > This makes me wonder whether having two serial numbers is a case of
> > > premature optimization.  But that is a different discussion.
> > > 
> > > I'd like to see this tested, especially by people running/building
> > > golang.  Does anybody remember specific details of the issue?  If it
> > > manifests itself, it should be pretty obvious as it results in a
> > > kernel printf and a killed process.
> > 
> > Sadly this isn't the bug we're looking for.  golang still segfaults
> > with this diff.
> 
> How do you reproduce the golang segfault?

$ cd /usr/ports/lang/go/ && make 

You'll see the build fail :)



Re: uvm_map_inentry diff for testing

2020-03-18 Thread Mark Kettenis
> Date: Wed, 18 Mar 2020 11:34:59 +0100
> From: Martin Pieuchot 
> 
> On 17/03/20(Tue) 20:08, Mark Kettenis wrote:
> > While playing with dt(4)/btrace(4) flamegraphs on a 32-core arm64
> > machine, I noticed that the kernel was spending a lot of time (6.84%)
> > in uvm_map_inentry().  This is caused by kernel lock contention.
> > Pushing baack the kernel lock further into the slow path reduces the
> > time to 0.05%.
> > 
> > Now mpi@ tried this before in sys/uvm/uvm_map.c rev 1.249.  That was
> > backed out because it caused issues with golang.  So I had a look
> > again and identified a potential issue.  When we "fix" the cached
> > entry, we use the serial number that has been passed down to us.  But
> > since that serial number was recorded before we grapped the vm_map
> > lock, it may be stale.  Instead we should be using the current map
> > serial number.
> > 
> > This makes me wonder whether having two serial numbers is a case of
> > premature optimization.  But that is a different discussion.
> > 
> > I'd like to see this tested, especially by people running/building
> > golang.  Does anybody remember specific details of the issue?  If it
> > manifests itself, it should be pretty obvious as it results in a
> > kernel printf and a killed process.
> 
> Sadly this isn't the bug we're looking for.  golang still segfaults
> with this diff.

How do you reproduce the golang segfault?

> That said it is still a bug, so please commit it without moving the
> KERNEL_LOCK(),  ok mpi@.
> 
> > Index: uvm/uvm_map.c
> > ===
> > RCS file: /cvs/src/sys/uvm/uvm_map.c,v
> > retrieving revision 1.263
> > diff -u -p -r1.263 uvm_map.c
> > --- uvm/uvm_map.c   4 Mar 2020 21:15:38 -   1.263
> > +++ uvm/uvm_map.c   17 Mar 2020 18:57:19 -
> > @@ -161,7 +161,7 @@ void uvm_map_addr_augment(struct 
> > vm_m
> >  int uvm_map_inentry_recheck(u_long, vaddr_t,
> >  struct p_inentry *);
> >  boolean_t   uvm_map_inentry_fix(struct proc *, struct p_inentry *,
> > -vaddr_t, int (*)(vm_map_entry_t), u_long);
> > +vaddr_t, int (*)(vm_map_entry_t));
> >  /*
> >   * Tree management functions.
> >   */
> > @@ -1848,10 +1848,11 @@ uvm_map_inentry_recheck(u_long serial, v
> >   */
> >  boolean_t
> >  uvm_map_inentry_fix(struct proc *p, struct p_inentry *ie, vaddr_t addr,
> > -int (*fn)(vm_map_entry_t), u_long serial)
> > +int (*fn)(vm_map_entry_t))
> >  {
> > vm_map_t map = >p_vmspace->vm_map;
> > vm_map_entry_t entry;
> > +   u_long serial;
> > int ret;
> >  
> > if (addr < map->min_offset || addr >= map->max_offset)
> > @@ -1866,6 +1867,11 @@ uvm_map_inentry_fix(struct proc *p, stru
> > return (FALSE);
> > }
> >  
> > +   if (ie == >p_spinentry)
> > +   serial = map->sserial;
> > +   else
> > +   serial = map->wserial;
> > +
> > ret = (*fn)(entry);
> > if (ret == 0) {
> > vm_map_unlock_read(map);
> > @@ -1889,16 +1895,16 @@ uvm_map_inentry(struct proc *p, struct p
> > boolean_t ok = TRUE;
> >  
> > if (uvm_map_inentry_recheck(serial, addr, ie)) {
> > -   KERNEL_LOCK();
> > -   ok = uvm_map_inentry_fix(p, ie, addr, fn, serial);
> > +   ok = uvm_map_inentry_fix(p, ie, addr, fn);
> > if (!ok) {
> > printf(fmt, p->p_p->ps_comm, p->p_p->ps_pid, p->p_tid,
> > addr, ie->ie_start, ie->ie_end);
> > +   KERNEL_LOCK();
> > p->p_p->ps_acflag |= AMAP;
> > sv.sival_ptr = (void *)PROC_PC(p);
> > trapsignal(p, SIGSEGV, 0, SEGV_ACCERR, sv);
> > +   KERNEL_UNLOCK();
> > }
> > -   KERNEL_UNLOCK();
> > }
> > return (ok);
> >  }
> > 
> 



Re: uvm_map_inentry diff for testing

2020-03-18 Thread Martin Pieuchot
On 17/03/20(Tue) 20:08, Mark Kettenis wrote:
> While playing with dt(4)/btrace(4) flamegraphs on a 32-core arm64
> machine, I noticed that the kernel was spending a lot of time (6.84%)
> in uvm_map_inentry().  This is caused by kernel lock contention.
> Pushing baack the kernel lock further into the slow path reduces the
> time to 0.05%.
> 
> Now mpi@ tried this before in sys/uvm/uvm_map.c rev 1.249.  That was
> backed out because it caused issues with golang.  So I had a look
> again and identified a potential issue.  When we "fix" the cached
> entry, we use the serial number that has been passed down to us.  But
> since that serial number was recorded before we grapped the vm_map
> lock, it may be stale.  Instead we should be using the current map
> serial number.
> 
> This makes me wonder whether having two serial numbers is a case of
> premature optimization.  But that is a different discussion.
> 
> I'd like to see this tested, especially by people running/building
> golang.  Does anybody remember specific details of the issue?  If it
> manifests itself, it should be pretty obvious as it results in a
> kernel printf and a killed process.

Sadly this isn't the bug we're looking for.  golang still segfaults
with this diff.

That said it is still a bug, so please commit it without moving the
KERNEL_LOCK(),  ok mpi@.

> Index: uvm/uvm_map.c
> ===
> RCS file: /cvs/src/sys/uvm/uvm_map.c,v
> retrieving revision 1.263
> diff -u -p -r1.263 uvm_map.c
> --- uvm/uvm_map.c 4 Mar 2020 21:15:38 -   1.263
> +++ uvm/uvm_map.c 17 Mar 2020 18:57:19 -
> @@ -161,7 +161,7 @@ void   uvm_map_addr_augment(struct 
> vm_m
>  int   uvm_map_inentry_recheck(u_long, vaddr_t,
>struct p_inentry *);
>  boolean_t uvm_map_inentry_fix(struct proc *, struct p_inentry *,
> -  vaddr_t, int (*)(vm_map_entry_t), u_long);
> +  vaddr_t, int (*)(vm_map_entry_t));
>  /*
>   * Tree management functions.
>   */
> @@ -1848,10 +1848,11 @@ uvm_map_inentry_recheck(u_long serial, v
>   */
>  boolean_t
>  uvm_map_inentry_fix(struct proc *p, struct p_inentry *ie, vaddr_t addr,
> -int (*fn)(vm_map_entry_t), u_long serial)
> +int (*fn)(vm_map_entry_t))
>  {
>   vm_map_t map = >p_vmspace->vm_map;
>   vm_map_entry_t entry;
> + u_long serial;
>   int ret;
>  
>   if (addr < map->min_offset || addr >= map->max_offset)
> @@ -1866,6 +1867,11 @@ uvm_map_inentry_fix(struct proc *p, stru
>   return (FALSE);
>   }
>  
> + if (ie == >p_spinentry)
> + serial = map->sserial;
> + else
> + serial = map->wserial;
> +
>   ret = (*fn)(entry);
>   if (ret == 0) {
>   vm_map_unlock_read(map);
> @@ -1889,16 +1895,16 @@ uvm_map_inentry(struct proc *p, struct p
>   boolean_t ok = TRUE;
>  
>   if (uvm_map_inentry_recheck(serial, addr, ie)) {
> - KERNEL_LOCK();
> - ok = uvm_map_inentry_fix(p, ie, addr, fn, serial);
> + ok = uvm_map_inentry_fix(p, ie, addr, fn);
>   if (!ok) {
>   printf(fmt, p->p_p->ps_comm, p->p_p->ps_pid, p->p_tid,
>   addr, ie->ie_start, ie->ie_end);
> + KERNEL_LOCK();
>   p->p_p->ps_acflag |= AMAP;
>   sv.sival_ptr = (void *)PROC_PC(p);
>   trapsignal(p, SIGSEGV, 0, SEGV_ACCERR, sv);
> + KERNEL_UNLOCK();
>   }
> - KERNEL_UNLOCK();
>   }
>   return (ok);
>  }
> 



uvm_map_inentry diff for testing

2020-03-17 Thread Mark Kettenis
While playing with dt(4)/btrace(4) flamegraphs on a 32-core arm64
machine, I noticed that the kernel was spending a lot of time (6.84%)
in uvm_map_inentry().  This is caused by kernel lock contention.
Pushing baack the kernel lock further into the slow path reduces the
time to 0.05%.

Now mpi@ tried this before in sys/uvm/uvm_map.c rev 1.249.  That was
backed out because it caused issues with golang.  So I had a look
again and identified a potential issue.  When we "fix" the cached
entry, we use the serial number that has been passed down to us.  But
since that serial number was recorded before we grapped the vm_map
lock, it may be stale.  Instead we should be using the current map
serial number.

This makes me wonder whether having two serial numbers is a case of
premature optimization.  But that is a different discussion.

I'd like to see this tested, especially by people running/building
golang.  Does anybody remember specific details of the issue?  If it
manifests itself, it should be pretty obvious as it results in a
kernel printf and a killed process.


Index: uvm/uvm_map.c
===
RCS file: /cvs/src/sys/uvm/uvm_map.c,v
retrieving revision 1.263
diff -u -p -r1.263 uvm_map.c
--- uvm/uvm_map.c   4 Mar 2020 21:15:38 -   1.263
+++ uvm/uvm_map.c   17 Mar 2020 18:57:19 -
@@ -161,7 +161,7 @@ void uvm_map_addr_augment(struct 
vm_m
 int uvm_map_inentry_recheck(u_long, vaddr_t,
 struct p_inentry *);
 boolean_t   uvm_map_inentry_fix(struct proc *, struct p_inentry *,
-vaddr_t, int (*)(vm_map_entry_t), u_long);
+vaddr_t, int (*)(vm_map_entry_t));
 /*
  * Tree management functions.
  */
@@ -1848,10 +1848,11 @@ uvm_map_inentry_recheck(u_long serial, v
  */
 boolean_t
 uvm_map_inentry_fix(struct proc *p, struct p_inentry *ie, vaddr_t addr,
-int (*fn)(vm_map_entry_t), u_long serial)
+int (*fn)(vm_map_entry_t))
 {
vm_map_t map = >p_vmspace->vm_map;
vm_map_entry_t entry;
+   u_long serial;
int ret;
 
if (addr < map->min_offset || addr >= map->max_offset)
@@ -1866,6 +1867,11 @@ uvm_map_inentry_fix(struct proc *p, stru
return (FALSE);
}
 
+   if (ie == >p_spinentry)
+   serial = map->sserial;
+   else
+   serial = map->wserial;
+
ret = (*fn)(entry);
if (ret == 0) {
vm_map_unlock_read(map);
@@ -1889,16 +1895,16 @@ uvm_map_inentry(struct proc *p, struct p
boolean_t ok = TRUE;
 
if (uvm_map_inentry_recheck(serial, addr, ie)) {
-   KERNEL_LOCK();
-   ok = uvm_map_inentry_fix(p, ie, addr, fn, serial);
+   ok = uvm_map_inentry_fix(p, ie, addr, fn);
if (!ok) {
printf(fmt, p->p_p->ps_comm, p->p_p->ps_pid, p->p_tid,
addr, ie->ie_start, ie->ie_end);
+   KERNEL_LOCK();
p->p_p->ps_acflag |= AMAP;
sv.sival_ptr = (void *)PROC_PC(p);
trapsignal(p, SIGSEGV, 0, SEGV_ACCERR, sv);
+   KERNEL_UNLOCK();
}
-   KERNEL_UNLOCK();
}
return (ok);
 }