Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a3b7b11 authored by Stefan Haberland's avatar Stefan Haberland Committed by Martin Schwidefsky
Browse files

s390/dasd: add query host access to volume support



With this feature, applications can query if a DASD volume is online
to another operating system instances by checking the online status of
all attached hosts from the storage server.

Reviewed-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Reviewed-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarStefan Haberland <stefan.haberland@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 2fd92273
Loading
Loading
Loading
Loading
+56 −0
Original line number Original line Diff line number Diff line
@@ -75,6 +75,8 @@ static void dasd_block_timeout(unsigned long);
static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *);
static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *);
static void dasd_profile_init(struct dasd_profile *, struct dentry *);
static void dasd_profile_init(struct dasd_profile *, struct dentry *);
static void dasd_profile_exit(struct dasd_profile *);
static void dasd_profile_exit(struct dasd_profile *);
static void dasd_hosts_init(struct dentry *, struct dasd_device *);
static void dasd_hosts_exit(struct dasd_device *);


/*
/*
 * SECTION: Operations on the device structure.
 * SECTION: Operations on the device structure.
@@ -267,6 +269,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device)
		dasd_debugfs_setup(dev_name(&device->cdev->dev),
		dasd_debugfs_setup(dev_name(&device->cdev->dev),
				   dasd_debugfs_root_entry);
				   dasd_debugfs_root_entry);
	dasd_profile_init(&device->profile, device->debugfs_dentry);
	dasd_profile_init(&device->profile, device->debugfs_dentry);
	dasd_hosts_init(device->debugfs_dentry, device);


	/* register 'device' debug area, used for all DBF_DEV_XXX calls */
	/* register 'device' debug area, used for all DBF_DEV_XXX calls */
	device->debug_area = debug_register(dev_name(&device->cdev->dev), 4, 1,
	device->debug_area = debug_register(dev_name(&device->cdev->dev), 4, 1,
@@ -304,6 +307,7 @@ static int dasd_state_basic_to_known(struct dasd_device *device)
		return rc;
		return rc;
	dasd_device_clear_timer(device);
	dasd_device_clear_timer(device);
	dasd_profile_exit(&device->profile);
	dasd_profile_exit(&device->profile);
	dasd_hosts_exit(device);
	debugfs_remove(device->debugfs_dentry);
	debugfs_remove(device->debugfs_dentry);
	DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device);
	DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device);
	if (device->debug_area != NULL) {
	if (device->debug_area != NULL) {
@@ -1150,6 +1154,58 @@ int dasd_profile_on(struct dasd_profile *profile)


#endif				/* CONFIG_DASD_PROFILE */
#endif				/* CONFIG_DASD_PROFILE */


static int dasd_hosts_show(struct seq_file *m, void *v)
{
	struct dasd_device *device;
	int rc = -EOPNOTSUPP;

	device = m->private;
	dasd_get_device(device);

	if (device->discipline->hosts_print)
		rc = device->discipline->hosts_print(device, m);

	dasd_put_device(device);
	return rc;
}

static int dasd_hosts_open(struct inode *inode, struct file *file)
{
	struct dasd_device *device = inode->i_private;

	return single_open(file, dasd_hosts_show, device);
}

static const struct file_operations dasd_hosts_fops = {
	.owner		= THIS_MODULE,
	.open		= dasd_hosts_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

static void dasd_hosts_exit(struct dasd_device *device)
{
	debugfs_remove(device->hosts_dentry);
	device->hosts_dentry = NULL;
}

static void dasd_hosts_init(struct dentry *base_dentry,
			    struct dasd_device *device)
{
	struct dentry *pde;
	umode_t mode;

	if (!base_dentry)
		return;

	mode = S_IRUSR | S_IFREG;
	pde = debugfs_create_file("host_access_list", mode, base_dentry,
				  device, &dasd_hosts_fops);
	if (pde && !IS_ERR(pde))
		device->hosts_dentry = pde;
}

/*
/*
 * Allocate memory for a channel program with 'cplength' channel
 * Allocate memory for a channel program with 'cplength' channel
 * command words and 'datasize' additional space. There are two
 * command words and 'datasize' additional space. There are two
+27 −0
Original line number Original line Diff line number Diff line
@@ -981,6 +981,32 @@ dasd_safe_offline_store(struct device *dev, struct device_attribute *attr,


static DEVICE_ATTR(safe_offline, 0200, NULL, dasd_safe_offline_store);
static DEVICE_ATTR(safe_offline, 0200, NULL, dasd_safe_offline_store);


static ssize_t
dasd_access_show(struct device *dev, struct device_attribute *attr,
		 char *buf)
{
	struct ccw_device *cdev = to_ccwdev(dev);
	struct dasd_device *device;
	int count;

	device = dasd_device_from_cdev(cdev);
	if (IS_ERR(device))
		return PTR_ERR(device);

	if (device->discipline->host_access_count)
		count = device->discipline->host_access_count(device);
	else
		count = -EOPNOTSUPP;

	dasd_put_device(device);
	if (count < 0)
		return count;

	return sprintf(buf, "%d\n", count);
}

static DEVICE_ATTR(host_access_count, 0444, dasd_access_show, NULL);

static ssize_t
static ssize_t
dasd_discipline_show(struct device *dev, struct device_attribute *attr,
dasd_discipline_show(struct device *dev, struct device_attribute *attr,
		     char *buf)
		     char *buf)
@@ -1471,6 +1497,7 @@ static struct attribute * dasd_attrs[] = {
	&dev_attr_reservation_policy.attr,
	&dev_attr_reservation_policy.attr,
	&dev_attr_last_known_reservation_state.attr,
	&dev_attr_last_known_reservation_state.attr,
	&dev_attr_safe_offline.attr,
	&dev_attr_safe_offline.attr,
	&dev_attr_host_access_count.attr,
	&dev_attr_path_masks.attr,
	&dev_attr_path_masks.attr,
	NULL,
	NULL,
};
};
+164 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/module.h>
#include <linux/compat.h>
#include <linux/compat.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/seq_file.h>


#include <asm/css_chars.h>
#include <asm/css_chars.h>
#include <asm/debug.h>
#include <asm/debug.h>
@@ -4627,6 +4628,167 @@ static int dasd_eckd_read_message_buffer(struct dasd_device *device,
	return rc;
	return rc;
}
}


static int dasd_eckd_query_host_access(struct dasd_device *device,
				       struct dasd_psf_query_host_access *data)
{
	struct dasd_eckd_private *private = device->private;
	struct dasd_psf_query_host_access *host_access;
	struct dasd_psf_prssd_data *prssdp;
	struct dasd_ccw_req *cqr;
	struct ccw1 *ccw;
	int rc;

	/* not available for HYPER PAV alias devices */
	if (!device->block && private->lcu->pav == HYPER_PAV)
		return -EOPNOTSUPP;

	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */	+ 1 /* RSSD */,
				   sizeof(struct dasd_psf_prssd_data) + 1,
				   device);
	if (IS_ERR(cqr)) {
		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
				"Could not allocate read message buffer request");
		return PTR_ERR(cqr);
	}
	host_access = kzalloc(sizeof(*host_access), GFP_KERNEL | GFP_DMA);
	if (!host_access) {
		dasd_sfree_request(cqr, device);
		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
				"Could not allocate host_access buffer");
		return -ENOMEM;
	}
	cqr->startdev = device;
	cqr->memdev = device;
	cqr->block = NULL;
	cqr->retries = 256;
	cqr->expires = 10 * HZ;

	/* Prepare for Read Subsystem Data */
	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
	prssdp->order = PSF_ORDER_PRSSD;
	prssdp->suborder = PSF_SUBORDER_QHA;	/* query host access */
	/* LSS and Volume that will be queried */
	prssdp->lss = private->ned->ID;
	prssdp->volume = private->ned->unit_addr;
	/* all other bytes of prssdp must be zero */

	ccw = cqr->cpaddr;
	ccw->cmd_code = DASD_ECKD_CCW_PSF;
	ccw->count = sizeof(struct dasd_psf_prssd_data);
	ccw->flags |= CCW_FLAG_CC;
	ccw->flags |= CCW_FLAG_SLI;
	ccw->cda = (__u32)(addr_t) prssdp;

	/* Read Subsystem Data - query host access */
	ccw++;
	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
	ccw->count = sizeof(struct dasd_psf_query_host_access);
	ccw->flags |= CCW_FLAG_SLI;
	ccw->cda = (__u32)(addr_t) host_access;

	cqr->buildclk = get_tod_clock();
	cqr->status = DASD_CQR_FILLED;
	rc = dasd_sleep_on(cqr);
	if (rc == 0) {
		*data = *host_access;
	} else {
		DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
				"Reading host access data failed with rc=%d\n",
				rc);
		rc = -EOPNOTSUPP;
	}

	dasd_sfree_request(cqr, cqr->memdev);
	kfree(host_access);
	return rc;
}
/*
 * return number of grouped devices
 */
