Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 17 Jul 2007 15:45:54 +0300
From:      Kostik Belousov <kostikbel@gmail.com>
To:        Eric Anholt <anholt@FreeBSD.org>
Cc:        x11@FreeBSD.org
Subject:   Re: LOR when running google-earth
Message-ID:  <20070717124554.GB2200@deviant.kiev.zoral.com.ua>
In-Reply-To: <469CAC37.1080205@micom.mng.net>
References:  <469B17BB.9050305@micom.mng.net> <20070716105316.GT2200@deviant.kiev.zoral.com.ua> <469C240B.4060201@micom.mng.net> <20070717042309.GY2200@deviant.kiev.zoral.com.ua> <469C4CCC.5000102@micom.mng.net> <20070717112639.GA2200@deviant.kiev.zoral.com.ua> <469CAC37.1080205@micom.mng.net>

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

--dv+7Y1Jmnoh5Cnne
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

On Tue, Jul 17, 2007 at 07:47:03PM +0800, Ganbold wrote:
> Kostik Belousov wrote:
> >On Tue, Jul 17, 2007 at 12:59:56PM +0800, Ganbold wrote:
> > =20
> >>Thanks for the patch, Konstantin.
> >>It seems like LOR is gone after the patch.
> >>However I got following when I tried to switch virtual desktops under=
=20
> >>beryl:
> >>
> >>error: [drm:pid1:i915_emit_box] *ERROR* Bad box 64485,100..0,774
> >>error: [drm:pid1:i915_cmdbuffer] *ERROR* i915_dispatch_cmdbuffer failed
> >>error: [drm:pid1:i915_emit_box] *ERROR* Bad box 64485,100..0,774
> >>error: [drm:pid1:i915_cmdbuffer] *ERROR* i915_dispatch_cmdbuffer failed
> >>error: [drm:pid1157:drm_close] *ERROR* can't find authenticator
> >>
> >>On shell where I'm running googleearth it shows:
> >>
> >>DRM_I830_CMDBUFFER:  -22
> >>
> >>and googleearth crashes :(
> >>
> >>Maybe above problems are related to beryl/emerald.
> >>
> >>   =20
> >Ganbold,
> >does the problems you described show only with patch applied ? Or, they =
are
> >reproducable without patch, on the stock kernel ?
> > =20
> Konstantin,
>=20
> I just tested stock i915 module. googleearth crashes with same message:
>=20
> error: [drm:pid1:i915_emit_box] *ERROR* Bad box 64485,100..0,774
> error: [drm:pid1:i915_cmdbuffer] *ERROR* i915_dispatch_cmdbuffer failed
>=20
> DRM_I830_CMDBUFFER: -22
>=20
> So my guess was correct.
>=20
> thanks,
>=20
> Ganbold

Eric,
could you, please, review some further change to the i915 drm driver.

The LOR reported by Ganbold happens when useracc() checks the range of the
user address space in the dev/drm/i915_dma.c, using the DRM_VERIFYAREA_READ=
()
macro. It calls useracc(), that locks sx lock for user map, while holding
drm mutex.

lock order reversal: (sleepable after non-sleepable)
 1st 0xc3c06cd8 drm device (drm device) @=20
/usr/src/sys/modules/drm/drm/../../../dev/drm/drm_drv.c:907
 2nd 0xc3e6ea3c user map (user map) @ /usr/src/sys/vm/vm_glue.c:182
KDB: stack backtrace:
db_trace_self_wrapper(c08a3fa4,e6450a44,c066992e,c08a6485,c3e6ea3c,...)=20
at db_trace_self_wrapper+0x26
kdb_backtrace(c08a6485,c3e6ea3c,c08be517,c08be517,c08bdfed,...) at=20
kdb_backtrace+0x29
witness_checkorder(c3e6ea3c,9,c08bdfed,b6,c435a000,...) at=20
witness_checkorder+0x6de
_sx_xlock(c3e6ea3c,0,c08bdfed,b6,e6450aa8,...) at _sx_xlock+0x7d
_vm_map_lock_read(c3e6e9f8,c08bdfed,b6,c435a000,a60,...) at=20
_vm_map_lock_read+0x50
useracc(4ce1ede8,8,1,f5,c09ce1bc,...) at useracc+0x65
i915_cmdbuffer(c3bf3800,8018644b,c43814a0,3,c435a000,...) at=20
i915_cmdbuffer+0x10f
drm_ioctl(c3bf3800,8018644b,c43814a0,3,c435a000,...) at drm_ioctl+0x357
giant_ioctl(c3bf3800,8018644b,c43814a0,3,c435a000,...) at giant_ioctl+0x56
devfs_ioctl_f(c4cdd1b0,8018644b,c43814a0,c4356700,c435a000,...) at=20
devfs_ioctl_f+0xc9
kern_ioctl(c435a000,9,8018644b,c43814a0,450c4c,...) at kern_ioctl+0x243
ioctl(c435a000,e6450cfc,e6450c80,c4022e4a,c435a000,...) at ioctl+0x134
linux_ioctl_drm(c435a000,e6450cfc,c4030c45,a2f,c435a000,...) at=20
linux_ioctl_drm+0x2f
linux_ioctl(c435a000,e6450cfc,e6450cf8,e6450d1c,c4032dd0,...) at=20
linux_ioctl+0xca
syscall(e6450d38) at syscall+0x2b3
Xint0x80_syscall() at Xint0x80_syscall+0x20

In fact, besides the LOR, there are two bigger problems:
1. the check is racy, since userspace may modity the address space after
   the check is done, but before the actual access.
2. useracc() does not page in the requested address, thus further LOR may
   happen while vm_fault() would handle the page in.

The same LOR could occur due to use of DRM_COPY_FROM_USER_UNCHECKED(), if t=
he
page is swapped out.

Patch below tries to fix the problem for i915 driver (and it looks like the=
se
are all consumers of the DRM_VERIFY_AREA_READ) by wiring the user pages with
the help of vslock(). Also, I eliminated DRM_COPY_FROM_USER_UNCHECKED.

The obvious drawbacks of the patch are:
1. It further changes the drm code, making the FreeBSD fork of it.
2. vslock() causes user map fragmentation by clipping the wiring map entry
   to the wired region.
3. The only vslock() user in the CVS tree is sysctl(). I am not sure whether
   adding another one is good.

diff --git a/sys/dev/drm/i915_dma.c b/sys/dev/drm/i915_dma.c
index dfaf334..c9dd799 100644
--- a/sys/dev/drm/i915_dma.c
+++ b/sys/dev/drm/i915_dma.c
@@ -367,20 +367,14 @@ static int i915_emit_cmds(drm_device_t * dev, int __u=
ser * buffer, int dwords)
 	for (i =3D 0; i < dwords;) {
 		int cmd, sz;
=20
-	     if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) {
-
-			return DRM_ERR(EINVAL);
-	      }
+		cmd =3D buffer[i];
 		if ((sz =3D validate_cmd(cmd)) =3D=3D 0 || i + sz > dwords)
 			return DRM_ERR(EINVAL);
=20
 		OUT_RING(cmd);
=20
 		while (++i, --sz) {
-			if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i],
-							 sizeof(cmd))) {
-				return DRM_ERR(EINVAL);
-			}
+			cmd =3D buffer[i];
 			OUT_RING(cmd);
 		}
 	}
