Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 3 Aug 2011 16:50:44 +0300
From:      Kostik Belousov <kostikbel@gmail.com>
To:        Kohji Okuno <okuno.kohji@jp.panasonic.com>
Cc:        freebsd-current@freebsd.org
Subject:   Re: Bug: devfs is sure to have the bug.
Message-ID:  <20110803135044.GM17489@deviant.kiev.zoral.com.ua>
In-Reply-To: <20110803.144423.2018247186416313018.okuno.kohji@jp.panasonic.com>
References:  <20110803.141636.145758949453810779.okuno.kohji@jp.panasonic.com> <20110803.144423.2018247186416313018.okuno.kohji@jp.panasonic.com>

next in thread | previous in thread | raw e-mail | index | archive | help

--Z7URUY1CzunwZ4PW
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Wed, Aug 03, 2011 at 02:44:23PM +0900, Kohji Okuno wrote:
> Hello,
>=20
> > Hello,
> >=20
> > I think that devfs is sure to have the bug.
> > I found that I couldn't open "/dev/XXX" though the kernel detected XXX
> > device.
> >=20
> >=20
> > "dm->dm_generation" is updated with "devfs_generation" in
> > devfs_populate(), and the context holds only "dm->dm_lock" in
> > devfs_populate().
> >=20
> > On the other hand, "devfs_generation" is incremented in devfs_create()
> > and devfs_destroy() the context holds only "devmtx" in devfs_create()
> > and devfs_destroy().
> >=20
> > If a context executes devfs_create() when other context is executing
> > (***), then "dm->dm_generation" is updated incorrect value.
> > As a result, we can not open the last detected device (we receive ENOEN=
T).
> >=20
> > I think that we should change the lock method.
> > May I have advice?
> >=20
> > void
> > devfs_populate(struct devfs_mount *dm)
> > {
> >=20
> >         sx_assert(&dm->dm_lock, SX_XLOCKED);
> >         if (dm->dm_generation =3D=3D devfs_generation)
> >                 return;
> >         while (devfs_populate_loop(dm, 0))
> >                 continue;
> > (***)
> >         dm->dm_generation =3D devfs_generation;
> > }
> >=20
> > void
> > devfs_create(struct cdev *dev)
> > {
> >         struct cdev_priv *cdp;
> >=20
> >         mtx_assert(&devmtx, MA_OWNED);
> >         cdp =3D cdev2priv(dev);
> >         cdp->cdp_flags |=3D CDP_ACTIVE;
> >         cdp->cdp_inode =3D alloc_unrl(devfs_inos);
> >         dev_refl(dev);
> >         TAILQ_INSERT_TAIL(&cdevp_list, cdp, cdp_list);
> >         devfs_generation++;
> > }
> >=20
> > void
> > devfs_destroy(struct cdev *dev)
> > {
> >         struct cdev_priv *cdp;
> >=20
> >         mtx_assert(&devmtx, MA_OWNED);
> >         cdp =3D cdev2priv(dev);
> >         cdp->cdp_flags &=3D ~CDP_ACTIVE;
> >         devfs_generation++;
> > }
> >=20
> > Thanks.
>=20
> I propose the solution. May I have advice?
>=20
> void
> devfs_populate(struct devfs_mount *dm)
> {
>=20
>         sx_assert(&dm->dm_lock, SX_XLOCKED);
> #if 1 /* I propose... */
>         int tmp_generation;
> retry:
>         tmp_generation =3D devfs_generation;
>         if (dm->dm_generation =3D=3D tmp_generation)
>                 return;
>         while (devfs_populate_loop(dm, 0))
>                 continue;
>         if (tmp_generation !=3D devfs_generation)
>                 goto retry;
>         dm->dm_generation =3D tmp_generation;
> #else /* Original */
>         if (dm->dm_generation =3D=3D devfs_generation)
>                 return;
>         while (devfs_populate_loop(dm, 0))
>                 continue;
>         dm->dm_generation =3D devfs_generation;
> #endif
> }