static int dasd_eckd_host_access_count(struct dasd_device *device)
{
	struct dasd_psf_query_host_access *access;
	struct dasd_ckd_path_group_entry *entry;
	struct dasd_ckd_host_information *info;
	int count = 0;
	int rc, i;

	access = kzalloc(sizeof(*access), GFP_NOIO);
	if (!access) {
		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
				"Could not allocate access buffer");
		return -ENOMEM;
	}
	rc = dasd_eckd_query_host_access(device, access);
	if (rc) {
		kfree(access);
		return rc;
	}

	info = (struct dasd_ckd_host_information *)
		access->host_access_information;
	for (i = 0; i < info->entry_count; i++) {
		entry = (struct dasd_ckd_path_group_entry *)
			(info->entry + i * info->entry_size);
		if (entry->status_flags & DASD_ECKD_PG_GROUPED)
			count++;
	}

	kfree(access);
	return count;
}

/*
 * write host access information to a sequential file
 */
static int dasd_hosts_print(struct dasd_device *device, struct seq_file *m)
{
	struct dasd_psf_query_host_access *access;
	struct dasd_ckd_path_group_entry *entry;
	struct dasd_ckd_host_information *info;
	char sysplex[9] = "";
	int rc, i, j;

	access = kzalloc(sizeof(*access), GFP_NOIO);
	if (!access) {
		DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
				"Could not allocate access buffer");
		return -ENOMEM;
	}
	rc = dasd_eckd_query_host_access(device, access);
	if (rc) {
		kfree(access);
		return rc;
	}

	info = (struct dasd_ckd_host_information *)
		access->host_access_information;
	for (i = 0; i < info->entry_count; i++) {
		entry = (struct dasd_ckd_path_group_entry *)
			(info->entry + i * info->entry_size);
		/* PGID */
		seq_puts(m, "pgid ");
		for (j = 0; j < 11; j++)
			seq_printf(m, "%02x", entry->pgid[j]);
		seq_putc(m, '\n');
		/* FLAGS */
		seq_printf(m, "status_flags %02x\n", entry->status_flags);
		/* SYSPLEX NAME */
		memcpy(&sysplex, &entry->sysplex_name, sizeof(sysplex) - 1);
		EBCASC(sysplex, sizeof(sysplex));
		seq_printf(m, "sysplex_name %8s\n", sysplex);
		/* SUPPORTED CYLINDER */
		seq_printf(m, "supported_cylinder %d\n", entry->cylinder);
		/* TIMESTAMP */
		seq_printf(m, "timestamp %lu\n", (unsigned long)
			   entry->timestamp);
	}
	kfree(access);

	return 0;
}

