Add data integrity support to the oracleasm driver.
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
obj-$(CONFIG_ORACLEASM) := oracleasm.o
oracleasm-y += driver.o masklog.o proc.o transaction_file.o
+oracleasm-$(CONFIG_BLK_DEV_INTEGRITY) += integrity.o
+
* 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>
#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.
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;
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);
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,
"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;
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:
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
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);
}
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;
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+#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
--- /dev/null
+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
* 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,
};
/*
# 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*/
};
/*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;
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 */
#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 */