Skip site navigation (1)Skip section navigation (2)
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>