/*
/*
 * Perform Subsystem Function - CUIR response
 * Perform Subsystem Function - CUIR response
 */
 */
@@ -5099,6 +5261,8 @@ static struct dasd_discipline dasd_eckd_discipline = {
	.get_uid = dasd_eckd_get_uid,
	.get_uid = dasd_eckd_get_uid,
	.kick_validate = dasd_eckd_kick_validate_server,
	.kick_validate = dasd_eckd_kick_validate_server,
	.check_attention = dasd_eckd_check_attention,
	.check_attention = dasd_eckd_check_attention,
	.host_access_count = dasd_eckd_host_access_count,
	.hosts_print = dasd_hosts_print,
};
};


static int __init
static int __init
+32 −1
Original line number Original line Diff line number Diff line
@@ -53,6 +53,7 @@
 */
 */
#define PSF_ORDER_PRSSD			 0x18
#define PSF_ORDER_PRSSD			 0x18
#define PSF_ORDER_CUIR_RESPONSE		 0x1A
#define PSF_ORDER_CUIR_RESPONSE		 0x1A
#define PSF_SUBORDER_QHA		 0x1C
#define PSF_ORDER_SSC			 0x1D
#define PSF_ORDER_SSC			 0x1D


/*
/*
@@ -81,6 +82,8 @@
#define ATTENTION_LENGTH_CUIR		 0x0e
#define ATTENTION_LENGTH_CUIR		 0x0e
#define ATTENTION_FORMAT_CUIR		 0x01
#define ATTENTION_FORMAT_CUIR		 0x01


#define DASD_ECKD_PG_GROUPED		 0x10

/*
/*
 * Size that is reportet for large volumes in the old 16-bit no_cyl field
 * Size that is reportet for large volumes in the old 16-bit no_cyl field
 */
 */
