]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
oracleasm: Data integrity support
authorMartin K. Petersen <martin.petersen@oracle.com>
Thu, 14 Jun 2012 05:44:07 +0000 (01:44 -0400)
committerMartin K. Petersen <martin.petersen@oracle.com>
Thu, 14 Jun 2012 05:44:07 +0000 (01:44 -0400)
Add data integrity support to the oracleasm driver.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/block/oracleasm/Makefile
drivers/block/oracleasm/driver.c
drivers/block/oracleasm/integrity.c [new file with mode: 0644]
drivers/block/oracleasm/integrity.h [new file with mode: 0644]
include/linux/oracleasm/Kbuild [new file with mode: 0644]
include/linux/oracleasm/abi.h
include/linux/oracleasm/error.h
include/linux/oracleasm/module_version.h

index a71ddd958fd1dc8bd4cb9c708ed412b57bb74820..edac43fe191cd1bd4cc24bcbb8bd2182548da1bd 100644 (file)
@@ -1,3 +1,5 @@
 
 obj-$(CONFIG_ORACLEASM) := oracleasm.o
 oracleasm-y += driver.o  masklog.o  proc.o  transaction_file.o
+oracleasm-$(CONFIG_BLK_DEV_INTEGRITY) += integrity.o
+
index 39b6c918a40d43191ea64ab961c3fe045be9ef48..5d7f2a177950d7591c9f8905860276c4558f3d5e 100644 (file)
@@ -13,6 +13,8 @@
  *      required to support the userspace library.
  *
  * MODIFIED   (YYYY/MM/DD)
+ *     2008/09/01 - Martin K. Petersen <martin.petersen@oracle.com>
+ *             Data integrity changes.
  *      2004/01/02 - Joel Becker <joel.becker@oracle.com>
  *             Initial GPL header.
  *      2004/09/10 - Joel Becker <joel.becker@oracle.com>
@@ -85,6 +87,7 @@
 #include "proc.h"
 #include "transaction_file.h"
 #include "request.h"
+#include "integrity.h"
 
 #if PAGE_CACHE_SIZE % 1024
 #error Oh no, PAGE_CACHE_SIZE is not divisible by 1k! I cannot cope.
