devfs(4): defer freeing until we drop devmtx ("cdev")

Before r332974 the old code would sometimes cause a rare lock order
reversal against pagequeue, which looked roughly like this:

witness_checkorder()
__mtx_lock-flags()
vm_page_alloc()
uma_small_alloc()
keg_alloc_slab()
keg_fetch-slab()
zone_fetch-slab()
zone_import()
zone_alloc_bucket()
uma_zalloc_arg()
bucket_alloc()
uma_zfree_arg()
free()
devfs_metoo()
devfs_populate_loop()
devfs_populate()
devfs_rioctl()
VOP_IOCTL_APV()
VOP_IOCTL()
vn_ioctl()
fo_ioctl()
kern_ioctl()
sys_ioctl()

Since r332974 the original problem no longer exists, but it still
makes sense to move things out of the - often congested - lock.

Reviewed By:	kib, markj
Sponsored by:	NetApp, Inc.
Sponsored by:	Klara, Inc.
Differential Revision: https://reviews.freebsd.org/D27334
This commit is contained in:
Edward Tomasz Napierala 2020-12-29 13:45:53 +00:00
parent e9556246be
commit 4ddb3cc597

View File

@ -482,7 +482,7 @@ devfs_purge(struct devfs_mount *dm, struct devfs_dirent *dd)
static void
devfs_metoo(struct cdev_priv *cdp, struct devfs_mount *dm)
{
struct devfs_dirent **dep;
struct devfs_dirent **dep, **olddep;
int siz;
siz = (dm->dm_idx + 1) * sizeof *dep;
@ -495,8 +495,7 @@ devfs_metoo(struct cdev_priv *cdp, struct devfs_mount *dm)
return;
}
memcpy(dep, cdp->cdp_dirents, (cdp->cdp_maxdirent + 1) * sizeof *dep);
if (cdp->cdp_maxdirent > 0)
free(cdp->cdp_dirents, M_DEVFS2);
olddep = cdp->cdp_maxdirent > 0 ? cdp->cdp_dirents : NULL;
cdp->cdp_dirents = dep;
/*
* XXX: if malloc told us how much we actually got this could
@ -504,6 +503,7 @@ devfs_metoo(struct cdev_priv *cdp, struct devfs_mount *dm)
*/
cdp->cdp_maxdirent = dm->dm_idx;
dev_unlock();
free(olddep, M_DEVFS2);
}
/*