The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ld.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: ld.c,v 1.27.2.1 2004/07/28 11:27:00 tron Exp $ */
    2 
    3 /*-
    4  * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
    5  * All rights reserved.
    6  *
    7  * This code is derived from software contributed to The NetBSD Foundation
    8  * by Andrew Doran and Charles M. Hannum.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  * 3. All advertising materials mentioning features or use of this software
   19  *    must display the following acknowledgement:
   20  *        This product includes software developed by the NetBSD
   21  *        Foundation, Inc. and its contributors.
   22  * 4. Neither the name of The NetBSD Foundation nor the names of its
   23  *    contributors may be used to endorse or promote products derived
   24  *    from this software without specific prior written permission.
   25  *
   26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   36  * POSSIBILITY OF SUCH DAMAGE.
   37  */
   38 
   39 /*
   40  * Disk driver for use by RAID controllers.
   41  */
   42 
   43 #include <sys/cdefs.h>
   44 __KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.27.2.1 2004/07/28 11:27:00 tron Exp $");
   45 
   46 #include "rnd.h"
   47 
   48 #include <sys/param.h>
   49 #include <sys/systm.h>
   50 #include <sys/kernel.h>
   51 #include <sys/device.h>
   52 #include <sys/queue.h>
   53 #include <sys/proc.h>
   54 #include <sys/buf.h>
   55 #include <sys/endian.h>
   56 #include <sys/disklabel.h>
   57 #include <sys/disk.h>
   58 #include <sys/dkio.h>
   59 #include <sys/stat.h>
   60 #include <sys/lock.h>
   61 #include <sys/conf.h>
   62 #include <sys/fcntl.h>
   63 #include <sys/vnode.h>
   64 #include <sys/syslog.h>
   65 #if NRND > 0
   66 #include <sys/rnd.h>
   67 #endif
   68 
   69 #include <dev/ldvar.h>
   70 
   71 static void     ldgetdefaultlabel(struct ld_softc *, struct disklabel *);
   72 static void     ldgetdisklabel(struct ld_softc *);
   73 static int      ldlock(struct ld_softc *);
   74 static void     ldminphys(struct buf *bp);
   75 static void     ldshutdown(void *);
   76 static void     ldstart(struct ld_softc *);
   77 static void     ldunlock(struct ld_softc *);
   78 
   79 extern struct   cfdriver ld_cd;
   80 
   81 dev_type_open(ldopen);
   82 dev_type_close(ldclose);
   83 dev_type_read(ldread);
   84 dev_type_write(ldwrite);
   85 dev_type_ioctl(ldioctl);
   86 dev_type_strategy(ldstrategy);
   87 dev_type_dump(lddump);
   88 dev_type_size(ldsize);
   89 
   90 const struct bdevsw ld_bdevsw = {
   91         ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK
   92 };
   93 
   94 const struct cdevsw ld_cdevsw = {
   95         ldopen, ldclose, ldread, ldwrite, ldioctl,
   96         nostop, notty, nopoll, nommap, nokqfilter, D_DISK
   97 };
   98 
   99 static struct   dkdriver lddkdriver = { ldstrategy };
  100 static void     *ld_sdh;
  101 
  102 void
  103 ldattach(struct ld_softc *sc)
  104 {
  105         char buf[9];
  106 
  107         if ((sc->sc_flags & LDF_ENABLED) == 0) {
  108                 printf("%s: disabled\n", sc->sc_dv.dv_xname);
  109                 return;
  110         }
  111 
  112         /* Initialise and attach the disk structure. */
  113         sc->sc_dk.dk_driver = &lddkdriver;
  114         sc->sc_dk.dk_name = sc->sc_dv.dv_xname;
  115         disk_attach(&sc->sc_dk);
  116 
  117         if (sc->sc_maxxfer > MAXPHYS)
  118                 sc->sc_maxxfer = MAXPHYS;
  119 
  120         /* Build synthetic geometry if necessary. */
  121         if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 ||
  122             sc->sc_ncylinders == 0) {
  123                 uint64_t ncyl;
  124 
  125                 if (sc->sc_secperunit <= 528 * 2048)            /* 528MB */
  126                         sc->sc_nheads = 16;
  127                 else if (sc->sc_secperunit <= 1024 * 2048)      /* 1GB */
  128                         sc->sc_nheads = 32;
  129                 else if (sc->sc_secperunit <= 21504 * 2048)     /* 21GB */
  130                         sc->sc_nheads = 64;
  131                 else if (sc->sc_secperunit <= 43008 * 2048)     /* 42GB */
  132                         sc->sc_nheads = 128;
  133                 else
  134                         sc->sc_nheads = 255;
  135 
  136                 sc->sc_nsectors = 63;
  137                 sc->sc_ncylinders = INT_MAX;
  138                 ncyl = sc->sc_secperunit / 
  139                     (sc->sc_nheads * sc->sc_nsectors);
  140                 if (ncyl < INT_MAX)
  141                         sc->sc_ncylinders = (int)ncyl;
  142         }
  143 
  144         format_bytes(buf, sizeof(buf), sc->sc_secperunit *
  145             sc->sc_secsize);
  146         printf("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n",
  147             sc->sc_dv.dv_xname, buf, sc->sc_ncylinders, sc->sc_nheads,
  148             sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit);
  149 
  150 #if NRND > 0
  151         /* Attach the device into the rnd source list. */
  152         rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname,
  153             RND_TYPE_DISK, 0);
  154 #endif
  155 
  156         /* Set the `shutdownhook'. */
  157         if (ld_sdh == NULL)
  158                 ld_sdh = shutdownhook_establish(ldshutdown, NULL);
  159         bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT()|BUFQ_SORT_RAWBLOCK);
  160 }
  161 
  162 int
  163 ldadjqparam(struct ld_softc *sc, int max)
  164 {
  165         int s;
  166 
  167         s = splbio();
  168         sc->sc_maxqueuecnt = max;
  169         splx(s);
  170 
  171         return (0);
  172 }
  173 
  174 int
  175 ldbegindetach(struct ld_softc *sc, int flags)
  176 {
  177         int s, rv = 0;
  178 
  179         if ((sc->sc_flags & LDF_ENABLED) == 0)
  180                 return (0);
  181 
  182         if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0)
  183                 return (EBUSY);
  184 
  185         s = splbio();
  186         sc->sc_maxqueuecnt = 0;
  187         sc->sc_flags |= LDF_DETACH;
  188         while (sc->sc_queuecnt > 0) {
  189                 sc->sc_flags |= LDF_DRAIN;
  190                 rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0);
  191                 if (rv)
  192                         break;
  193         }
  194         splx(s);
  195 
  196         return (rv);
  197 }
  198 
  199 void
  200 ldenddetach(struct ld_softc *sc)
  201 {
  202         struct buf *bp;
  203         int s, bmaj, cmaj, i, mn;
  204 
  205         if ((sc->sc_flags & LDF_ENABLED) == 0)
  206                 return;
  207 
  208         /* Wait for commands queued with the hardware to complete. */
  209         if (sc->sc_queuecnt != 0)
  210                 if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz))
  211                         printf("%s: not drained\n", sc->sc_dv.dv_xname);
  212 
  213         /* Locate the major numbers. */
  214         bmaj = bdevsw_lookup_major(&ld_bdevsw);
  215         cmaj = cdevsw_lookup_major(&ld_cdevsw);
  216 
  217         /* Kill off any queued buffers. */
  218         s = splbio();
  219         while ((bp = BUFQ_GET(&sc->sc_bufq)) != NULL) {
  220                 bp->b_error = EIO;
  221                 bp->b_flags |= B_ERROR;
  222                 bp->b_resid = bp->b_bcount;
  223                 biodone(bp);
  224         }
  225         bufq_free(&sc->sc_bufq);
  226         splx(s);
  227 
  228         /* Nuke the vnodes for any open instances. */
  229         for (i = 0; i < MAXPARTITIONS; i++) {
  230                 mn = DISKMINOR(sc->sc_dv.dv_unit, i);
  231                 vdevgone(bmaj, mn, mn, VBLK);
  232                 vdevgone(cmaj, mn, mn, VCHR);
  233         }
  234 
  235         /* Detach from the disk list. */
  236         disk_detach(&sc->sc_dk);
  237 
  238 #if NRND > 0
  239         /* Unhook the entropy source. */
  240         rnd_detach_source(&sc->sc_rnd_source);
  241 #endif
  242 
  243         /*
  244          * XXX We can't really flush the cache here, beceause the
  245          * XXX device may already be non-existent from the controller's
  246          * XXX perspective.
  247          */
  248 #if 0
  249         /* Flush the device's cache. */
  250         if (sc->sc_flush != NULL)
  251                 if ((*sc->sc_flush)(sc) != 0)
  252                         printf("%s: unable to flush cache\n",
  253                             sc->sc_dv.dv_xname);
  254 #endif
  255 }
  256 
  257 /* ARGSUSED */
  258 static void
  259 ldshutdown(void *cookie)
  260 {
  261         struct ld_softc *sc;
  262         int i;
  263 
  264         for (i = 0; i < ld_cd.cd_ndevs; i++) {
  265                 if ((sc = device_lookup(&ld_cd, i)) == NULL)
  266                         continue;
  267                 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0)
  268                         printf("%s: unable to flush cache\n",
  269                             sc->sc_dv.dv_xname);
  270         }
  271 }
  272 
  273 /* ARGSUSED */
  274 int
  275 ldopen(dev_t dev, int flags, int fmt, struct proc *p)
  276 {
  277         struct ld_softc *sc;
  278         int unit, part;
  279 
  280         unit = DISKUNIT(dev);
  281         if ((sc = device_lookup(&ld_cd, unit))== NULL)
  282                 return (ENXIO);
  283         if ((sc->sc_flags & LDF_ENABLED) == 0)
  284                 return (ENODEV);
  285         part = DISKPART(dev);
  286         ldlock(sc);
  287 
  288         if (sc->sc_dk.dk_openmask == 0) {
  289                 /* Load the partition info if not already loaded. */
  290                 if ((sc->sc_flags & LDF_VLABEL) == 0)
  291                         ldgetdisklabel(sc);
  292         }
  293 
  294         /* Check that the partition exists. */
  295         if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions ||
  296             sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
  297                 ldunlock(sc);
  298                 return (ENXIO);
  299         }
  300 
  301         /* Ensure only one open at a time. */
  302         switch (fmt) {
  303         case S_IFCHR:
  304                 sc->sc_dk.dk_copenmask |= (1 << part);
  305                 break;
  306         case S_IFBLK:
  307                 sc->sc_dk.dk_bopenmask |= (1 << part);
  308                 break;
  309         }
  310         sc->sc_dk.dk_openmask =
  311             sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
  312 
  313         ldunlock(sc);
  314         return (0);
  315 }
  316 
  317 /* ARGSUSED */
  318 int
  319 ldclose(dev_t dev, int flags, int fmt, struct proc *p)
  320 {
  321         struct ld_softc *sc;
  322         int part, unit;
  323 
  324         unit = DISKUNIT(dev);
  325         part = DISKPART(dev);
  326         sc = device_lookup(&ld_cd, unit);
  327         ldlock(sc);
  328 
  329         switch (fmt) {
  330         case S_IFCHR:
  331                 sc->sc_dk.dk_copenmask &= ~(1 << part);
  332                 break;
  333         case S_IFBLK:
  334                 sc->sc_dk.dk_bopenmask &= ~(1 << part);
  335                 break;
  336         }
  337         sc->sc_dk.dk_openmask =
  338             sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
  339 
  340         if (sc->sc_dk.dk_openmask == 0) {
  341                 if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0)
  342                         printf("%s: unable to flush cache\n",
  343                             sc->sc_dv.dv_xname);
  344                 if ((sc->sc_flags & LDF_KLABEL) == 0)
  345                         sc->sc_flags &= ~LDF_VLABEL;
  346         }
  347 
  348         ldunlock(sc);
  349         return (0);
  350 }
  351 
  352 /* ARGSUSED */
  353 int
  354 ldread(dev_t dev, struct uio *uio, int ioflag)
  355 {
  356 
  357         return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio));
  358 }
  359 
  360 /* ARGSUSED */
  361 int
  362 ldwrite(dev_t dev, struct uio *uio, int ioflag)
  363 {
  364 
  365         return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio));
  366 }
  367 
  368 /* ARGSUSED */
  369 int
  370 ldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
  371 {
  372         struct ld_softc *sc;
  373         int part, unit, error;
  374 #ifdef __HAVE_OLD_DISKLABEL
  375         struct disklabel newlabel;
  376 #endif
  377         struct disklabel *lp;
  378 
  379         unit = DISKUNIT(dev);
  380         part = DISKPART(dev);
  381         sc = device_lookup(&ld_cd, unit);
  382         error = 0;
  383 
  384         switch (cmd) {
  385         case DIOCGDINFO:
  386                 memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel));
  387                 return (0);
  388 
  389 #ifdef __HAVE_OLD_DISKLABEL
  390         case ODIOCGDINFO:
  391                 newlabel = *(sc->sc_dk.dk_label);
  392                 if (newlabel.d_npartitions > OLDMAXPARTITIONS)
  393                         return ENOTTY;
  394                 memcpy(addr, &newlabel, sizeof(struct olddisklabel));
  395                 return (0);
  396 #endif
  397 
  398         case DIOCGPART:
  399                 ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
  400                 ((struct partinfo *)addr)->part =
  401                     &sc->sc_dk.dk_label->d_partitions[part];
  402                 break;
  403 
  404         case DIOCWDINFO:
  405         case DIOCSDINFO:
  406 #ifdef __HAVE_OLD_DISKLABEL
  407         case ODIOCWDINFO:
  408         case ODIOCSDINFO:
  409 
  410                 if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
  411                         memset(&newlabel, 0, sizeof newlabel);
  412                         memcpy(&newlabel, addr, sizeof (struct olddisklabel));
  413                         lp = &newlabel;
  414                 } else
  415 #endif
  416                 lp = (struct disklabel *)addr;
  417 
  418                 if ((flag & FWRITE) == 0)
  419                         return (EBADF);
  420 
  421                 if ((error = ldlock(sc)) != 0)
  422                         return (error);
  423                 sc->sc_flags |= LDF_LABELLING;
  424 
  425                 error = setdisklabel(sc->sc_dk.dk_label,
  426                     lp, /*sc->sc_dk.dk_openmask : */0,
  427                     sc->sc_dk.dk_cpulabel);
  428                 if (error == 0 && (cmd == DIOCWDINFO
  429 #ifdef __HAVE_OLD_DISKLABEL
  430                     || cmd == ODIOCWDINFO
  431 #endif
  432                     ))
  433                         error = writedisklabel(
  434                             MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 
  435                             ldstrategy, sc->sc_dk.dk_label, 
  436                             sc->sc_dk.dk_cpulabel);
  437 
  438                 sc->sc_flags &= ~LDF_LABELLING;
  439                 ldunlock(sc);
  440                 break;
  441 
  442         case DIOCKLABEL:
  443                 if ((flag & FWRITE) == 0)
  444                         return (EBADF);
  445                 if (*(int *)addr)
  446                         sc->sc_flags |= LDF_KLABEL;
  447                 else
  448                         sc->sc_flags &= ~LDF_KLABEL;
  449                 break;
  450 
  451         case DIOCWLABEL:
  452                 if ((flag & FWRITE) == 0)
  453                         return (EBADF);
  454                 if (*(int *)addr)
  455                         sc->sc_flags |= LDF_WLABEL;
  456                 else
  457                         sc->sc_flags &= ~LDF_WLABEL;
  458                 break;
  459 
  460         case DIOCGDEFLABEL:
  461                 ldgetdefaultlabel(sc, (struct disklabel *)addr);
  462                 break;
  463 
  464 #ifdef __HAVE_OLD_DISKLABEL
  465         case ODIOCGDEFLABEL:
  466                 ldgetdefaultlabel(sc, &newlabel);
  467                 if (newlabel.d_npartitions > OLDMAXPARTITIONS)
  468                         return ENOTTY;
  469                 memcpy(addr, &newlabel, sizeof (struct olddisklabel));
  470                 break;
  471 #endif
  472 
  473         default:
  474                 error = ENOTTY;
  475                 break;
  476         }
  477 
  478         return (error);
  479 }
  480 
  481 void
  482 ldstrategy(struct buf *bp)
  483 {
  484         struct ld_softc *sc;
  485         struct disklabel *lp;
  486         daddr_t blkno;
  487         int s, part;
  488 
  489         sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
  490         part = DISKPART(bp->b_dev);
  491 
  492         if ((sc->sc_flags & LDF_DETACH) != 0) {
  493                 bp->b_error = EIO;
  494                 goto bad;
  495         }
  496 
  497         lp = sc->sc_dk.dk_label;
  498 
  499         /*
  500          * The transfer must be a whole number of blocks and the offset must
  501          * not be negative.
  502          */
  503         if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) {
  504                 bp->b_error = EINVAL;
  505                 goto bad;
  506         }
  507 
  508         /* If it's a null transfer, return immediately. */
  509         if (bp->b_bcount == 0)
  510                 goto done;
  511 
  512         /*
  513          * Do bounds checking and adjust the transfer.  If error, process.
  514          * If past the end of partition, just return.
  515          */
  516         if (part != RAW_PART &&
  517             bounds_check_with_label(&sc->sc_dk, bp,
  518             (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) {
  519                 goto done;
  520         }
  521 
  522         /*
  523          * Convert the block number to absolute and put it in terms
  524          * of the device's logical block size.
  525          */
  526         if (lp->d_secsize == DEV_BSIZE)
  527                 blkno = bp->b_blkno;
  528         else if (lp->d_secsize > DEV_BSIZE)
  529                 blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
  530         else
  531                 blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize);
  532 
  533         if (part != RAW_PART)
  534                 blkno += lp->d_partitions[part].p_offset;
  535 
  536         bp->b_rawblkno = blkno;
  537 
  538         s = splbio();
  539         BUFQ_PUT(&sc->sc_bufq, bp);
  540         ldstart(sc);
  541         splx(s);
  542         return;
  543 
  544  bad:
  545         bp->b_flags |= B_ERROR;
  546  done:
  547         bp->b_resid = bp->b_bcount;
  548         biodone(bp);
  549 }
  550 
  551 static void
  552 ldstart(struct ld_softc *sc)
  553 {
  554         struct buf *bp;
  555         int error;
  556 
  557         while (sc->sc_queuecnt < sc->sc_maxqueuecnt) {
  558                 /* See if there is work to do. */
  559                 if ((bp = BUFQ_PEEK(&sc->sc_bufq)) == NULL)
  560                         break;
  561 
  562                 disk_busy(&sc->sc_dk);
  563                 sc->sc_queuecnt++;
  564 
  565                 if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) {
  566                         /*
  567                          * The back-end is running the job; remove it from
  568                          * the queue.
  569                          */
  570                         (void) BUFQ_GET(&sc->sc_bufq);
  571                 } else  {
  572                         disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ));
  573                         sc->sc_queuecnt--;
  574                         if (error == EAGAIN) {
  575                                 /*
  576                                  * Temporary resource shortage in the
  577                                  * back-end; just defer the job until
  578                                  * later.
  579                                  *
  580                                  * XXX We might consider a watchdog timer
  581                                  * XXX to make sure we are kicked into action.
  582                                  */
  583                                 break;
  584                         } else {
  585                                 (void) BUFQ_GET(&sc->sc_bufq);
  586                                 bp->b_error = error;
  587                                 bp->b_flags |= B_ERROR;
  588                                 bp->b_resid = bp->b_bcount;
  589                                 biodone(bp);
  590                         }
  591                 }
  592         }
  593 }
  594 
  595 void
  596 lddone(struct ld_softc *sc, struct buf *bp)
  597 {
  598 
  599         if ((bp->b_flags & B_ERROR) != 0) {
  600                 diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label);
  601                 printf("\n");
  602         }
  603 
  604         disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid,
  605             (bp->b_flags & B_READ));
  606 #if NRND > 0
  607         rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno);
  608 #endif
  609         biodone(bp);
  610 
  611         if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) {
  612                 if ((sc->sc_flags & LDF_DRAIN) != 0) {
  613                         sc->sc_flags &= ~LDF_DRAIN;
  614                         wakeup(&sc->sc_queuecnt);
  615                 }
  616                 ldstart(sc);
  617         }
  618 }
  619 
  620 int
  621 ldsize(dev_t dev)
  622 {
  623         struct ld_softc *sc;
  624         int part, unit, omask, size;
  625 
  626         unit = DISKUNIT(dev);
  627         if ((sc = device_lookup(&ld_cd, unit)) == NULL)
  628                 return (ENODEV);
  629         if ((sc->sc_flags & LDF_ENABLED) == 0)
  630                 return (ENODEV);
  631         part = DISKPART(dev);
  632 
  633         omask = sc->sc_dk.dk_openmask & (1 << part);
  634 
  635         if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0)
  636                 return (-1);
  637         else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
  638                 size = -1;
  639         else
  640                 size = sc->sc_dk.dk_label->d_partitions[part].p_size *
  641                     (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE);
  642         if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0)
  643                 return (-1);
  644 
  645         return (size);
  646 }
  647 
  648 /*
  649  * Load the label information from the specified device.
  650  */
  651 static void
  652 ldgetdisklabel(struct ld_softc *sc)
  653 {
  654         const char *errstring;
  655 
  656         ldgetdefaultlabel(sc, sc->sc_dk.dk_label);
  657 
  658         /* Call the generic disklabel extraction routine. */
  659         errstring = readdisklabel(MAKEDISKDEV(0, sc->sc_dv.dv_unit, RAW_PART),
  660             ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel);
  661         if (errstring != NULL)
  662                 printf("%s: %s\n", sc->sc_dv.dv_xname, errstring);
  663 
  664         /* In-core label now valid. */
  665         sc->sc_flags |= LDF_VLABEL;
  666 }
  667 
  668 /*
  669  * Construct a ficticious label.
  670  */
  671 static void
  672 ldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp)
  673 {
  674 
  675         memset(lp, 0, sizeof(struct disklabel));
  676 
  677         lp->d_secsize = sc->sc_secsize;
  678         lp->d_ntracks = sc->sc_nheads;
  679         lp->d_nsectors = sc->sc_nsectors;
  680         lp->d_ncylinders = sc->sc_ncylinders;
  681         lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
  682         lp->d_type = DTYPE_LD;
  683         strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename));
  684         strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
  685         lp->d_secperunit = sc->sc_secperunit;
  686         lp->d_rpm = 7200;
  687         lp->d_interleave = 1;
  688         lp->d_flags = 0;
  689 
  690         lp->d_partitions[RAW_PART].p_offset = 0;
  691         lp->d_partitions[RAW_PART].p_size =
  692             lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
  693         lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
  694         lp->d_npartitions = RAW_PART + 1;
  695 
  696         lp->d_magic = DISKMAGIC;
  697         lp->d_magic2 = DISKMAGIC;
  698         lp->d_checksum = dkcksum(lp);
  699 }
  700 
  701 /*
  702  * Wait interruptibly for an exclusive lock.
  703  *
  704  * XXX Several drivers do this; it should be abstracted and made MP-safe.
  705  */
  706 static int
  707 ldlock(struct ld_softc *sc)
  708 {
  709         int error;
  710 
  711         while ((sc->sc_flags & LDF_LKHELD) != 0) {
  712                 sc->sc_flags |= LDF_LKWANTED;
  713                 if ((error = tsleep(sc, PRIBIO | PCATCH, "ldlck", 0)) != 0)
  714                         return (error);
  715         }
  716         sc->sc_flags |= LDF_LKHELD;
  717         return (0);
  718 }
  719 
  720 /*
  721  * Unlock and wake up any waiters.
  722  */
  723 static void
  724 ldunlock(struct ld_softc *sc)
  725 {
  726 
  727         sc->sc_flags &= ~LDF_LKHELD;
  728         if ((sc->sc_flags & LDF_LKWANTED) != 0) {
  729                 sc->sc_flags &= ~LDF_LKWANTED;
  730                 wakeup(sc);
  731         }
  732 }
  733 
  734 /*
  735  * Take a dump.
  736  */
  737 int
  738 lddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
  739 {
  740         struct ld_softc *sc;
  741         struct disklabel *lp;
  742         int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv;
  743         static int dumping;
  744 
  745         unit = DISKUNIT(dev);
  746         if ((sc = device_lookup(&ld_cd, unit)) == NULL)
  747                 return (ENXIO);
  748         if ((sc->sc_flags & LDF_ENABLED) == 0)
  749                 return (ENODEV);
  750         if (sc->sc_dump == NULL)
  751                 return (ENXIO);
  752 
  753         /* Check if recursive dump; if so, punt. */
  754         if (dumping)
  755                 return (EFAULT);
  756         dumping = 1;
  757 
  758         /* Convert to disk sectors.  Request must be a multiple of size. */
  759         part = DISKPART(dev);
  760         lp = sc->sc_dk.dk_label;
  761         if ((size % lp->d_secsize) != 0)
  762                 return (EFAULT);
  763         towrt = size / lp->d_secsize;
  764         blkno = dbtob(blkno) / lp->d_secsize;   /* blkno in DEV_BSIZE units */
  765 
  766         nsects = lp->d_partitions[part].p_size;
  767         sectoff = lp->d_partitions[part].p_offset;
  768 
  769         /* Check transfer bounds against partition size. */
  770         if ((blkno < 0) || ((blkno + towrt) > nsects))
  771                 return (EINVAL);
  772 
  773         /* Offset block number to start of partition. */
  774         blkno += sectoff;
  775 
  776         /* Start dumping and return when done. */
  777         maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1;
  778         while (towrt > 0) {
  779                 nblk = min(maxblkcnt, towrt);
  780 
  781                 if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0)
  782                         return (rv);
  783 
  784                 towrt -= nblk;
  785                 blkno += nblk;
  786                 va += nblk * sc->sc_secsize;
  787         }
  788 
  789         dumping = 0;
  790         return (0);
  791 }
  792 
  793 /*
  794  * Adjust the size of a transfer.
  795  */
  796 static void
  797 ldminphys(struct buf *bp)
  798 {
  799         struct ld_softc *sc;
  800 
  801         sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
  802 
  803         if (bp->b_bcount > sc->sc_maxxfer)
  804                 bp->b_bcount = sc->sc_maxxfer;
  805         minphys(bp);
  806 }

Cache object: 92ef83d9b1ede6fc08c3cccbf56e67a5


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.