I think the problem you described is real, and suggested change is right.
Initially, I thought that we should work with devfs_generation as with
the atomic type due to unlocked access in the devfs_populate(), but then
convinced myself that this is not needed.

But also, I think there is another half of the problem. Namely,
devfs_lookup() calls devfs_populate_vp(), and then does lookup with the
help of devfs_lookupx(). We will miss the generation update
happen after the drop of the dm_lock in devfs_populate_vp() to reacquire
the directory vnode lock.

I propose the change below, consisting of your fix and also retry of
population and lookup in case generations do not match for ENOENT case.

diff --git a/sys/fs/devfs/devfs_devs.c b/sys/fs/devfs/devfs_devs.c
index d72ada0..8ff9bc2 100644
--- a/sys/fs/devfs/devfs_devs.c
+++ b/sys/fs/devfs/devfs_devs.c
@@ -63,7 +63,7 @@ static MALLOC_DEFINE(M_CDEVP, "DEVFS1", "DEVFS cdev_priv =
storage");
=20
 static SYSCTL_NODE(_vfs, OID_AUTO, devfs, CTLFLAG_RW, 0, "DEVFS filesystem=
");
=20
-static unsigned devfs_generation;
+unsigned devfs_generation;
 SYSCTL_UINT(_vfs_devfs, OID_AUTO, generation, CTLFLAG_RD,
 	&devfs_generation, 0, "DEVFS generation number");
=20
@@ -630,13 +630,15 @@ devfs_populate_loop(struct devfs_mount *dm, int clean=
up)
 void
 devfs_populate(struct devfs_mount *dm)
 {
+	unsigned gen;
=20
 	sx_assert(&dm->dm_lock, SX_XLOCKED);
-	if (dm->dm_generation =3D=3D devfs_generation)
+	gen =3D devfs_generation;
+	if (dm->dm_generation =3D=3D gen)
 		return;
 	while (devfs_populate_loop(dm, 0))
 		continue;
-	dm->dm_generation =3D devfs_generation;
+	dm->dm_generation =3D gen;
 }
=20
 /*
diff --git a/sys/fs/devfs/devfs_int.h b/sys/fs/devfs/devfs_int.h
index cdc6aba..cb01ad1 100644
--- a/sys/fs/devfs/devfs_int.h
+++ b/sys/fs/devfs/devfs_int.h
@@ -71,6 +71,8 @@ struct cdev_priv {
=20
 #define	cdev2priv(c)	member2struct(cdev_priv, cdp_c, c)
=20
+extern unsigned devfs_generation;
+
 struct cdev	*devfs_alloc(int);
 int	devfs_dev_exists(const char *);
 void	devfs_free(struct cdev *);
diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c
index 955bd8b..2603caa 100644
--- a/sys/fs/devfs/devfs_vnops.c
+++ b/sys/fs/devfs/devfs_vnops.c
@@ -188,7 +188,7 @@ devfs_clear_cdevpriv(void)
  * On success devfs_populate_vp() returns with dmp->dm_lock held.
  */
 static int
-devfs_populate_vp(struct vnode *vp)
+devfs_populate_vp(struct vnode *vp, int dm_locked)
 {
 	struct devfs_dirent *de;
 	struct devfs_mount *dmp;
@@ -199,7 +199,8 @@ devfs_populate_vp(struct vnode *vp)
 	dmp =3D VFSTODEVFS(vp->v_mount);
 	locked =3D VOP_ISLOCKED(vp);
=20
-	sx_xlock(&dmp->dm_lock);
+	if (!dm_locked)
+		sx_xlock(&dmp->dm_lock);
 	DEVFS_DMP_HOLD(dmp);
=20
 	/* Can't call devfs_populate() with the vnode lock held. */
@@ -242,7 +243,7 @@ devfs_vptocnp(struct vop_vptocnp_args *ap)
=20
 	dmp =3D VFSTODEVFS(vp->v_mount);
=20
-	error =3D devfs_populate_vp(vp);
+	error =3D devfs_populate_vp(vp, 0);
 	if (error !=3D 0)
 		return (error);
=20
@@ -643,7 +644,7 @@ devfs_getattr(struct vop_getattr_args *ap)
 	struct devfs_mount *dmp;
 	struct cdev *dev;
=20
-	error =3D devfs_populate_vp(vp);
+	error =3D devfs_populate_vp(vp, 0);
 	if (error !=3D 0)
 		return (error);
=20
@@ -903,7 +904,7 @@ devfs_lookupx(struct vop_lookup_args *ap, int *dm_unloc=
k)
=20
 		if (cdev =3D=3D NULL)
 			sx_xlock(&dmp->dm_lock);
-		else if (devfs_populate_vp(dvp) !=3D 0) {
+		else if (devfs_populate_vp(dvp, 0) !=3D 0) {
 			*dm_unlock =3D 0;
 			sx_xlock(&dmp->dm_lock);
 			if (DEVFS_DMP_DROP(dmp)) {
@@ -966,19 +967,30 @@ devfs_lookupx(struct vop_lookup_args *ap, int *dm_unl=
ock)
 static int
 devfs_lookup(struct vop_lookup_args *ap)
 {
-	int j;
 	struct devfs_mount *dmp;
-	int dm_unlock;
+	int error, dm_unlock;
=20
-	if (devfs_populate_vp(ap->a_dvp) !=3D 0)
+	dm_unlock =3D 0;
+retry:
+	if (devfs_populate_vp(ap->a_dvp, dm_unlock) !=3D 0)
 		return (ENOTDIR);
=20
 	dmp =3D VFSTODEVFS(ap->a_dvp->v_mount);
 	dm_unlock =3D 1;
-	j =3D devfs_lookupx(ap, &dm_unlock);
-	if (dm_unlock =3D=3D 1)
+	error =3D devfs_lookupx(ap, &dm_unlock);
+	if (error =3D=3D ENOENT) {
+		if (dm_unlock)
+			sx_assert(&dmp->dm_lock, SA_XLOCKED);
+		else {
+			sx_xlock(&dmp->dm_lock);
+			dm_unlock =3D 1;
+		}
+		if (devfs_generation !=3D dmp->dm_generation)
+			goto retry;
+	}
+	if (dm_unlock)
 		sx_xunlock(&dmp->dm_lock);
-	return (j);
+	return (error);
 }
=20
 static int
@@ -1202,7 +1214,7 @@ devfs_readdir(struct vop_readdir_args *ap)
 	}
=20
 	dmp =3D VFSTODEVFS(ap->a_vp->v_mount);
-	if (devfs_populate_vp(ap->a_vp) !=3D 0) {
+	if (devfs_populate_vp(ap->a_vp, 0) !=3D 0) {
 		if (tmp_ncookies !=3D NULL)
 			ap->a_ncookies =3D tmp_ncookies;
 		return (EIO);
@@ -1565,7 +1577,7 @@ devfs_symlink(struct vop_symlink_args *ap)
 	if (error)
 		return(error);
 	dmp =3D VFSTODEVFS(ap->a_dvp->v_mount);
-	if (devfs_populate_vp(ap->a_dvp) !=3D 0)
+	if (devfs_populate_vp(ap->a_dvp, 0) !=3D 0)
 		return (ENOENT);
=20
 	dd =3D ap->a_dvp->v_data;

--Z7URUY1CzunwZ4PW
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (FreeBSD)

iEYEARECAAYFAk45UjQACgkQC3+MBN1Mb4hpsgCfY6WlwW7KY4ZDqYlaD4qT2LAi
5l0AoOvRtF51UCA0dkVNYz6sa28GvbEK
=XL0G
-----END PGP SIGNATURE-----

--Z7URUY1CzunwZ4PW--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20110803135044.GM17489>