From: "Martin K. Petersen" <martin.petersen@oracle.com>
Date: Fri, 26 Oct 2018 18:07:53 -0400
Subject: oracleasm: Implement support for QUERY HANDLE operation
Git-commit: 49b40ac30fc6cd4837d3878e5ec78bb7dbf3e72e
Patch-mainline: v5.4.0-1948.2

ASMLib previously relied on tagging the disk handle pointer to store
the integrity format. This had the advantage that a simple masking
operation was all that was required to get from a handle to the
integrity information.

However, we have seen a few cases where it appears the disk handle has
been corrupted in userland post discovery. Consequently, it has proven
necessary to be able to query disk properties without relying on the
disk pointer tag.

The oracleasm driver currently only supports looking up disk by their
label string via the query_disk operation.  Implement a query_handle
operation that is similar to query_disk in that it returns all known
disk properties. However, the lookup is done by disk handle instead of
device node. Otherwise the two queries are identical.

Adding the new operation to oracleasm does not prevent older versions
of the library from working correctly. The old tagging mechanism is
still in place, the use of query_handle is entirely optional. Later
ASMLib versions will use the new mode of operation if the query_handle
transaction file appears in /dev/oracleasm.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Acked-by: Lee Duncan <lduncan@suse.com>
---
 drivers/block/oracleasm/driver.c  | 86 +++++++++++++++++++++++++++++++++++++++
 drivers/block/oracleasm/trace.h   | 32 +++++++++++++++
 include/linux/oracleasm/abi.h     | 10 +++++
 include/linux/oracleasm/manager.h |  3 +-
 4 files changed, 130 insertions(+), 1 deletion(-)

diff --git a/drivers/block/oracleasm/driver.c b/drivers/block/oracleasm/driver.c
index e1696ef9bfe8..d5fd119726a8 100644
--- a/drivers/block/oracleasm/driver.c
+++ b/drivers/block/oracleasm/driver.c
@@ -253,6 +253,7 @@ static ssize_t asmfs_svc_query_version(struct file *file, char *buf, size_t size
 static ssize_t asmfs_svc_get_iid(struct file *file, char *buf, size_t size);
 static ssize_t asmfs_svc_check_iid(struct file *file, char *buf, size_t size);
 static ssize_t asmfs_svc_query_disk(struct file *file, char *buf, size_t size);
+static ssize_t asmfs_svc_query_handle(struct file *file, char *buf, size_t size);
 static ssize_t asmfs_svc_open_disk(struct file *file, char *buf, size_t size);
 static ssize_t asmfs_svc_close_disk(struct file *file, char *buf, size_t size);
 static ssize_t asmfs_svc_io32(struct file *file, char *buf, size_t size);
@@ -271,6 +272,7 @@ static struct transaction_context trans_contexts[] = {
 #if BITS_PER_LONG == 64
 	[ASMOP_IO64]			= {asmfs_svc_io64},
 #endif
+	[ASMOP_QUERY_HANDLE]		= {asmfs_svc_query_handle},
 };
 
 static struct inode *asmdisk_alloc_inode(struct super_block *sb)
@@ -387,6 +389,15 @@ static int asmdisk_test(struct inode *inode, void *data)
 	return (d->d_inode == args->fa_inode) && (handle == args->fa_handle);
 }
 
+static int asmdisk_test_noinode(struct inode *inode, void *data)
+{
+	struct asmdisk_find_inode_args *args = data;
+	struct asm_disk_info *d = ASMDISK_I(inode);
+	unsigned long handle = (unsigned long)(d->d_bdev);
+
+	return handle == args->fa_handle;
+}
+
 static int asmdisk_set(struct inode *inode, void *data)
 {
 	struct asmdisk_find_inode_args *args = data;
@@ -2328,6 +2339,64 @@ static ssize_t asmfs_svc_io64(struct file *file, char *buf, size_t size)
 #endif  /* BITS_PER_LONG == 64 */
 
 
+static ssize_t asmfs_svc_query_handle(struct file *file, char *buf, size_t size)
+{
+	struct oracleasm_query_handle_v2 *qh_info;
+	struct asmdisk_find_inode_args args;
+	struct inode *disk_inode;
+	struct asm_disk_info *d;
+	struct block_device *bdev;
+	unsigned int lsecsz = 0;
+	int ret;
+
+	if (size != sizeof(struct oracleasm_query_handle_v2))
+		return -EINVAL;
+
+	qh_info = (struct oracleasm_query_handle_v2 *)buf;
+
+	ret = asmfs_verify_abi(&qh_info->qh_abi);
+	if (ret)
+		goto out;
+
+	ret = -EBADR;
+	if (qh_info->qh_abi.ai_size != sizeof(struct oracleasm_query_handle_v2))
+		goto out;
+
+	ret = -EBADRQC;
+	if (qh_info->qh_abi.ai_type != ASMOP_QUERY_HANDLE)
+		goto out;
+
+	args.fa_handle = (unsigned long)qh_info->qh_handle;
+	args.fa_inode = 0;
+	disk_inode = ilookup5(asmdisk_mnt->mnt_sb, qh_info->qh_handle,
+			      asmdisk_test_noinode, &args);
+	if (!disk_inode) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	d = ASMDISK_I(disk_inode);
+	iput(disk_inode);
+	bdev = d->d_bdev;
+
+	qh_info->qh_max_sectors = compute_max_sectors(bdev);
+	qh_info->qh_hardsect_size = asm_block_size(bdev);
+	qh_info->qh_feature = asm_integrity_format(bdev) & ASM_INTEGRITY_QDF_MASK;
+	if (use_logical_block_size == false) {
+		lsecsz = ilog2(bdev_logical_block_size(bdev));
+		qh_info->qh_feature |= lsecsz << ASM_LSECSZ_SHIFT
+			& ASM_LSECSZ_MASK;
+	}
+
+	trace_queryhandle(bdev, qh_info);
+	ret = 0;
+
+out:
+	qh_info->qh_abi.ai_status = ret;
+	return size;
+}
+
+
 /*
  * Because each of these operations need to access the filp->private,
  * we must multiplex.
@@ -2368,11 +2437,16 @@ static ssize_t asmfs_file_read(struct file *file, char *buf, size_t size, loff_t
 			ret = asmfs_svc_io64(file, (char *)buf, size);
 			break;
 #endif  /* BITS_PER_LONG == 64 */
+
+		case ASMOP_QUERY_HANDLE:
+			ret = asmfs_svc_query_handle(file, (char *)buf, size);
+			break;
 	}
 
 	return ret;
 }
 
