forked from Minki/linux
[S390] s390_hypfs: Add new attributes
In order to access the data of the hypfs diagnose calls from user space also in binary form, this patch adds two new attributes in debugfs: * z/VM: s390_hypfs/d2fc_bin * LPAR: s390_hypfs/d204_bin Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
cc961d400e
commit
57b28f6631
@ -11,6 +11,7 @@
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#define REG_FILE_MODE 0440
|
||||
#define UPDATE_FILE_MODE 0220
|
||||
@ -34,6 +35,9 @@ extern int hypfs_diag_create_files(struct super_block *sb, struct dentry *root);
|
||||
|
||||
/* VM Hypervisor */
|
||||
extern int hypfs_vm_init(void);
|
||||
extern void hypfs_vm_exit(void);
|
||||
extern int hypfs_vm_create_files(struct super_block *sb, struct dentry *root);
|
||||
|
||||
/* Directory for debugfs files */
|
||||
extern struct dentry *hypfs_dbfs_dir;
|
||||
#endif /* _HYPFS_H_ */
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/ebcdic.h>
|
||||
#include "hypfs.h"
|
||||
|
||||
@ -22,6 +23,8 @@
|
||||
#define CPU_NAME_LEN 16 /* type name len of cpus in diag224 name table */
|
||||
#define TMP_SIZE 64 /* size of temporary buffers */
|
||||
|
||||
#define DBFS_D204_HDR_VERSION 0
|
||||
|
||||
/* diag 204 subcodes */
|
||||
enum diag204_sc {
|
||||
SUBC_STIB4 = 4,
|
||||
@ -47,6 +50,8 @@ static void *diag204_buf; /* 4K aligned buffer for diag204 data */
|
||||
static void *diag204_buf_vmalloc; /* vmalloc pointer for diag204 data */
|
||||
static int diag204_buf_pages; /* number of pages for diag204 data */
|
||||
|
||||
static struct dentry *dbfs_d204_file;
|
||||
|
||||
/*
|
||||
* DIAG 204 data structures and member access functions.
|
||||
*
|
||||
@ -364,18 +369,21 @@ static void diag204_free_buffer(void)
|
||||
} else {
|
||||
free_pages((unsigned long) diag204_buf, 0);
|
||||
}
|
||||
diag204_buf_pages = 0;
|
||||
diag204_buf = NULL;
|
||||
}
|
||||
|
||||
static void *page_align_ptr(void *ptr)
|
||||
{
|
||||
return (void *) PAGE_ALIGN((unsigned long) ptr);
|
||||
}
|
||||
|
||||
static void *diag204_alloc_vbuf(int pages)
|
||||
{
|
||||
/* The buffer has to be page aligned! */
|
||||
diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1));
|
||||
if (!diag204_buf_vmalloc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
diag204_buf = (void*)((unsigned long)diag204_buf_vmalloc
|
||||
& ~0xfffUL) + 0x1000;
|
||||
diag204_buf = page_align_ptr(diag204_buf_vmalloc);
|
||||
diag204_buf_pages = pages;
|
||||
return diag204_buf;
|
||||
}
|
||||
@ -468,17 +476,26 @@ fail_alloc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int diag204_do_store(void *buf, int pages)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = diag204((unsigned long) diag204_store_sc |
|
||||
(unsigned long) diag204_info_type, pages, buf);
|
||||
return rc < 0 ? -ENOSYS : 0;
|
||||
}
|
||||
|
||||
static void *diag204_store(void)
|
||||
{
|
||||
void *buf;
|
||||
int pages;
|
||||
int pages, rc;
|
||||
|
||||
buf = diag204_get_buffer(diag204_info_type, &pages);
|
||||
if (IS_ERR(buf))
|
||||
goto out;
|
||||
if (diag204((unsigned long)diag204_store_sc |
|
||||
(unsigned long)diag204_info_type, pages, buf) < 0)
|
||||
return ERR_PTR(-ENOSYS);
|
||||
rc = diag204_do_store(buf, pages);
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
out:
|
||||
return buf;
|
||||
}
|
||||
@ -526,6 +543,92 @@ static int diag224_idx2name(int index, char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dbfs_d204_hdr {
|
||||
u64 len; /* Length of d204 buffer without header */
|
||||
u16 version; /* Version of header */
|
||||
u8 sc; /* Used subcode */
|
||||
char reserved[53];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct dbfs_d204 {
|
||||
struct dbfs_d204_hdr hdr; /* 64 byte header */
|
||||
char buf[]; /* d204 buffer */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct dbfs_d204_private {
|
||||
struct dbfs_d204 *d204; /* Aligned d204 data with header */
|
||||
void *base; /* Base pointer (needed for vfree) */
|
||||
};
|
||||
|
||||
static int dbfs_d204_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dbfs_d204_private *data;
|
||||
struct dbfs_d204 *d204;
|
||||
int rc, buf_size;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr);
|
||||
data->base = vmalloc(buf_size);
|
||||
if (!data->base) {
|
||||
rc = -ENOMEM;
|
||||
goto fail_kfree_data;
|
||||
}
|
||||
memset(data->base, 0, buf_size);
|
||||
d204 = page_align_ptr(data->base + sizeof(d204->hdr))
|
||||
- sizeof(d204->hdr);
|
||||
rc = diag204_do_store(&d204->buf, diag204_buf_pages);
|
||||
if (rc)
|
||||
goto fail_vfree_base;
|
||||
d204->hdr.version = DBFS_D204_HDR_VERSION;
|
||||
d204->hdr.len = PAGE_SIZE * diag204_buf_pages;
|
||||
d204->hdr.sc = diag204_store_sc;
|
||||
data->d204 = d204;
|
||||
file->private_data = data;
|
||||
return nonseekable_open(inode, file);
|
||||
|
||||
fail_vfree_base:
|
||||
vfree(data->base);
|
||||
fail_kfree_data:
|
||||
kfree(data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dbfs_d204_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dbfs_d204_private *data = file->private_data;
|
||||
|
||||
vfree(data->base);
|
||||
kfree(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t dbfs_d204_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *ppos)
|
||||
{
|
||||
struct dbfs_d204_private *data = file->private_data;
|
||||
|
||||
return simple_read_from_buffer(buf, size, ppos, data->d204,
|
||||
data->d204->hdr.len +
|
||||
sizeof(data->d204->hdr));
|
||||
}
|
||||
|
||||
static const struct file_operations dbfs_d204_ops = {
|
||||
.open = dbfs_d204_open,
|
||||
.read = dbfs_d204_read,
|
||||
.release = dbfs_d204_release,
|
||||
};
|
||||
|
||||
static int hypfs_dbfs_init(void)
|
||||
{
|
||||
dbfs_d204_file = debugfs_create_file("diag_204", 0400, hypfs_dbfs_dir,
|
||||
NULL, &dbfs_d204_ops);
|
||||
if (IS_ERR(dbfs_d204_file))
|
||||
return PTR_ERR(dbfs_d204_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__init int hypfs_diag_init(void)
|
||||
{
|
||||
int rc;
|
||||
@ -540,11 +643,17 @@ __init int hypfs_diag_init(void)
|
||||
pr_err("The hardware system does not provide all "
|
||||
"functions required by hypfs\n");
|
||||
}
|
||||
if (diag204_info_type == INFO_EXT) {
|
||||
rc = hypfs_dbfs_init();
|
||||
if (rc)
|
||||
diag204_free_buffer();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void hypfs_diag_exit(void)
|
||||
{
|
||||
debugfs_remove(dbfs_d204_file);
|
||||
diag224_delete_name_table();
|
||||
diag204_free_buffer();
|
||||
}
|
||||
|
@ -10,14 +10,18 @@
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/ebcdic.h>
|
||||
#include <asm/timex.h>
|
||||
#include "hypfs.h"
|
||||
|
||||
#define NAME_LEN 8
|
||||
#define DBFS_D2FC_HDR_VERSION 0
|
||||
|
||||
static char local_guest[] = " ";
|
||||
static char all_guests[] = "* ";
|
||||
static char *guest_query;
|
||||
|
||||
static struct dentry *dbfs_d2fc_file;
|
||||
|
||||
struct diag2fc_data {
|
||||
__u32 version;
|
||||
__u32 flags;
|
||||
@ -76,23 +80,26 @@ static int diag2fc(int size, char* query, void *addr)
|
||||
return -residual_cnt;
|
||||
}
|
||||
|
||||
static struct diag2fc_data *diag2fc_store(char *query, int *count)
|
||||
/*
|
||||
* Allocate buffer for "query" and store diag 2fc at "offset"
|
||||
*/
|
||||
static void *diag2fc_store(char *query, unsigned int *count, int offset)
|
||||
{
|
||||
void *data;
|
||||
int size;
|
||||
struct diag2fc_data *data;
|
||||
|
||||
do {
|
||||
size = diag2fc(0, query, NULL);
|
||||
if (size < 0)
|
||||
return ERR_PTR(-EACCES);
|
||||
data = vmalloc(size);
|
||||
data = vmalloc(size + offset);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (diag2fc(size, query, data) == 0)
|
||||
if (diag2fc(size, query, data + offset) == 0)
|
||||
break;
|
||||
vfree(data);
|
||||
} while (1);
|
||||
*count = (size / sizeof(*data));
|
||||
*count = (size / sizeof(struct diag2fc_data));
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -168,9 +175,10 @@ int hypfs_vm_create_files(struct super_block *sb, struct dentry *root)
|
||||
{
|
||||
struct dentry *dir, *file;
|
||||
struct diag2fc_data *data;
|
||||
int rc, i, count = 0;
|
||||
unsigned int count = 0;
|
||||
int rc, i;
|
||||
|
||||
data = diag2fc_store(guest_query, &count);
|
||||
data = diag2fc_store(guest_query, &count, 0);
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
@ -218,8 +226,61 @@ failed:
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct dbfs_d2fc_hdr {
|
||||
u64 len; /* Length of d2fc buffer without header */
|
||||
u16 version; /* Version of header */
|
||||
char tod_ext[16]; /* TOD clock for d2fc */
|
||||
u64 count; /* Number of VM guests in d2fc buffer */
|
||||
char reserved[30];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct dbfs_d2fc {
|
||||
struct dbfs_d2fc_hdr hdr; /* 64 byte header */
|
||||
char buf[]; /* d2fc buffer */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
static int dbfs_d2fc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dbfs_d2fc *data;
|
||||
unsigned int count;
|
||||
|
||||
data = diag2fc_store(guest_query, &count, sizeof(data->hdr));
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
get_clock_ext(data->hdr.tod_ext);
|
||||
data->hdr.len = count * sizeof(struct diag2fc_data);
|
||||
data->hdr.version = DBFS_D2FC_HDR_VERSION;
|
||||
data->hdr.count = count;
|
||||
memset(&data->hdr.reserved, 0, sizeof(data->hdr.reserved));
|
||||
file->private_data = data;
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static int dbfs_d2fc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
diag2fc_free(file->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t dbfs_d2fc_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *ppos)
|
||||
{
|
||||
struct dbfs_d2fc *data = file->private_data;
|
||||
|
||||
return simple_read_from_buffer(buf, size, ppos, data, data->hdr.len +
|
||||
sizeof(struct dbfs_d2fc_hdr));
|
||||
}
|
||||
|
||||
static const struct file_operations dbfs_d2fc_ops = {
|
||||
.open = dbfs_d2fc_open,
|
||||
.read = dbfs_d2fc_read,
|
||||
.release = dbfs_d2fc_release,
|
||||
};
|
||||
|
||||
int hypfs_vm_init(void)
|
||||
{
|
||||
if (!MACHINE_IS_VM)
|
||||
return 0;
|
||||
if (diag2fc(0, all_guests, NULL) > 0)
|
||||
guest_query = all_guests;
|
||||
else if (diag2fc(0, local_guest, NULL) > 0)
|
||||
@ -227,5 +288,17 @@ int hypfs_vm_init(void)
|
||||
else
|
||||
return -EACCES;
|
||||
|
||||
dbfs_d2fc_file = debugfs_create_file("diag_2fc", 0400, hypfs_dbfs_dir,
|
||||
NULL, &dbfs_d2fc_ops);
|
||||
if (IS_ERR(dbfs_d2fc_file))
|
||||
return PTR_ERR(dbfs_d2fc_file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hypfs_vm_exit(void)
|
||||
{
|
||||
if (!MACHINE_IS_VM)
|
||||
return;
|
||||
debugfs_remove(dbfs_d2fc_file);
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ static const struct super_operations hypfs_s_ops;
|
||||
/* start of list of all dentries, which have to be deleted on update */
|
||||
static struct dentry *hypfs_last_dentry;
|
||||
|
||||
struct dentry *hypfs_dbfs_dir;
|
||||
|
||||
static void hypfs_update_update(struct super_block *sb)
|
||||
{
|
||||
struct hypfs_sb_info *sb_info = sb->s_fs_info;
|
||||
@ -468,20 +470,22 @@ static int __init hypfs_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (MACHINE_IS_VM) {
|
||||
if (hypfs_vm_init())
|
||||
/* no diag 2fc, just exit */
|
||||
return -ENODATA;
|
||||
} else {
|
||||
if (hypfs_diag_init()) {
|
||||
rc = -ENODATA;
|
||||
goto fail_diag;
|
||||
}
|
||||
hypfs_dbfs_dir = debugfs_create_dir("s390_hypfs", NULL);
|
||||
if (IS_ERR(hypfs_dbfs_dir))
|
||||
return PTR_ERR(hypfs_dbfs_dir);
|
||||
|
||||
if (hypfs_diag_init()) {
|
||||
rc = -ENODATA;
|
||||
goto fail_debugfs_remove;
|
||||
}
|
||||
if (hypfs_vm_init()) {
|
||||
rc = -ENODATA;
|
||||
goto fail_hypfs_diag_exit;
|
||||
}
|
||||
s390_kobj = kobject_create_and_add("s390", hypervisor_kobj);
|
||||
if (!s390_kobj) {
|
||||
rc = -ENOMEM;
|
||||
goto fail_sysfs;
|
||||
goto fail_hypfs_vm_exit;
|
||||
}
|
||||
rc = register_filesystem(&hypfs_type);
|
||||
if (rc)
|
||||
@ -490,18 +494,22 @@ static int __init hypfs_init(void)
|
||||
|
||||
fail_filesystem:
|
||||
kobject_put(s390_kobj);
|
||||
fail_sysfs:
|
||||
if (!MACHINE_IS_VM)
|
||||
hypfs_diag_exit();
|
||||
fail_diag:
|
||||
fail_hypfs_vm_exit:
|
||||
hypfs_vm_exit();
|
||||
fail_hypfs_diag_exit:
|
||||
hypfs_diag_exit();
|
||||
fail_debugfs_remove:
|
||||
debugfs_remove(hypfs_dbfs_dir);
|
||||
|
||||
pr_err("Initialization of hypfs failed with rc=%i\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit hypfs_exit(void)
|
||||
{
|
||||
if (!MACHINE_IS_VM)
|
||||
hypfs_diag_exit();
|
||||
hypfs_diag_exit();
|
||||
hypfs_vm_exit();
|
||||
debugfs_remove(hypfs_dbfs_dir);
|
||||
unregister_filesystem(&hypfs_type);
|
||||
kobject_put(s390_kobj);
|
||||
}
|
||||
|
@ -61,11 +61,15 @@ static inline unsigned long long get_clock (void)
|
||||
return clk;
|
||||
}
|
||||
|
||||
static inline void get_clock_ext(char *clk)
|
||||
{
|
||||
asm volatile("stcke %0" : "=Q" (*clk) : : "cc");
|
||||
}
|
||||
|
||||
static inline unsigned long long get_clock_xt(void)
|
||||
{
|
||||
unsigned char clk[16];
|
||||
|
||||
asm volatile("stcke %0" : "=Q" (clk) : : "cc");
|
||||
get_clock_ext(clk);
|
||||
return *((unsigned long long *)&clk[1]);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user