On Wed, Nov 18, 2020 at 08:31:23PM -0300, Martin Pieuchot wrote:
> I found another race related to some missing locking, this time around
> uvm_pageclean().
>
> Diff below fixes the two places in /sys/uvm where the page queue lock
> should be taken. To prevent further corruption I added some assertions
> and documented some global data structures that are currently protected
> by this lock.
>
> Note that uvm_pagefree() is called by many pmaps most of the time
> without the lock held. The diff below doesn't fix them and that's why
> some assertions are commented out.
>
> ok?
It looks like there are a couple of other paths to uvm_pagefree() that
don't take the page queue lock - uvm_km_pgremove_intrface() and (on non
pmap direct archs) uvm_km_doputpage().
Since that doesn't really affect the diff, and everything else is right
as far as I can tell, ok jmatthew@
>
> Index: uvm/uvm.h
> ===================================================================
> RCS file: /cvs/src/sys/uvm/uvm.h,v
> retrieving revision 1.67
> diff -u -p -r1.67 uvm.h
> --- uvm/uvm.h 6 Dec 2019 08:33:25 -0000 1.67
> +++ uvm/uvm.h 18 Nov 2020 23:22:15 -0000
> @@ -44,18 +44,20 @@
> /*
> * uvm structure (vm global state: collected in one structure for ease
> * of reference...)
> + *
> + * Locks used to protect struct members in this file:
> + * Q uvm.pageqlock
> */
> -
> struct uvm {
> /* vm_page related parameters */
>
> /* vm_page queues */
> - struct pglist page_active; /* allocated pages, in use */
> - struct pglist page_inactive_swp;/* pages inactive (reclaim or free) */
> - struct pglist page_inactive_obj;/* pages inactive (reclaim or free) */
> + struct pglist page_active; /* [Q] allocated pages, in use */
> + struct pglist page_inactive_swp;/* [Q] pages inactive (reclaim/free) */
> + struct pglist page_inactive_obj;/* [Q] pages inactive (reclaim/free) */
> /* Lock order: pageqlock, then fpageqlock. */
> - struct mutex pageqlock; /* lock for active/inactive page q */
> - struct mutex fpageqlock; /* lock for free page q + pdaemon */
> + struct mutex pageqlock; /* [] lock for active/inactive page q */
> + struct mutex fpageqlock; /* [] lock for free page q + pdaemon */
> boolean_t page_init_done; /* TRUE if uvm_page_init() finished */
> struct uvm_pmr_control pmr_control; /* pmemrange data */
>
> Index: uvm/uvm_anon.c
> ===================================================================
> RCS file: /cvs/src/sys/uvm/uvm_anon.c,v
> retrieving revision 1.49
> diff -u -p -r1.49 uvm_anon.c
> --- uvm/uvm_anon.c 4 Jan 2020 16:17:29 -0000 1.49
> +++ uvm/uvm_anon.c 18 Nov 2020 23:22:15 -0000
> @@ -106,7 +106,9 @@ uvm_anfree_list(struct vm_anon *anon, st
> * clean page, and put on on pglist
> * for later freeing.
> */
> + uvm_lock_pageq();
> uvm_pageclean(pg);
> + uvm_unlock_pageq();
> TAILQ_INSERT_HEAD(pgl, pg, pageq);
> } else {
> uvm_lock_pageq(); /* lock out pagedaemon */
> Index: uvm/uvm_object.c
> ===================================================================
> RCS file: /cvs/src/sys/uvm/uvm_object.c,v
> retrieving revision 1.17
> diff -u -p -r1.17 uvm_object.c
> --- uvm/uvm_object.c 21 Oct 2020 09:08:14 -0000 1.17
> +++ uvm/uvm_object.c 18 Nov 2020 23:22:15 -0000
> @@ -172,7 +172,9 @@ uvm_objfree(struct uvm_object *uobj)
> * this pg from the uobj we are throwing away
> */
> atomic_clearbits_int(&pg->pg_flags, PG_TABLED);
> + uvm_lock_pageq();
> uvm_pageclean(pg);
> + uvm_unlock_pageq();
> TAILQ_INSERT_TAIL(&pgl, pg, pageq);
> }
> uvm_pmr_freepageq(&pgl);
> Index: uvm/uvm_page.c
> ===================================================================
> RCS file: /cvs/src/sys/uvm/uvm_page.c,v
> retrieving revision 1.150
> diff -u -p -r1.150 uvm_page.c
> --- uvm/uvm_page.c 22 Sep 2020 14:31:08 -0000 1.150
> +++ uvm/uvm_page.c 18 Nov 2020 23:22:15 -0000
> @@ -973,6 +973,10 @@ uvm_pageclean(struct vm_page *pg)
> {
> u_int flags_to_clear = 0;
>
> +#if all_pmap_are_fixed
> + MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
> +#endif
> +
> #ifdef DEBUG
> if (pg->uobject == (void *)0xdeadbeef &&
> pg->uanon == (void *)0xdeadbeef) {
> @@ -1037,6 +1041,10 @@ uvm_pageclean(struct vm_page *pg)
> void
> uvm_pagefree(struct vm_page *pg)
> {
> +#if all_pmap_are_fixed
> + MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
> +#endif
> +
> uvm_pageclean(pg);
> uvm_pmr_freepages(pg, 1);
> }
> @@ -1229,6 +1237,8 @@ uvm_pagelookup(struct uvm_object *obj, v
> void
> uvm_pagewire(struct vm_page *pg)
> {
> + MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
> +
> if (pg->wire_count == 0) {
> if (pg->pg_flags & PQ_ACTIVE) {
> TAILQ_REMOVE(&uvm.page_active, pg, pageq);
> @@ -1257,6 +1267,8 @@ uvm_pagewire(struct vm_page *pg)
> void
> uvm_pageunwire(struct vm_page *pg)
> {
> + MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
> +
> pg->wire_count--;
> if (pg->wire_count == 0) {
> TAILQ_INSERT_TAIL(&uvm.page_active, pg, pageq);
> @@ -1276,6 +1288,8 @@ uvm_pageunwire(struct vm_page *pg)
> void
> uvm_pagedeactivate(struct vm_page *pg)
> {
> + MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
> +
> if (pg->pg_flags & PQ_ACTIVE) {
> TAILQ_REMOVE(&uvm.page_active, pg, pageq);
> atomic_clearbits_int(&pg->pg_flags, PQ_ACTIVE);
> @@ -1310,6 +1324,8 @@ uvm_pagedeactivate(struct vm_page *pg)
> void
> uvm_pageactivate(struct vm_page *pg)
> {
> + MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
> +
> if (pg->pg_flags & PQ_INACTIVE) {
> if (pg->pg_flags & PQ_SWAPBACKED)
> TAILQ_REMOVE(&uvm.page_inactive_swp, pg, pageq);
> Index: uvm/uvm_pdaemon.c
> ===================================================================
> RCS file: /cvs/src/sys/uvm/uvm_pdaemon.c,v
> retrieving revision 1.87
> diff -u -p -r1.87 uvm_pdaemon.c
> --- uvm/uvm_pdaemon.c 29 Sep 2020 11:47:41 -0000 1.87
> +++ uvm/uvm_pdaemon.c 18 Nov 2020 23:22:15 -0000
> @@ -822,6 +822,8 @@ uvmpd_scan(void)
> struct uvm_object *uobj;
> boolean_t got_it;
>
> + MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
> +
> uvmexp.pdrevs++; /* counter */
> uobj = NULL;
>
>