+
 static struct file_operations asmfs_file_operations = {
 	.open		= asmfs_file_open,
 	.release	= asmfs_file_release,
@@ -2544,6 +2618,18 @@ static int asmfs_fill_super(struct super_block *sb,
 		goto out_genocide;
 	d_add(dentry, inode);
 
+	name.name = asm_operation_files[ASMOP_QUERY_HANDLE];
+	name.len = strlen(asm_operation_files[ASMOP_QUERY_HANDLE]);
+	name.hash = full_name_hash(root, name.name, name.len);
+	dentry = d_alloc(root, &name);
+	if (!dentry)
+		goto out_genocide;
+	inode = new_transaction_inode(sb, 0770,
+				      &trans_contexts[ASMOP_QUERY_HANDLE]);
+	if (!inode)
+		goto out_genocide;
+	d_add(dentry, inode);
+
 	sb->s_root = root;
 
 
diff --git a/drivers/block/oracleasm/trace.h b/drivers/block/oracleasm/trace.h
index 9613c6b8eeb7..e882e47fedb2 100644
--- a/drivers/block/oracleasm/trace.h
+++ b/drivers/block/oracleasm/trace.h
@@ -197,6 +197,38 @@ TRACE_EVENT(querydisk,
 		  show_ifmt(__entry->integrity))
 );
 
+TRACE_EVENT(queryhandle,
+
+	TP_PROTO(struct block_device *bdev, struct oracleasm_query_handle_v2 *qh),
+
+	TP_ARGS(bdev, qh),
+
+	TP_STRUCT__entry(
+		__field(void *		, bdev		)
+		__field(void *		, qh		)
+		__field(dev_t		, dev		)
+		__field(sector_t	, max		)
+		__field(unsigned int	, pbs		)
+		__field(unsigned int	, lbs		)
+		__field(unsigned char	, integrity	)
+	),
+
+	TP_fast_assign(
+		__entry->bdev		= bdev;
+		__entry->qh		= qh;
+		__entry->dev		= bdev->bd_dev ? bdev->bd_dev : 0;
+		__entry->max		= qh->qh_max_sectors;
+		__entry->pbs		= qh->qh_hardsect_size;
+		__entry->lbs		= 1 << (qh->qh_feature >> ASM_LSECSZ_SHIFT);
+		__entry->integrity	= qh->qh_feature & ASM_INTEGRITY_QDF_MASK;
+	),
+
+	TP_printk("   dev=%u:%u max_blocks=%llu pbs=%u lbs=%u integrity=%s",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  (unsigned long long)__entry->max, __entry->pbs, __entry->lbs,
+		  show_ifmt(__entry->integrity))
+);
+
 TRACE_EVENT(integrity,
 
 	TP_PROTO(struct oracleasm_integrity_v2 *it,
diff --git a/include/linux/oracleasm/abi.h b/include/linux/oracleasm/abi.h
index 0b5e0c480a10..c0ac30bfca44 100644
--- a/include/linux/oracleasm/abi.h
+++ b/include/linux/oracleasm/abi.h
@@ -103,6 +103,7 @@ enum asm_operation_types
 	ASMOP_CLOSE_DISK,
 	ASMOP_IO32,
 	ASMOP_IO64,
+	ASMOP_QUERY_HANDLE,
 	ASM_NUM_OPERATIONS  /* This must always be last */
 };
 
@@ -217,5 +218,14 @@ struct oracleasm_get_iid_v2
 /*18*/
 };
 
+struct oracleasm_query_handle_v2
+{
+/*00*/	struct oracleasm_abi_info	qh_abi;
+/*10*/	__u64				qh_handle;
+	__u32				qh_max_sectors;
+	__u32				qh_hardsect_size;
+	__u32				qh_feature;
+};
+
 #endif  /* _ORACLEASM_ABI_H */
 
diff --git a/include/linux/oracleasm/manager.h b/include/linux/oracleasm/manager.h
index 0e0d63c7b251..440d9e623665 100644
--- a/include/linux/oracleasm/manager.h
+++ b/include/linux/oracleasm/manager.h
@@ -106,6 +106,7 @@ static char *asm_operation_files[] = {
 	[ASMOP_QUERY_VERSION]	= ".query_version",
  	[ASMOP_GET_IID]		= ".get_iid",
  	[ASMOP_CHECK_IID]	= ".check_iid",
+	[ASMOP_QUERY_HANDLE]	= ".query_handle",
  	[ASMOP_QUERY_DISK]	= ".query_disk",
 
 	/*
@@ -219,7 +220,7 @@ static inline char *asm_operation_path(const char *manager,
 
 	if (!manager || !*manager)
 		return NULL;
-	if (op > ASM_LAST_TRANSACTION_OP)
+	if (op > ASM_LAST_TRANSACTION_OP && op != ASMOP_QUERY_HANDLE)
 		return NULL;
 
 	len = strlen(manager) + strlen("/") +