@@ -401,10 +395,7 @@ static int i915_emit_box(drm_device_t * dev,
 	drm_clip_rect_t box;
 	RING_LOCALS;
=20
-	if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) {
-		return EFAULT;
-	}
-
+	box =3D boxes[i];
 	if (box.y2 <=3D box.y1 || box.x2 <=3D box.x1 || box.y2 <=3D 0 || box.x2 <=
=3D 0) {
 		DRM_ERROR("Bad box %d,%d..%d,%d\n",
 			  box.x1, box.y1, box.x2, box.y2);
@@ -604,6 +595,7 @@ static int i915_batchbuffer(DRM_IOCTL_ARGS)
 	drm_i915_sarea_t *sarea_priv =3D (drm_i915_sarea_t *)
 	    dev_priv->sarea_priv;
 	drm_i915_batchbuffer_t batch;
+	size_t cliplen;
 	int ret;
=20
 	if (!dev_priv->allow_batchbuffer) {
@@ -619,14 +611,25 @@ static int i915_batchbuffer(DRM_IOCTL_ARGS)
=20
 	LOCK_TEST_WITH_RETURN(dev, filp);
=20
+	DRM_UNLOCK();
+	cliplen =3D batch.num_cliprects * sizeof(drm_clip_rect_t);=09
 	if (batch.num_cliprects && DRM_VERIFYAREA_READ(batch.cliprects,
-						       batch.num_cliprects *
-						       sizeof(drm_clip_rect_t)))
+						       cliplen)) {
+		DRM_LOCK();
 		return DRM_ERR(EFAULT);
-
+	}
+	ret =3D vslock(batch.cliprects, cliplen);
+	if (ret) {
+		DRM_ERROR("Fault wiring cliprects\n");
+		DRM_LOCK();
+		return DRM_ERR(EFAULT);
+	}
+	DRM_LOCK();
 	ret =3D i915_dispatch_batchbuffer(dev, &batch);
