Currently dkcsumattach() walks the alldevs list whilst performing
operations that will sleep. This is rather bad if the list happens to be
modified (i.e. a device detachs) whilst it is sleeping.

The following diff resolves this issue. The process is very similar to
that used for softraid - we maintain a list of disks that we have checked
and each time we do something that may have slept we restart the
scan from the top of the list.

A couple of other things come along for the ride:

1. We can walk disklist rather than walking alldevs.

2. We can benefit from the more recent changes to disk_attach() and
use the devno that is stored in the disk struct.

3. The DKF_NOREADLABEL flag is used to skip disks to dkcsum, rather
than using dev_rawpart() - the list should be similar and we already
exclude cd(4) and fd(4) devices. If we do this dev_rawpart() can probably
be removed.

I have lightly tested this, however further testing (on i386 and amd64)
would be appreciated.

Comments or oks?

Index: arch/i386/i386/dkcsum.c
===================================================================
RCS file: /cvs/src/sys/arch/i386/i386/dkcsum.c,v
retrieving revision 1.29
diff -u -p -r1.29 dkcsum.c
--- arch/i386/i386/dkcsum.c     16 Apr 2011 03:21:15 -0000      1.29
+++ arch/i386/i386/dkcsum.c     2 Jul 2011 16:01:57 -0000
@@ -33,8 +33,10 @@
 #include <sys/buf.h>
 #include <sys/conf.h>
 #include <sys/device.h>
+#include <sys/disk.h>
 #include <sys/disklabel.h>
 #include <sys/fcntl.h>
+#include <sys/malloc.h>
 #include <sys/proc.h>
 #include <sys/reboot.h>
 #include <sys/stat.h>
@@ -44,16 +46,23 @@
 
 #include <lib/libz/zlib.h>
 
-dev_t dev_rawpart(struct device *);    /* XXX */
-
 extern u_int32_t bios_cksumlen;
 extern bios_diskinfo_t *bios_diskinfo;
 extern dev_t bootdev;
 
