timers: Lock base for same bucket optimization
[cascardo/linux.git] / fs / nfs / blocklayout / dev.c
index e5b8967..a69ef4e 100644 (file)
@@ -65,8 +65,8 @@ nfs4_block_decode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b)
                if (!p)
                        return -EIO;
                b->simple.nr_sigs = be32_to_cpup(p++);
-               if (!b->simple.nr_sigs) {
-                       dprintk("no signature\n");
+               if (!b->simple.nr_sigs || b->simple.nr_sigs > PNFS_BLOCK_MAX_UUIDS) {
+                       dprintk("Bad signature count: %d\n", b->simple.nr_sigs);
                        return -EIO;
                }
 
@@ -89,7 +89,8 @@ nfs4_block_decode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b)
                        memcpy(&b->simple.sigs[i].sig, p,
                                b->simple.sigs[i].sig_len);
 
-                       b->simple.len += 8 + 4 + b->simple.sigs[i].sig_len;
+                       b->simple.len += 8 + 4 + \
+                               (XDR_QUADLEN(b->simple.sigs[i].sig_len) << 2);
                }
                break;
        case PNFS_BLOCK_VOLUME_SLICE:
@@ -104,7 +105,12 @@ nfs4_block_decode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b)
                p = xdr_inline_decode(xdr, 4);
                if (!p)
                        return -EIO;
+
                b->concat.volumes_count = be32_to_cpup(p++);
+               if (b->concat.volumes_count > PNFS_BLOCK_MAX_DEVICES) {
+                       dprintk("Too many volumes: %d\n", b->concat.volumes_count);
+                       return -EIO;
+               }
 
                p = xdr_inline_decode(xdr, b->concat.volumes_count * 4);
                if (!p)
@@ -116,8 +122,13 @@ nfs4_block_decode_volume(struct xdr_stream *xdr, struct pnfs_block_volume *b)
                p = xdr_inline_decode(xdr, 8 + 4);
                if (!p)
                        return -EIO;
+
                p = xdr_decode_hyper(p, &b->stripe.chunk_size);
                b->stripe.volumes_count = be32_to_cpup(p++);
+               if (b->stripe.volumes_count > PNFS_BLOCK_MAX_DEVICES) {
+                       dprintk("Too many volumes: %d\n", b->stripe.volumes_count);
+                       return -EIO;
+               }
 
                p = xdr_inline_decode(xdr, b->stripe.volumes_count * 4);
                if (!p)
@@ -224,18 +235,20 @@ bl_parse_simple(struct nfs_server *server, struct pnfs_block_dev *d,
                struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
 {
        struct pnfs_block_volume *v = &volumes[idx];
+       struct block_device *bdev;
        dev_t dev;
 
        dev = bl_resolve_deviceid(server, v, gfp_mask);
        if (!dev)
                return -EIO;
 
-       d->bdev = blkdev_get_by_dev(dev, FMODE_READ | FMODE_WRITE, NULL);
-       if (IS_ERR(d->bdev)) {
+       bdev = blkdev_get_by_dev(dev, FMODE_READ | FMODE_WRITE, NULL);
+       if (IS_ERR(bdev)) {
                printk(KERN_WARNING "pNFS: failed to open device %d:%d (%ld)\n",
-                       MAJOR(dev), MINOR(dev), PTR_ERR(d->bdev));
-               return PTR_ERR(d->bdev);
+                       MAJOR(dev), MINOR(dev), PTR_ERR(bdev));
+               return PTR_ERR(bdev);
        }
+       d->bdev = bdev;
 
 
        d->len = i_size_read(d->bdev->bd_inode);
@@ -287,44 +300,71 @@ bl_validate_designator(struct pnfs_block_volume *v)
        }
 }
 
+/*
+ * Try to open the udev path for the WWN.  At least on Debian the udev
+ * by-id path will always point to the dm-multipath device if one exists.
+ */
+static struct block_device *
+bl_open_udev_path(struct pnfs_block_volume *v)
+{
+       struct block_device *bdev;
+       const char *devname;
+
+       devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/wwn-0x%*phN",
+                               v->scsi.designator_len, v->scsi.designator);
+       if (!devname)
+               return ERR_PTR(-ENOMEM);
+
+       bdev = blkdev_get_by_path(devname, FMODE_READ | FMODE_WRITE, NULL);
+       if (IS_ERR(bdev)) {
+               pr_warn("pNFS: failed to open device %s (%ld)\n",
+                       devname, PTR_ERR(bdev));
+       }
+
+       kfree(devname);
+       return bdev;
+}
+
+/*
+ * Try to open the RH/Fedora specific dm-mpath udev path for this WWN, as the
+ * wwn- links will only point to the first discovered SCSI device there.
+ */
+static struct block_device *
+bl_open_dm_mpath_udev_path(struct pnfs_block_volume *v)
+{
+       struct block_device *bdev;
+       const char *devname;
+
+       devname = kasprintf(GFP_KERNEL,
+                       "/dev/disk/by-id/dm-uuid-mpath-%d%*phN",
+                       v->scsi.designator_type,
+                       v->scsi.designator_len, v->scsi.designator);
+       if (!devname)
+               return ERR_PTR(-ENOMEM);
+
+       bdev = blkdev_get_by_path(devname, FMODE_READ | FMODE_WRITE, NULL);
+       kfree(devname);
+       return bdev;
+}
+
 static int
 bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d,
                struct pnfs_block_volume *volumes, int idx, gfp_t gfp_mask)
 {
        struct pnfs_block_volume *v = &volumes[idx];
+       struct block_device *bdev;
        const struct pr_ops *ops;
-       const char *devname;
        int error;
 
        if (!bl_validate_designator(v))
                return -EINVAL;
 
-       switch (v->scsi.designator_len) {
-       case 8:
-               devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/wwn-0x%8phN",
-                               v->scsi.designator);
-               break;
-       case 12:
-               devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/wwn-0x%12phN",
-                               v->scsi.designator);
-               break;
-       case 16:
-               devname = kasprintf(GFP_KERNEL, "/dev/disk/by-id/wwn-0x%16phN",
-                               v->scsi.designator);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       d->bdev = blkdev_get_by_path(devname, FMODE_READ, NULL);
-       if (IS_ERR(d->bdev)) {
-               pr_warn("pNFS: failed to open device %s (%ld)\n",
-                       devname, PTR_ERR(d->bdev));
-               kfree(devname);
-               return PTR_ERR(d->bdev);
-       }
-
-       kfree(devname);
+       bdev = bl_open_dm_mpath_udev_path(v);
+       if (IS_ERR(bdev))
+               bdev = bl_open_udev_path(v);
+       if (IS_ERR(bdev))
+               return PTR_ERR(bdev);
+       d->bdev = bdev;
 
        d->len = i_size_read(d->bdev->bd_inode);
        d->map = bl_map_simple;
@@ -352,7 +392,7 @@ bl_parse_scsi(struct nfs_server *server, struct pnfs_block_dev *d,
        return 0;
 
 out_blkdev_put:
-       blkdev_put(d->bdev, FMODE_READ);
+       blkdev_put(d->bdev, FMODE_READ | FMODE_WRITE);
        return error;
 }