-
 	sarea_priv->last_dispatch =3D (int)hw_status[5];
+	DRM_UNLOCK();
+	vsunlock(batch.cliprects, cliplen);
+	DRM_LOCK();
 	return ret;
 }
=20
@@ -638,6 +641,7 @@ static int i915_cmdbuffer(DRM_IOCTL_ARGS)
 	drm_i915_sarea_t *sarea_priv =3D (drm_i915_sarea_t *)
 	    dev_priv->sarea_priv;
 	drm_i915_cmdbuffer_t cmdbuf;
+	size_t cliplen;
 	int ret;
=20
 	DRM_COPY_FROM_USER_IOCTL(cmdbuf, (drm_i915_cmdbuffer_t __user *) data,
@@ -648,22 +652,38 @@ static int i915_cmdbuffer(DRM_IOCTL_ARGS)
=20
 	LOCK_TEST_WITH_RETURN(dev, filp);
=20
+	DRM_UNLOCK();
+	cliplen =3D cmdbuf.num_cliprects * sizeof(drm_clip_rect_t);
 	if (cmdbuf.num_cliprects &&
-	    DRM_VERIFYAREA_READ(cmdbuf.cliprects,
-				cmdbuf.num_cliprects *
-				sizeof(drm_clip_rect_t))) {
+	    DRM_VERIFYAREA_READ(cmdbuf.cliprects, cliplen)) {
 		DRM_ERROR("Fault accessing cliprects\n");
+		DRM_LOCK();
 		return DRM_ERR(EFAULT);
 	}
-
-	ret =3D i915_dispatch_cmdbuffer(dev, &cmdbuf);
+	ret =3D vslock(cmdbuf.cliprects, cliplen);
 	if (ret) {
-		DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
-		return ret;
+		DRM_ERROR("Fault wiring cliprects\n");
+		DRM_LOCK();
+		return DRM_ERR(EFAULT);
 	}
-
-	sarea_priv->last_dispatch =3D (int)hw_status[5];
-	return 0;
+	ret =3D vslock(cmdbuf.buf, cmdbuf.sz);
+	if (ret) {
+		vsunlock(cmdbuf.cliprects, cliplen);
+		DRM_ERROR("Fault wiring cmds\n");
+		DRM_LOCK();
+		return DRM_ERR(EFAULT);
+	}
+	DRM_LOCK();
+	ret =3D i915_dispatch_cmdbuffer(dev, &cmdbuf);
+	if (ret =3D=3D 0)
+		sarea_priv->last_dispatch =3D (int)hw_status[5];
+	else
+		DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
+	DRM_UNLOCK();
+	vsunlock(cmdbuf.buf, cmdbuf.sz);
+	vsunlock(cmdbuf.cliprects, cliplen);
+	DRM_LOCK();
+	return (ret);
 }
=20
 static int i915_do_cleanup_pageflip(drm_device_t * dev)

--dv+7Y1Jmnoh5Cnne
Content-Type: application/pgp-signature
Content-Disposition: inline

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

iD8DBQFGnLoBC3+MBN1Mb4gRAnimAKCZggW1/EPwvNjGGGCIsMaT1AbHqACg6DXr
DHc9Ke4fz2UG3hpEq0DiZCw=
=lLhk
-----END PGP SIGNATURE-----

--dv+7Y1Jmnoh5Cnne--



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