+struct dkcsum_disk {
+       dev_t dkc_devno;
+       SLIST_ENTRY(dkcsum_disk) dkc_link;
+};
+SLIST_HEAD(dkcsum_disklist_head, dkcsum_disk);
+
 void
 dkcsumattach(void)
 {
+       struct dkcsum_disklist_head dkc_disklist;
+       struct dkcsum_disk *dkc;
        struct device *dv;
+       struct disk *dk;
        struct buf *bp;
        struct bdevsw *bdsw;
        dev_t dev, pribootdev, altbootdev;
@@ -88,12 +97,39 @@ dkcsumattach(void)
         */
        bp = geteblk(bios_cksumlen * DEV_BSIZE);        /* XXX error check?  */
 
-       TAILQ_FOREACH(dv, &alldevs, dv_list) {
-               if (dv->dv_class != DV_DISK)
+       SLIST_INIT(&dkc_disklist);
+       dk = TAILQ_FIRST(&disklist);
+       while (dk != TAILQ_END(&disklist)) {
+
+               dv = dk->dk_device;
+               if (dv == NULL || dk->dk_devno == NODEV ||
+                   (dk->dk_flags & DKF_NOLABELREAD)) {
+                       dk = TAILQ_NEXT(dk, dk_link);
                        continue;
-               bp->b_dev = dev = dev_rawpart(dv);
-               if (dev == NODEV)
+               }
+
+               SLIST_FOREACH(dkc, &dkc_disklist, dkc_link)
+                       if (dkc->dkc_devno == dk->dk_devno)
+                               break;
+
+               if (dkc != NULL) {
+                       dk = TAILQ_NEXT(dk, dk_link);
                        continue;
+               }
+
+#ifdef DEBUG
+               printf("dkcsum: adding %s to the list of checked disks\n",
+                   dv->dv_xname);
+#endif
+               dkc = malloc(sizeof(struct dkcsum_disk), M_DEVBUF,
+                   M_NOWAIT | M_CANFAIL | M_ZERO);
+               if (dkc == NULL)
+                       panic("dkcsum: failed to allocate memory");
+               dkc->dkc_devno = dk->dk_devno;
+               SLIST_INSERT_HEAD(&dkc_disklist, dkc, dkc_link);
+
+               bp->b_dev = dev = MAKEDISKDEV(major(dk->dk_devno),
+                   DISKUNIT(dk->dk_devno), RAW_PART);
                bdsw = &bdevsw[major(dev)];
 
                /*
@@ -107,6 +143,7 @@ dkcsumattach(void)
                        printf("dkcsum: %s open failed (%d)\n",
                            dv->dv_xname, error);
 #endif
+                       dk = TAILQ_FIRST(&disklist);
                        continue;
                }
 
@@ -129,6 +166,7 @@ dkcsumattach(void)
                                printf("dkcsum: %s close failed (%d)\n",
                                    dv->dv_xname, error);
 #endif
+                       dk = TAILQ_FIRST(&disklist);
                        continue;
                }
                error = (*bdsw->d_close)(dev, FREAD, S_IFCHR, curproc);
@@ -138,6 +176,7 @@ dkcsumattach(void)
                        printf("dkcsum: %s closed failed (%d)\n",
                            dv->dv_xname, error);
 #endif
+                       dk = TAILQ_FIRST(&disklist);
                        continue;
                }
 
@@ -173,6 +212,7 @@ dkcsumattach(void)
                        printf("dkcsum: %s has no matching BIOS drive\n",
                            dv->dv_xname);
 #endif 
+                       dk = TAILQ_FIRST(&disklist);
                        continue;
                }
 
@@ -223,9 +263,18 @@ dkcsumattach(void)
                hit->bsd_dev = MAKEBOOTDEV(major(bp->b_dev), 0, 0,
                    DISKUNIT(bp->b_dev), RAW_PART);
                hit->flags |= BDI_PICKED;
+
+               /* Restart scan since we may have slept. */
+               dk = TAILQ_FIRST(&disklist);
        }
        bootdev = pribootdev ? pribootdev : altbootdev ? altbootdev : bootdev;
 
        bp->b_flags |= B_INVAL;
        brelse(bp);
+
+       while (!SLIST_EMPTY(&dkc_disklist)) {
+               dkc = SLIST_FIRST(&dkc_disklist);
+               SLIST_REMOVE_HEAD(&dkc_disklist, dkc_link);
+               free(dkc, M_DEVBUF);
+       }
 }
Index: arch/amd64/amd64/dkcsum.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/dkcsum.c,v
retrieving revision 1.18
diff -u -p -r1.18 dkcsum.c
--- arch/amd64/amd64/dkcsum.c   16 Apr 2011 03:21:15 -0000      1.18
+++ arch/amd64/amd64/dkcsum.c   2 Jul 2011 16:01:57 -0000
@@ -33,8 +33,10 @@
 #include <sys/buf.h>
 #include <sys/conf.h>
 #include <sys/device.h>
+#include <sys/disk.h>
 #include <sys/disklabel.h>
 #include <sys/fcntl.h>
+#include <sys/malloc.h>
 #include <sys/proc.h>
 #include <sys/reboot.h>
 #include <sys/stat.h>
@@ -44,16 +46,23 @@
 
 #include <lib/libz/zlib.h>
 
-dev_t dev_rawpart(struct device *);    /* XXX */
-
 extern u_int32_t bios_cksumlen;
 extern bios_diskinfo_t *bios_diskinfo;
 extern dev_t bootdev;
 
+struct dkcsum_disk {
+       dev_t dkc_devno;
+       SLIST_ENTRY(dkcsum_disk) dkc_link;
+};
+SLIST_HEAD(dkcsum_disklist_head, dkcsum_disk);
+
 void
 dkcsumattach(void)
 {
+       struct dkcsum_disklist_head dkc_disklist;
+       struct dkcsum_disk *dkc;
        struct device *dv;
+       struct disk *dk;
        struct buf *bp;
        struct bdevsw *bdsw;
        dev_t dev, pribootdev, altbootdev;
@@ -88,12 +97,39 @@ dkcsumattach(void)
         */
        bp = geteblk(bios_cksumlen * DEV_BSIZE);        /* XXX error check?  */
 
-       TAILQ_FOREACH(dv, &alldevs, dv_list) {
-               if (dv->dv_class != DV_DISK)
+       SLIST_INIT(&dkc_disklist);
+       dk = TAILQ_FIRST(&disklist);
+       while (dk != TAILQ_END(&disklist)) {
+
+               dv = dk->dk_device;
+               if (dv == NULL || dk->dk_devno == NODEV ||
+                   (dk->dk_flags & DKF_NOLABELREAD)) {
+                       dk = TAILQ_NEXT(dk, dk_link);
                        continue;
-               bp->b_dev = dev = dev_rawpart(dv);
-               if (dev == NODEV)
+               }
+
+               SLIST_FOREACH(dkc, &dkc_disklist, dkc_link)
+                       if (dkc->dkc_devno == dk->dk_devno)
+                               break;
+
+               if (dkc != NULL) {
+                       dk = TAILQ_NEXT(dk, dk_link);
                        continue;
+               }
+
+#ifdef DEBUG
+               printf("dkcsum: adding %s to the list of checked disks\n",
+                   dv->dv_xname);
+#endif
+               dkc = malloc(sizeof(struct dkcsum_disk), M_DEVBUF,
+                   M_NOWAIT | M_CANFAIL | M_ZERO);
+               if (dkc == NULL)
+                       panic("dkcsum: failed to allocate memory");
+               dkc->dkc_devno = dk->dk_devno;
+               SLIST_INSERT_HEAD(&dkc_disklist, dkc, dkc_link);
+
+               bp->b_dev = dev = MAKEDISKDEV(major(dk->dk_devno),
+                   DISKUNIT(dk->dk_devno), RAW_PART);
                bdsw = &bdevsw[major(dev)];
 
                /*
@@ -107,6 +143,7 @@ dkcsumattach(void)
                        printf("dkcsum: %s open failed (%d)\n",
                            dv->dv_xname, error);
 #endif
+                       dk = TAILQ_FIRST(&disklist);
                        continue;
                }
 
@@ -129,6 +166,7 @@ dkcsumattach(void)
                                printf("dkcsum: %s close failed (%d)\n",
                                    dv->dv_xname, error);
 #endif
+                       dk = TAILQ_FIRST(&disklist);
                        continue;
                }
                error = (*bdsw->d_close)(dev, FREAD, S_IFCHR, curproc);
@@ -138,6 +176,7 @@ dkcsumattach(void)
                        printf("dkcsum: %s closed failed (%d)\n",
                            dv->dv_xname, error);
 #endif
+                       dk = TAILQ_FIRST(&disklist);
                        continue;
                }
 
@@ -173,6 +212,7 @@ dkcsumattach(void)
                        printf("dkcsum: %s has no matching BIOS drive\n",
                            dv->dv_xname);
 #endif 
+                       dk = TAILQ_FIRST(&disklist);
                        continue;
                }
 
@@ -223,9 +263,18 @@ dkcsumattach(void)
                hit->bsd_dev = MAKEBOOTDEV(major(bp->b_dev), 0, 0,
                    DISKUNIT(bp->b_dev), RAW_PART);
                hit->flags |= BDI_PICKED;
+
+               /* Restart scan since we may have slept. */
+               dk = TAILQ_FIRST(&disklist);
        }
        bootdev = pribootdev ? pribootdev : altbootdev ? altbootdev : bootdev;
 
        bp->b_flags |= B_INVAL;
        brelse(bp);
+
+       while (!SLIST_EMPTY(&dkc_disklist)) {
+               dkc = SLIST_FIRST(&dkc_disklist);
+               SLIST_REMOVE_HEAD(&dkc_disklist, dkc_link);
+               free(dkc, M_DEVBUF);
+       }
 }

-- 

    "Reason is not automatic. Those who deny it cannot be conquered by it.
     Do not count on them. Leave them alone." -- Ayn Rand

Reply via email to