Date: Wed, 10 Mar 1999 17:47:53 -0500 (EST) From: "Mark J. Taylor" <mtaylor@cybernet.com> To: freebsd-hackers@freebsd.org, jkh@freebsd.org Subject: ccd driver problems with > 2GB segments, +fix Message-ID: <XFMail.990310174753.mtaylor@cybernet.com>
next in thread | raw e-mail | index | archive | help
I've found, and corrected, a serious problem with the ccd driver with regards to large segments. The bugs were discovered while developing our NetMAX system, under FreeBSD 2.2.x. The problems still exist in 3.x, and are easily repeatable. Creating a ccd with a segment that is greater than 2048 MB bytes in size will cause a kernel panic. Fix: The ccd driver has a variable "bcount" that is a long: it needs to be a u_long. And, a 64 bit int is shifted into a 32 bit int in ccdbuffer(): the 32 bit int must changed to be a 64 bit int. This second change has already been made in NetBSD (somebody else figured it out), where the ccd driver originated from. It was rather tricky to find: the math involving block numbers and ccd segment size had to work our just right, which generally meant that I had to create thousands (or tens of thousands) of files before the kernel would hang. But once I found that block, it was infinitely repeatable. Please, don't ignore this one. I've sent patches before, and made PRs, and they've just ended up in a black hole. I've got more patches to FreeBSD, but I feel reluctant to submit them, because nobody cares to either review them or commit them. Here's the patch to 3.1's /sys/dev/ccd/ccd.c, followed by a sequence of commands to generate a panic using the original ccd code: *** ccd.c.orig Thu Feb 25 23:07:51 1999 --- ccd.c Tue Mar 9 21:11:01 1999 *************** *** 196,204 **** static void ccdintr __P((struct ccd_softc *, struct buf *)); static int ccdinit __P((struct ccddevice *, char **, struct proc *)); static int ccdlookup __P((char *, struct proc *p, struct vnode **)); static void ccdbuffer __P((struct ccdbuf **ret, struct ccd_softc *, ! struct buf *, daddr_t, caddr_t, long)); static void ccdgetdisklabel __P((dev_t)); static void ccdmakedisklabel __P((struct ccd_softc *)); static int ccdlock __P((struct ccd_softc *)); static void ccdunlock __P((struct ccd_softc *)); --- 196,204 ---- static void ccdintr __P((struct ccd_softc *, struct buf *)); static int ccdinit __P((struct ccddevice *, char **, struct proc *)); static int ccdlookup __P((char *, struct proc *p, struct vnode **)); static void ccdbuffer __P((struct ccdbuf **ret, struct ccd_softc *, ! struct buf *, daddr_t, caddr_t, u_long)); static void ccdgetdisklabel __P((dev_t)); static void ccdmakedisklabel __P((struct ccd_softc *)); static int ccdlock __P((struct ccd_softc *)); static void ccdunlock __P((struct ccd_softc *)); *************** *** 764,772 **** ccdstart(cs, bp) register struct ccd_softc *cs; register struct buf *bp; { ! register long bcount, rcount; struct ccdbuf *cbp[4]; /* XXX! : 2 reads and 2 writes for RAID 4/5 */ caddr_t addr; daddr_t bn; --- 764,772 ---- ccdstart(cs, bp) register struct ccd_softc *cs; register struct buf *bp; { ! register u_long bcount, rcount; struct ccdbuf *cbp[4]; /* XXX! : 2 reads and 2 writes for RAID 4/5 */ caddr_t addr; daddr_t bn; *************** *** 819,831 **** register struct ccd_softc *cs; struct buf *bp; daddr_t bn; caddr_t addr; ! long bcount; { register struct ccdcinfo *ci, *ci2 = NULL; /* XXX */ register struct ccdbuf *cbp; register daddr_t cbn, cboff; #ifdef DEBUG if (ccddebug & CCDB_IO) printf("ccdbuffer(%x, %x, %d, %x, %d)\n", --- 819,832 ---- register struct ccd_softc *cs; struct buf *bp; daddr_t bn; caddr_t addr; ! u_long bcount; { register struct ccdcinfo *ci, *ci2 = NULL; /* XXX */ register struct ccdbuf *cbp; register daddr_t cbn, cboff; + register off_t cbc; #ifdef DEBUG if (ccddebug & CCDB_IO) printf("ccdbuffer(%x, %x, %d, %x, %d)\n", *************** *** 902,914 **** cbp->cb_buf.b_vp = ci->ci_vp; LIST_INIT(&cbp->cb_buf.b_dep); cbp->cb_buf.b_resid = 0; if (cs->sc_ileave == 0) ! cbp->cb_buf.b_bcount = dbtob(ci->ci_size - cbn); else ! cbp->cb_buf.b_bcount = dbtob(cs->sc_ileave - cboff); ! if (cbp->cb_buf.b_bcount > bcount) ! cbp->cb_buf.b_bcount = bcount; cbp->cb_buf.b_bufsize = cbp->cb_buf.b_bcount; /* --- 903,914 ---- cbp->cb_buf.b_vp = ci->ci_vp; LIST_INIT(&cbp->cb_buf.b_dep); cbp->cb_buf.b_resid = 0; if (cs->sc_ileave == 0) ! cbc = dbtob((off_t)(ci->ci_size - cbn)); else ! cbc = dbtob((off_t)(cs->sc_ileave - cboff)); ! cbp->cb_buf.b_bcount = (cbc < bcount) ? cbc : bcount; cbp->cb_buf.b_bufsize = cbp->cb_buf.b_bcount; /* How To Repeat (using a vnode instead of a disk slice): 1) create a vnode that we can use as a ccd: dd if=/dev/zero of=/var/tmp/XXX bs=1m count=2049 2) configure the vnode and label it: vnconfig -c -s labels /dev/vn0 /var/tmp/XXX disklabel -w -B vn0 auto 3) edit the disklabel to set "c" to type "4.2BSD" disklabel -e vn0 4) config this vnode as a ccd: ccdconfig -c ccd0 0 0 /dev/vn0c PANIC... With the patches above, the ccdconfig will not cause a panic, and the ccd should work as expected. -------------------------------------------------------------------- Mark J. Taylor Networking Research Cybernet Systems mtaylor@cybernet.com 727 Airport Blvd. PHONE (734) 668-2567 Ann Arbor, MI 48108 FAX (734) 668-8780 http://www.cybernet.com/ http://www.netmax.com/ -------------------------------------------------------------------- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?XFMail.990310174753.mtaylor>