From: Martin K. Petersen Date: Thu, 14 Jun 2012 05:44:07 +0000 (-0400) Subject: oracleasm: Data integrity support X-Git-Tag: v2.6.39-400.9.0~516^2~18^2~2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=b25990f3ecd4c588b048dc04a6ccafbbcbe3b36a;p=users%2Fjedix%2Flinux-maple.git oracleasm: Data integrity support Add data integrity support to the oracleasm driver. Signed-off-by: Martin K. Petersen --- diff --git a/drivers/block/oracleasm/Makefile b/drivers/block/oracleasm/Makefile index a71ddd958fd1..edac43fe191c 100644 --- a/drivers/block/oracleasm/Makefile +++ b/drivers/block/oracleasm/Makefile @@ -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 + diff --git a/drivers/block/oracleasm/driver.c b/drivers/block/oracleasm/driver.c index 39b6c918a40d..5d7f2a177950 100644 --- a/drivers/block/oracleasm/driver.c +++ b/drivers/block/oracleasm/driver.c @@ -13,6 +13,8 @@ * required to support the userspace library. * * MODIFIED (YYYY/MM/DD) + * 2008/09/01 - Martin K. Petersen + * Data integrity changes. * 2004/01/02 - Joel Becker * Initial GPL header. * 2004/09/10 - Joel Becker @@ -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 index 000000000000..c5f7368a3bb0 --- /dev/null +++ b/drivers/block/oracleasm/integrity.c @@ -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 + * + * MODIFIED (YYYY/MM/DD) + * 2010/04/07 Martin K. Petersen + * App tag checking + * 2009/11/04 - Martin K. Petersen + * Support for 4KB/4KB and 512/4KB formats. + * 2009/01/06 - Martin K. Petersen + * Moved into a separate file so we can compile on older + * kernels. + * 2008/09/01 - Martin K. Petersen + * 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 +#include +#include +#include + +#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 index 000000000000..977290939b8b --- /dev/null +++ b/drivers/block/oracleasm/integrity.h @@ -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 index 000000000000..dcbb036fa2d3 --- /dev/null +++ b/include/linux/oracleasm/Kbuild @@ -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 diff --git a/include/linux/oracleasm/abi.h b/include/linux/oracleasm/abi.h index 702fa516c087..3de3abd0c38e 100644 --- a/include/linux/oracleasm/abi.h +++ b/include/linux/oracleasm/abi.h @@ -77,12 +77,14 @@ * 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; diff --git a/include/linux/oracleasm/error.h b/include/linux/oracleasm/error.h index 4891d330f854..1433978a70e4 100644 --- a/include/linux/oracleasm/error.h +++ b/include/linux/oracleasm/error.h @@ -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 */ diff --git a/include/linux/oracleasm/module_version.h b/include/linux/oracleasm/module_version.h index 063c0a15eca7..617831300dba 100644 --- a/include/linux/oracleasm/module_version.h +++ b/include/linux/oracleasm/module_version.h @@ -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 */