@@ -1150,6 +1153,10 @@ static void asm_end_ioc(struct asm_request *r, unsigned int bytes_done,
                        r->r_error = ASM_ERR_IO;
                        break;
 
+               case -EILSEQ:
+                       r->r_error = asm_integrity_error(r);
+                       break;
+
                case -ENODEV:
                        r->r_error = ASM_ERR_NODEV;
                        r->r_status |= ASM_LOCAL_ERROR;
@@ -1204,6 +1211,7 @@ static int asm_submit_io(struct file *file,
        struct asm_disk_info *d;
        struct inode *disk_inode;
        struct block_device *bdev;
+       struct oracleasm_integrity_v2 *it;
 
        mlog_entry("(0x%p, 0x%p, 0x%p)\n", file, user_iocp, ioc);
 
@@ -1246,7 +1254,8 @@ static int asm_submit_io(struct file *file,
        spin_unlock_irq(&ASMFS_FILE(file)->f_lock);
 
        ret = -ENODEV;
-       args.fa_handle = (unsigned long)ioc->disk_asm_ioc;
+       args.fa_handle = (unsigned long)ioc->disk_asm_ioc &
+               ~ASM_INTEGRITY_HANDLE_MASK;
        args.fa_inode = ASMFS_I(inode);
        disk_inode = ilookup5(asmdisk_mnt->mnt_sb,
                              (unsigned long)args.fa_handle,
@@ -1318,6 +1327,11 @@ static int asm_submit_io(struct file *file,
             "Request 0x%p (user_ioc 0x%p) passed validation checks\n",
             r, user_iocp);
 
+       if (bdev_get_integrity(bdev))
+               it = (struct oracleasm_integrity_v2 *)ioc->check_asm_ioc;
+       else
+               it = NULL;
+
        switch (ioc->operation_asm_ioc) {
                default:
                        goto out_error;
@@ -1325,10 +1339,18 @@ static int asm_submit_io(struct file *file,
 
                case ASM_READ:
                        rw = READ;
+
+                       if (it && asm_integrity_check(it, bdev) < 0)
+                               goto out_error;
+
                        break;
 
                case ASM_WRITE:
                        rw = WRITE;
+
+                       if (it && asm_integrity_check(it, bdev) < 0)
+                               goto out_error;
+
                        break;
 
                case ASM_NOOP:
@@ -1368,6 +1390,17 @@ static int asm_submit_io(struct file *file,
        r->r_bio->bi_sector = ioc->first_asm_ioc *
                (bdev_physical_block_size(bdev) >> 9);
 
+       if (it) {
+               ret = asm_integrity_map(it, r, rw == READ);
+
+               if (ret < 0) {
+                       mlog(ML_ERROR|ML_BIO,
+                            "Could not attach integrity payload\n");
+                       bio_unmap_user(r->r_bio);
+                       goto out_error;
+               }
+       }
+
        /*
         * If the bio is a bounced bio, we have to put the
         * end_io on the child "real" bio
@@ -2012,6 +2045,7 @@ static void asm_cleanup_bios(struct file *file)
 
                spin_unlock_irq(&afi->f_lock);
                mlog(ML_BIO, "Unmapping bio 0x%p\n", bio);
+               asm_integrity_unmap(bio);
                bio_unmap_user(bio);
                spin_lock_irq(&afi->f_lock);
        }
@@ -2329,10 +2363,13 @@ static ssize_t asmfs_svc_query_disk(struct file *file, char *buf, size_t size)
 
        qd_info->qd_max_sectors = compute_max_sectors(bdev);
        qd_info->qd_hardsect_size = bdev_physical_block_size(bdev);
+       qd_info->qd_feature = asm_integrity_format(bdev) &
+               ASM_INTEGRITY_QDF_MASK;
        mlog(ML_ABI|ML_DISK,
             "Querydisk returning qd_max_sectors = %u and "
-            "qd_hardsect_size = %d\n",
-            qd_info->qd_max_sectors, qd_info->qd_hardsect_size);
+            "qd_hardsect_size = %u, qd_integrity = %u\n",
+            qd_info->qd_max_sectors, qd_info->qd_hardsect_size,
+            asm_integrity_format(bdev));
 
        ret = 0;
 
diff --git a/drivers/block/oracleasm/integrity.c b/drivers/block/oracleasm/integrity.c
new file mode 100644 (file)
index 0000000..c5f7368
--- /dev/null
@@ -0,0 +1,220 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * NAME
+ *     integrity.c - ASM block layer data integrity support.
+ *
+ * AUTHOR
+ *     Martin K. Petersen <martin.petersen@oracle.com>
+ *
+ * MODIFIED   (YYYY/MM/DD)
+ *     2010/04/07 Martin K. Petersen <martin.petersen@oracle.com>
+ *             App tag checking
+ *     2009/11/04 - Martin K. Petersen <martin.petersen@oracle.com>
+ *             Support for 4KB/4KB and 512/4KB formats.
+ *     2009/01/06 - Martin K. Petersen <martin.petersen@oracle.com>
+ *             Moved into a separate file so we can compile on older
+ *             kernels.
+ *     2008/09/01 - Martin K. Petersen <martin.petersen@oracle.com>
+ *             Data integrity changes.
+ *
+ * Copyright (c) 2008-2010 Oracle.  All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License, version 2 as published by the Free Software Foundation.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/pagemap.h>
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/compat.h>
+
+#include "linux/oracleasm/compat32.h"
+#include "linux/oracleasm/kernel.h"
+#include "linux/oracleasm/abi.h"
+#include "linux/oracleasm/disk.h"
+#include "linux/oracleasm/error.h"
+
+#include "request.h"
+#include "masklog.h"
+#include "integrity.h"
+
+u32 asm_integrity_format(struct block_device *bdev)
+{
+       struct blk_integrity *bi = bdev_get_integrity(bdev);
+       unsigned int lbs = bdev_logical_block_size(bdev);
+       unsigned int pbs = bdev_physical_block_size(bdev);
+       unsigned int format = 0;
+
+       if (!bi)
+               return 0;
+
+       if (lbs == 512 && pbs == 512)
+               format = ASM_IMODE_512_512;
+       else if (lbs == 512 && pbs == 4096)
+               format = ASM_IMODE_512_4K;
+       else if (lbs == 4096 && pbs == 4096)
+               format = ASM_IMODE_4K_4K;
+       else
+               return 0;
+
+       if (!strcmp(bi->name, "T10-DIF-TYPE1-CRC"))
+               return format;
+
+       if (!strcmp(bi->name, "T10-DIF-TYPE1-IP"))
+               return format | ASM_IFMT_IP_CHECKSUM;
+
+       return 0;
+} /* asm_integrity_format */
+
+
+int asm_integrity_check(struct oracleasm_integrity_v2 *it, struct block_device *bdev)
+{
+       unsigned int dev_format;
+
+       /* Strip feature flags */
+       dev_format = asm_integrity_format(bdev) & ASM_INTEGRITY_HANDLE_MASK;
+
+       if (!dev_format)
+               return 0;
+
+       if (it->it_magic != ASM_INTEGRITY_MAGIC) {
+               mlog(ML_ERROR|ML_IOC, "IOC integrity: Bad magic...\n");
+               return -EINVAL;
+       }
+
+       if (it->it_format != dev_format) {
+               mlog(ML_ERROR|ML_IOC,
+                    "IOC integrity: incorrect format for %s (%u != %u)\n",
+                    bdev->bd_disk->disk_name, it->it_format, dev_format);
+               return -EINVAL;
+       }
+
+       if (it->it_bytes == 0) {
+               mlog(ML_ERROR|ML_IOC,
+                    "IOC integrity: zero integrity buffer length\n");
+               return -EINVAL;
+       }
+
+       if (it->it_buf == 0) {
+               mlog(ML_ERROR|ML_IOC,
+                    "IOC integrity: NULL integrity buffer\n");
+               return -EINVAL;
+       }
+
+       return 0;
+} /* asm_integrity_check */
+
+
+int asm_integrity_map(struct oracleasm_integrity_v2 *it, struct asm_request *r, int write_to_vm)
+{
+       int len = it->it_bytes;
+       unsigned long uaddr = (unsigned long)it->it_buf;
+       unsigned long end = (uaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       unsigned long start = uaddr >> PAGE_SHIFT;
+       unsigned int nr_pages = end - start;
+       unsigned int offset;
+       int i, ret;
+       struct bio *bio = r->r_bio;
+       struct bio_integrity_payload *bip;
+       struct page **pages;
+
+       ret = 0;
+
+       if (nr_pages < 1) {
+               mlog(ML_ERROR, "%s: nr_pages < 1\n", __func__);
+               return -EINVAL;
+       }
+
+       bip = bio_integrity_alloc(bio, GFP_NOIO, nr_pages);
+       if (!bip) {
+               mlog(ML_ERROR, "%s: could not allocate bip\n", __func__);
+               return -ENOMEM;
+       }
+
+       bip->bip_size = len;
+       bip->bip_sector = bio->bi_sector;
+       bio->bi_flags |= (1 << BIO_FS_INTEGRITY);
+
+       /* This is a retry. Prevent reference tag from being remapped again */
+       if (it->it_flags & ASM_IFLAG_REMAPPED)
+               bio->bi_flags |= 1 << BIO_MAPPED_INTEGRITY;
+
+       pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);
+       if (!pages) {
+               mlog(ML_ERROR, "%s: could not allocate page array\n", __func__);
+               return -ENOMEM;
+       }
+
+       ret = get_user_pages_fast(uaddr, nr_pages, write_to_vm, &pages[0]);
+       if (ret < nr_pages) {
+               mlog(ML_ERROR, "%s: could not get user pages\n", __func__);
+               kfree(pages);
+               return -EFAULT;
+       }
+
+       offset = offset_in_page(it->it_buf);
+       ret = 0;
+
+       for (i = 0 ; i < nr_pages ; i++) {
+               unsigned int bytes = PAGE_SIZE - offset;
+               unsigned int added;
+
+               if (len <= 0)
+                       break;
+
+               if (bytes > len)
+                       bytes = len;
+
+               added = bio_integrity_add_page(bio, pages[i], bytes, offset);
+
+               if (added < bytes) {
+                       ret = -1;
+                       mlog(ML_ERROR, "%s: bio %p added %u bytes, wanted %u\n",
+                            __func__, bio, added, bytes);
+                       break;
+               }
+
+               len -= bytes;
+               offset = 0;
+       }
+
+       while (i < nr_pages)
+               page_cache_release(pages[i++]);
+
+       kfree(pages);
+
+       if (bio->bi_integrity->bip_vcnt == 0)
+               ret = -EINVAL;
+
+       return ret;
+} /* asm_integrity_map */
+
+
+void asm_integrity_unmap(struct bio *bio)
+{
+       struct bio_vec *iv;
+       unsigned int i;
+
+       if (!bio_flagged(bio, BIO_FS_INTEGRITY))
+               return;
+
+       bip_for_each_vec(iv, bio->bi_integrity, i) {
+               if (bio_data_dir(bio) == READ)
+                       set_page_dirty_lock(iv->bv_page);
+
+               page_cache_release(iv->bv_page);
+       }
+} /* asm_integrity_unmap */
+
+
+unsigned int asm_integrity_error(struct asm_request *r)
+{
+       return ASM_ERR_INTEGRITY;
+}
diff --git a/drivers/block/oracleasm/integrity.h b/drivers/block/oracleasm/integrity.h
new file mode 100644 (file)
index 0000000..9772909
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef ASM_INTEGRITY_H
+#define ASM_INTEGRITY_H
+
+#if defined(CONFIG_BLK_DEV_INTEGRITY)
+
+extern u32  asm_integrity_format(struct block_device *);
+extern int  asm_integrity_check(struct oracleasm_integrity_v2 *, struct block_device *);
+extern int  asm_integrity_map(struct oracleasm_integrity_v2 *, struct asm_request *, int);
+extern void asm_integrity_unmap(struct bio *);
+extern unsigned int asm_integrity_error(struct asm_request *);
+
+#else  /* CONFIG_BLK_DEV_INTEGRITY */
+
+#define asm_integrity_format(a)                (0)
+#define asm_integrity_check(a, b)      (0)
+#define asm_integrity_map(a, b, c)     (0)
+#define asm_integrity_unmap(a)         do { } while (0)
+#define asm_integrity_error(a)         (ASM_ERR_IO)
+
+#endif /* CONFIG_BLK_DEV_INTEGRITY */
+
+#endif
diff --git a/include/linux/oracleasm/Kbuild b/include/linux/oracleasm/Kbuild
new file mode 100644 (file)
index 0000000..dcbb036
--- /dev/null
@@ -0,0 +1,9 @@
+header-y += abi.h
+header-y += abi_compat.h
+header-y += compat32.h
+header-y += disk.h
+header-y += error.h
+header-y += kernel.h
+header-y += manager.h
+header-y += manager_compat.h
+header-y += module_version.h
index 702fa516c087d2acf0c1ca840fdc00ed70406c89..3de3abd0c38e3c28b7a8384f8df10e11c63549df 100644 (file)
  * Defines
  */
 
-#define ASM_ABI_VERSION_V2     2UL
-#define ASM_ABI_VERSION                ASM_ABI_VERSION_V2
+#define ASM_ABI_VERSION_V2             2UL
+#define ASM_ABI_VERSION                        ASM_ABI_VERSION_V2
 
 enum asm_abi_magic {
-       ASMFS_MAGIC             = 0x958459f6,
-       ASM_ABI_MAGIC           = 0x41534DU,
+       ASMFS_MAGIC                     = 0x958459f6,
+       ASM_ABI_MAGIC                   = 0x41534DU,
+       ASM_INTEGRITY_MAGIC             = 0x444958,
+       ASM_INTEGRITY_TAG               = 0x4F52,
 };
 
 /*
@@ -115,19 +117,17 @@ enum asm_operation_types
 # endif  /* BITS_PER_LONG == 64 */
 #endif  /* BITS_PER_LONG == 32 */
 
-
-
 /*
  * Structures
  */
 
 struct oracleasm_abi_info
 {
-/*00*/ __u32           ai_magic;       /* ASM_ABI_MAGIC */
-       __u16           ai_version;     /* ABI version */
-       __u16           ai_type;        /* Type of operation */
-       __u32           ai_size;        /* Size of passed structure */
-       __u32           ai_status;      /* Did it succeed */
+/*00*/ __u32                           ai_magic;       /* ASM_ABI_MAGIC */
+       __u16                           ai_version;     /* ABI version */
+       __u16                           ai_type;        /* Type of operation */
+       __u32                           ai_size;        /* Size of passed struct */
+       __u32                           ai_status;      /* Did it succeed */
 /*10*/ 
 };
 
@@ -150,16 +150,41 @@ struct oracleasm_io_v2
 /*50*/
 };
 
+struct oracleasm_integrity_v2
+{
+       __u32                           it_magic;
+       __u8                            it_format;
+       __u8                            it_flags;
+       __u16                           it_bytes;
+       __u64                           it_buf;
+};
+
+enum oracleasm_integrity_handling_flags {
+       ASM_IFLAG_REMAPPED              = 1,    /* PI has been remapped */
+       ASM_IFLAG_IP_CHECKSUM           = 2,    /* IP checksum instead of CRC */
+};
+
 struct oracleasm_query_disk_v2
 {
 /*00*/ struct oracleasm_abi_info       qd_abi;
 /*10*/ __u32                           qd_fd;
        __u32                           qd_max_sectors;
        __u32                           qd_hardsect_size;
-       __u32                           qd_pad1;        /* Pad to 64bit aligned size */
+       __u32                           qd_feature;
 /*20*/
 };
 
+enum oracleasm_feature_integrity {
+       ASM_IMODE_NONE                  = 0,    /* 00: No data integrity */
+       ASM_IMODE_512_512               = 1,    /* 01: lbs = 512, asmbs = 512 */
+       ASM_IMODE_512_4K                = 2,    /* 02: lbs = 512, asmbs = 4KB */
+       ASM_IMODE_4K_4K                 = 3,    /* 03: lbs = 4KB, asmbs = 4KB */
+       ASM_IMODE_MASK                  = 3,    /* Interleaving mode mask */
+       ASM_IFMT_IP_CHECKSUM            = 4,    /* 0: T10 CRC, 1: IP checksum */
+       ASM_INTEGRITY_HANDLE_MASK       = 7,    /* Integrity handle mask */
+       ASM_INTEGRITY_QDF_MASK          = 0xff, /* Querydisk feature mask */
+};
+
 struct oracleasm_open_disk_v2
 {
 /*00*/ struct oracleasm_abi_info       od_abi;
index 4891d330f85486b7e171055ee284cacb6c71697a..1433978a70e419c0d5ed8fd6f87a944450b9e76f 100644 (file)
@@ -82,6 +82,7 @@ enum _ASMErrors
     ASM_ERR_IO          = 3,    /* I/O error */
     ASM_ERR_DSCVR       = 4,    /* Bad discovery string */
     ASM_ERR_NODEV       = 5,    /* Invalid device */
+    ASM_ERR_INTEGRITY  = 6,    /* Data integrity error */
 };
 
 #endif  /* _ORACLEASM_ERROR_H */
index 063c0a15eca7a9ab243536428291cc184aeeb87a..617831300dba6ef02c47f1117c36179ae592df42 100644 (file)
@@ -25,7 +25,7 @@
 #ifndef _ASM_MODULE_VERSION_H
 #define _ASM_MODULE_VERSION_H
 
-#define ASM_MODULE_VERSION "2.0.7"
+#define ASM_MODULE_VERSION "2.0.8"
 
 #endif  /* _ASM_MODULE_VERSION */