Skip to content

Commit

Permalink
[S390] s390_hypfs: Add new attributes
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael Holzheu authored and Martin Schwidefsky committed May 17, 2010
1 parent cc961d4 commit 57b28f6
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 32 deletions.
4 changes: 4 additions & 0 deletions arch/s390/hypfs/hypfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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_ */
123 changes: 116 additions & 7 deletions arch/s390/hypfs/hypfs_diag.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <asm/ebcdic.h>
#include "hypfs.h"

#define LPAR_NAME_LEN 8 /* lpar name len in diag 204 data */
#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,
Expand All @@ -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.
*
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -468,17 +476,26 @@ static int diag204_probe(void)
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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down
87 changes: 80 additions & 7 deletions arch/s390/hypfs/hypfs_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -218,14 +226,79 @@ int hypfs_vm_create_files(struct super_block *sb, struct dentry *root)
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)
guest_query = local_guest;
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);
}
Loading

0 comments on commit 57b28f6

Please sign in to comment.