Print this page
7367 blkdev: support block size larger than 512
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>

@@ -21,10 +21,11 @@
 /*
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
  * Copyright 2012 Alexey Zaytsev <alexey.zaytsev@gmail.com> All rights reserved.
  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2017 The MathWorks, Inc.  All rights reserved.
  */
 
 #include <sys/types.h>
 #include <sys/ksynch.h>
 #include <sys/kmem.h>

@@ -169,10 +170,11 @@
 static void bd_submit(bd_t *, bd_xfer_impl_t *);
 static void bd_runq_exit(bd_xfer_impl_t *, int);
 static void bd_update_state(bd_t *);
 static int bd_check_state(bd_t *, enum dkio_state *);
 static int bd_flush_write_cache(bd_t *, struct dk_callback *);
+static int bd_check_uio(dev_t, struct uio *);
 
 struct cmlb_tg_ops bd_tg_ops = {
         TG_DK_OPS_VERSION_1,
         bd_tg_rdwr,
         bd_tg_getinfo,

@@ -751,11 +753,11 @@
 
         ASSERT(bp);
 
         xi->i_bp = bp;
         xi->i_func = func;
-        xi->i_blkno = bp->b_lblkno;
+        xi->i_blkno = bp->b_lblkno >> (bd->d_blkshift - DEV_BSHIFT);
 
         if (bp->b_bcount == 0) {
                 xi->i_len = 0;
                 xi->i_nblks = 0;
                 xi->i_kaddr = NULL;

@@ -816,11 +818,11 @@
                         if ((ddi_dma_numwin(xi->i_dmah, &xi->i_num_win) !=
                             DDI_SUCCESS) ||
                             (ddi_dma_getwin(xi->i_dmah, 0, &xi->i_offset,
                             &len, &xi->i_dmac, &xi->i_ndmac) !=
                             DDI_SUCCESS) ||
-                            (P2PHASE(len, shift) != 0)) {
+                            (P2PHASE(len, (1U << shift)) != 0)) {
                                 (void) ddi_dma_unbind_handle(xi->i_dmah);
                                 rv = EFAULT;
                                 goto done;
                         }
                         xi->i_len = len;

@@ -1033,52 +1035,58 @@
         diskaddr_t      psize;
         bd_t            *bd;
         bd_xfer_impl_t  *xi;
         buf_t           *bp;
         int             rv;
+        uint32_t        shift;
+        daddr_t         d_blkno;
+        int     d_nblk;
 
         rw_enter(&bd_lock, RW_READER);
 
         part = BDPART(dev);
         inst = BDINST(dev);
 
         if ((bd = ddi_get_soft_state(bd_state, inst)) == NULL) {
                 rw_exit(&bd_lock);
                 return (ENXIO);
         }
+        shift = bd->d_blkshift;
+        d_blkno = blkno >> (shift - DEV_BSHIFT);
+        d_nblk = nblk >> (shift - DEV_BSHIFT);
         /*
          * do cmlb, but do it synchronously unless we already have the
          * partition (which we probably should.)
          */
         if (cmlb_partinfo(bd->d_cmlbh, part, &psize, &pstart, NULL, NULL,
             (void *)1)) {
                 rw_exit(&bd_lock);
                 return (ENXIO);
         }
 
-        if ((blkno + nblk) > psize) {
+        if ((d_blkno + d_nblk) > psize) {
                 rw_exit(&bd_lock);
                 return (EINVAL);
         }
         bp = getrbuf(KM_NOSLEEP);
         if (bp == NULL) {
                 rw_exit(&bd_lock);
                 return (ENOMEM);
         }
 
-        bp->b_bcount = nblk << bd->d_blkshift;
+        bp->b_bcount = nblk << DEV_BSHIFT;
         bp->b_resid = bp->b_bcount;
         bp->b_lblkno = blkno;
         bp->b_un.b_addr = caddr;
 
         xi = bd_xfer_alloc(bd, bp,  bd->d_ops.o_write, KM_NOSLEEP);
         if (xi == NULL) {
                 rw_exit(&bd_lock);
                 freerbuf(bp);
                 return (ENOMEM);
         }
-        xi->i_blkno = blkno + pstart;
+        xi->i_blkno = d_blkno + pstart;
         xi->i_flags = BD_XFER_POLL;
         bd_submit(bd, xi);
         rw_exit(&bd_lock);
 
         /*

@@ -1111,34 +1119,69 @@
         if (bp->b_bcount > bd->d_maxxfer)
                 bp->b_bcount = bd->d_maxxfer;
 }
 
 static int
+bd_check_uio(dev_t dev, struct uio *uio)
+{
+        bd_t            *bd;
+        uint32_t        shift;
+
+        if ((bd = ddi_get_soft_state(bd_state, BDINST(dev))) == NULL) {
+                return (ENXIO);
+        }
+
+        shift = bd->d_blkshift;
+        if ((P2PHASE(uio->uio_loffset, (1U << shift)) != 0) ||
+            (P2PHASE(uio->uio_iov->iov_len, (1U << shift)) != 0)) {
+                return (EINVAL);
+        }
+
+        return (0);
+}
+
+static int
 bd_read(dev_t dev, struct uio *uio, cred_t *credp)
 {
         _NOTE(ARGUNUSED(credp));
+        int     ret = bd_check_uio(dev, uio);
+        if (ret != 0) {
+                return (ret);
+        }
         return (physio(bd_strategy, NULL, dev, B_READ, bd_minphys, uio));
 }
 
 static int
 bd_write(dev_t dev, struct uio *uio, cred_t *credp)
 {
         _NOTE(ARGUNUSED(credp));
+        int     ret = bd_check_uio(dev, uio);
+        if (ret != 0) {
+                return (ret);
+        }
         return (physio(bd_strategy, NULL, dev, B_WRITE, bd_minphys, uio));
 }
 
 static int
 bd_aread(dev_t dev, struct aio_req *aio, cred_t *credp)
 {
         _NOTE(ARGUNUSED(credp));
+        int     ret = bd_check_uio(dev, aio->aio_uio);
+        if (ret != 0) {
+                return (ret);
+        }
         return (aphysio(bd_strategy, anocancel, dev, B_READ, bd_minphys, aio));
 }
 
 static int
 bd_awrite(dev_t dev, struct aio_req *aio, cred_t *credp)
 {
         _NOTE(ARGUNUSED(credp));
+        int     ret = bd_check_uio(dev, aio->aio_uio);
+        if (ret != 0) {
+                return (ret);
+        }
         return (aphysio(bd_strategy, anocancel, dev, B_WRITE, bd_minphys, aio));
 }
 
 static int
 bd_strategy(struct buf *bp)

@@ -1150,10 +1193,11 @@
         diskaddr_t      p_nblks;
         diskaddr_t      b_nblks;
         bd_xfer_impl_t  *xi;
         uint32_t        shift;
         int             (*func)(void *, bd_xfer_t *);
+        diskaddr_t      lblkno;
 
         part = BDPART(bp->b_edev);
         inst = BDINST(bp->b_edev);
 
         ASSERT(bp);

@@ -1172,25 +1216,26 @@
                 biodone(bp);
                 return (0);
         }
 
         shift = bd->d_blkshift;
-
-        if ((P2PHASE(bp->b_bcount, (1U << shift)) != 0) ||
-            (bp->b_lblkno > p_nblks)) {
-                bioerror(bp, ENXIO);
+        lblkno = bp->b_lblkno >> (shift - DEV_BSHIFT);
+        if ((P2PHASE(bp->b_lblkno, (1U << (shift - DEV_BSHIFT))) != 0) ||
+            (P2PHASE(bp->b_bcount, (1U << shift)) != 0) ||
+            (lblkno > p_nblks)) {
+                bioerror(bp, EINVAL);
                 biodone(bp);
                 return (0);
         }
         b_nblks = bp->b_bcount >> shift;
-        if ((bp->b_lblkno == p_nblks) || (bp->b_bcount == 0)) {
+        if ((lblkno == p_nblks) || (bp->b_bcount == 0)) {
                 biodone(bp);
                 return (0);
         }
 
-        if ((b_nblks + bp->b_lblkno) > p_nblks) {
-                bp->b_resid = ((bp->b_lblkno + b_nblks - p_nblks) << shift);
+        if ((b_nblks + lblkno) > p_nblks) {
+                bp->b_resid = ((lblkno + b_nblks - p_nblks) << shift);
                 bp->b_bcount -= bp->b_resid;
         } else {
                 bp->b_resid = 0;
         }
         func = (bp->b_flags & B_READ) ? bd->d_ops.o_read : bd->d_ops.o_write;

@@ -1202,11 +1247,11 @@
         if (xi == NULL) {
                 /* bd_request_alloc will have done bioerror */
                 biodone(bp);
                 return (0);
         }
-        xi->i_blkno = bp->b_lblkno + p_lba;
+        xi->i_blkno = lblkno + p_lba;
 
         bd_submit(bd, xi);
 
         return (0);
 }

@@ -1903,11 +1948,11 @@
                 len = min(bp->b_bcount - xi->i_offset, bd->d_maxxfer);
         }
 
 
         if ((rv != DDI_SUCCESS) ||
-            (P2PHASE(len, (1U << xi->i_blkshift) != 0))) {
+            (P2PHASE(len, (1U << xi->i_blkshift)) != 0)) {
                 bd_runq_exit(xi, EFAULT);
 
                 bp->b_resid += xi->i_resid;
                 bd_xfer_free(xi);
                 bioerror(bp, EFAULT);