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

Commit 07be0382 authored by Martin Schwidefsky's avatar Martin Schwidefsky
Browse files

s390/hypfs: add interface for diagnose 0x304



To provide access to the set-partition-resource-parameter interface
to user space add a new attribute to hypfs/debugfs:
 * s390_hypsfs/diag_304
The data for the query-partition-resource-parameters command can
be access by a read on the attribute. All other diagnose 0x304
requests need to be submitted via ioctl with CAP_SYS_ADMIN rights.

Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent fded4329
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ Code Seq#(hex) Include File Comments
0x09	all	linux/raid/md_u.h
0x10	00-0F	drivers/char/s390/vmcp.h
0x10	10-1F	arch/s390/include/uapi/sclp_ctl.h
0x10	20-2F	arch/s390/include/uapi/asm/hypfs.h
0x12	all	linux/fs.h
		linux/blkpg.h
0x1b	all	InfiniBand Subsystem	<http://infiniband.sourceforge.net/>
+1 −1
Original line number Diff line number Diff line
@@ -4,4 +4,4 @@

obj-$(CONFIG_S390_HYPFS_FS) += s390_hypfs.o

s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o hypfs_dbfs.o
s390_hypfs-objs := inode.o hypfs_diag.o hypfs_vm.o hypfs_dbfs.o hypfs_sprp.o
+7 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/debugfs.h>
#include <linux/workqueue.h>
#include <linux/kref.h>
#include <asm/hypfs.h>

#define REG_FILE_MODE    0440
#define UPDATE_FILE_MODE 0220
@@ -36,6 +37,10 @@ extern int hypfs_vm_init(void);
extern void hypfs_vm_exit(void);
extern int hypfs_vm_create_files(struct dentry *root);

/* Set Partition-Resource Parameter */
int hypfs_sprp_init(void);
void hypfs_sprp_exit(void);

/* debugfs interface */
struct hypfs_dbfs_file;

@@ -52,6 +57,8 @@ struct hypfs_dbfs_file {
	int		(*data_create)(void **data, void **data_free_ptr,
				       size_t *size);
	void		(*data_free)(const void *buf_free_ptr);
	long		(*unlocked_ioctl) (struct file *, unsigned int,
					   unsigned long);

	/* Private data for hypfs_dbfs.c */
	struct hypfs_dbfs_data	*data;
+16 −0
Original line number Diff line number Diff line
@@ -81,9 +81,25 @@ static ssize_t dbfs_read(struct file *file, char __user *buf,
	return rc;
}

static long dbfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	struct hypfs_dbfs_file *df;
	long rc;

	df = file->f_path.dentry->d_inode->i_private;
	mutex_lock(&df->lock);
	if (df->unlocked_ioctl)
		rc = df->unlocked_ioctl(file, cmd, arg);
	else
		rc = -ENOTTY;
	mutex_unlock(&df->lock);
	return rc;
}

static const struct file_operations dbfs_ops = {
	.read		= dbfs_read,
	.llseek		= no_llseek,
	.unlocked_ioctl = dbfs_ioctl,
};

int hypfs_dbfs_create_file(struct hypfs_dbfs_file *df)
+141 −0
Original line number Diff line number Diff line
/*
 *    Hypervisor filesystem for Linux on s390.
 *    Set Partition-Resource Parameter interface.
 *
 *    Copyright IBM Corp. 2013
 *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
 */

#include <linux/compat.h>
#include <linux/errno.h>
#include <linux/gfp.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <asm/compat.h>
#include <asm/sclp.h>
#include "hypfs.h"

#define DIAG304_SET_WEIGHTS	0
#define DIAG304_QUERY_PRP	1
#define DIAG304_SET_CAPPING	2

#define DIAG304_CMD_MAX		2

static unsigned long hypfs_sprp_diag304(void *data, unsigned long cmd)
{
	register unsigned long _data asm("2") = (unsigned long) data;
	register unsigned long _rc asm("3");
	register unsigned long _cmd asm("4") = cmd;

	asm volatile("diag %1,%2,0x304\n"
		     : "=d" (_rc) : "d" (_data), "d" (_cmd) : "memory");

	return _rc;
}

static void hypfs_sprp_free(const void *data)
{
	free_page((unsigned long) data);
}

static int hypfs_sprp_create(void **data_ptr, void **free_ptr, size_t *size)
{
	unsigned long rc;
	void *data;

	data = (void *) get_zeroed_page(GFP_KERNEL);
	if (!data)
		return -ENOMEM;
	rc = hypfs_sprp_diag304(data, DIAG304_QUERY_PRP);
	if (rc != 1) {
		*data_ptr = *free_ptr = NULL;
		*size = 0;
		free_page((unsigned long) data);
		return -EIO;
	}
	*data_ptr = *free_ptr = data;
	*size = PAGE_SIZE;
	return 0;
}

static int __hypfs_sprp_ioctl(void __user *user_area)
{
	struct hypfs_diag304 diag304;
	unsigned long cmd;
	void __user *udata;
	void *data;
	int rc;

	if (copy_from_user(&diag304, user_area, sizeof(diag304)))
		return -EFAULT;
	if ((diag304.args[0] >> 8) != 0 || diag304.args[1] > DIAG304_CMD_MAX)
		return -EINVAL;

	data = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
	if (!data)
		return -ENOMEM;

	udata = (void __user *)(unsigned long) diag304.data;
	if (diag304.args[1] == DIAG304_SET_WEIGHTS ||
	    diag304.args[1] == DIAG304_SET_CAPPING)
		if (copy_from_user(data, udata, PAGE_SIZE)) {
			rc = -EFAULT;
			goto out;
		}

	cmd = *(unsigned long *) &diag304.args[0];
	diag304.rc = hypfs_sprp_diag304(data, cmd);

	if (diag304.args[1] == DIAG304_QUERY_PRP)
		if (copy_to_user(udata, data, PAGE_SIZE)) {
			rc = -EFAULT;
			goto out;
		}

	rc = copy_to_user(user_area, &diag304, sizeof(diag304)) ? -EFAULT : 0;
out:
	free_page((unsigned long) data);
	return rc;
}

static long hypfs_sprp_ioctl(struct file *file, unsigned int cmd,
			       unsigned long arg)
{
	void __user *argp;

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
	if (is_compat_task())
		argp = compat_ptr(arg);
	else
		argp = (void __user *) arg;
	switch (cmd) {
	case HYPFS_DIAG304:
		return __hypfs_sprp_ioctl(argp);
	default: /* unknown ioctl number */
		return -ENOTTY;
	}
	return 0;
}

static struct hypfs_dbfs_file hypfs_sprp_file = {
	.name		= "diag_304",
	.data_create	= hypfs_sprp_create,
	.data_free	= hypfs_sprp_free,
	.unlocked_ioctl = hypfs_sprp_ioctl,
};

int hypfs_sprp_init(void)
{
	if (!sclp_has_sprp())
		return 0;
	return hypfs_dbfs_create_file(&hypfs_sprp_file);
}

void hypfs_sprp_exit(void)
{
	if (!sclp_has_sprp())
		return;
	hypfs_dbfs_remove_file(&hypfs_sprp_file);
}
Loading