@@ -403,13 +406,41 @@ struct dasd_psf_cuir_response {
	__u8 ssid;
	__u8 ssid;
} __packed;
} __packed;


struct dasd_ckd_path_group_entry {
	__u8 status_flags;
	__u8 pgid[11];
	__u8 sysplex_name[8];
	__u32 timestamp;
	__u32 cylinder;
	__u8 reserved[4];
} __packed;

struct dasd_ckd_host_information {
	__u8 access_flags;
	__u8 entry_size;
	__u16 entry_count;
	__u8 entry[16390];
} __packed;

struct dasd_psf_query_host_access {
	__u8 access_flag;
	__u8 version;
	__u16 CKD_length;
	__u16 SCSI_length;
	__u8 unused[10];
	__u8 host_access_information[16394];
} __packed;

/*
/*
 * Perform Subsystem Function - Prepare for Read Subsystem Data
 * Perform Subsystem Function - Prepare for Read Subsystem Data
 */
 */
struct dasd_psf_prssd_data {
struct dasd_psf_prssd_data {
	unsigned char order;
	unsigned char order;
	unsigned char flags;
	unsigned char flags;
	unsigned char reserved[4];
	unsigned char reserved1;
	unsigned char reserved2;
	unsigned char lss;
	unsigned char volume;
	unsigned char suborder;
	unsigned char suborder;
	unsigned char varies[5];
	unsigned char varies[5];
} __attribute__ ((packed));
} __attribute__ ((packed));
+3 −0
Original line number Original line Diff line number Diff line
@@ -365,6 +365,8 @@ struct dasd_discipline {
	int (*get_uid) (struct dasd_device *, struct dasd_uid *);
	int (*get_uid) (struct dasd_device *, struct dasd_uid *);
	void (*kick_validate) (struct dasd_device *);
	void (*kick_validate) (struct dasd_device *);
	int (*check_attention)(struct dasd_device *, __u8);
	int (*check_attention)(struct dasd_device *, __u8);
	int (*host_access_count)(struct dasd_device *);
	int (*hosts_print)(struct dasd_device *, struct seq_file *);
};
};


extern struct dasd_discipline *dasd_diag_discipline_pointer;
extern struct dasd_discipline *dasd_diag_discipline_pointer;
@@ -487,6 +489,7 @@ struct dasd_device {
	unsigned long blk_timeout;
	unsigned long blk_timeout;


	struct dentry *debugfs_dentry;
	struct dentry *debugfs_dentry;
	struct dentry *hosts_dentry;
	struct dasd_profile profile;
	struct dasd_profile profile;
};
};