Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /*
* Hypervisor filesystem for Linux on s390
*
* Diag 0C implementation
*
* Copyright IBM Corp. 2014
*/
#include <linux/slab.h>
#include <linux/cpu.h>
#include <asm/diag.h>
#include <asm/hypfs.h>
#include "hypfs.h"
#define DBFS_D0C_HDR_VERSION 0
/*
* Execute diagnose 0c in 31 bit mode
*/
static void diag0c(struct hypfs_diag0c_entry *entry)
{
diag_stat_inc(DIAG_STAT_X00C);
asm volatile (
" sam31\n"
" diag %0,%0,0x0c\n"
" sam64\n"
: /* no output register */
: "a" (entry)
: "memory");
}
/*
* Get hypfs_diag0c_entry from CPU vector and store diag0c data
*/
static void diag0c_fn(void *data)
{
diag0c(((void **) data)[smp_processor_id()]);
}
/*
* Allocate buffer and store diag 0c data
*/
static void *diag0c_store(unsigned int *count)
{
struct hypfs_diag0c_data *diag0c_data;
unsigned int cpu_count, cpu, i;
void **cpu_vec;
get_online_cpus();
cpu_count = num_online_cpus();
cpu_vec = kmalloc(sizeof(*cpu_vec) * num_possible_cpus(), GFP_KERNEL);
if (!cpu_vec)
goto fail_put_online_cpus;
/* Note: Diag 0c needs 8 byte alignment and real storage */
diag0c_data = kzalloc(sizeof(struct hypfs_diag0c_hdr) +
cpu_count * sizeof(struct hypfs_diag0c_entry),
GFP_KERNEL | GFP_DMA);
if (!diag0c_data)
goto fail_kfree_cpu_vec;
i = 0;
/* Fill CPU vector for each online CPU */
for_each_online_cpu(cpu) {
diag0c_data->entry[i].cpu = cpu;
cpu_vec[cpu] = &diag0c_data->entry[i++];
}
/* Collect data all CPUs */
on_each_cpu(diag0c_fn, cpu_vec, 1);
*count = cpu_count;
kfree(cpu_vec);
put_online_cpus();
return diag0c_data;
fail_kfree_cpu_vec:
kfree(cpu_vec);
fail_put_online_cpus:
put_online_cpus();
return ERR_PTR(-ENOMEM);
}
/*
* Hypfs DBFS callback: Free diag 0c data
*/
static void dbfs_diag0c_free(const void *data)
{
kfree(data);
}
/*
* Hypfs DBFS callback: Create diag 0c data
*/
static int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size)
{
struct hypfs_diag0c_data *diag0c_data;
unsigned int count;
diag0c_data = diag0c_store(&count);
if (IS_ERR(diag0c_data))
return PTR_ERR(diag0c_data);
memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr));
get_tod_clock_ext(diag0c_data->hdr.tod_ext);
diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry);
diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION;
diag0c_data->hdr.count = count;
*data = diag0c_data;
*data_free_ptr = diag0c_data;
*size = diag0c_data->hdr.len + sizeof(struct hypfs_diag0c_hdr);
return 0;
}
/*
* Hypfs DBFS file structure
*/
static struct hypfs_dbfs_file dbfs_file_0c = {
.name = "diag_0c",
.data_create = dbfs_diag0c_create,
.data_free = dbfs_diag0c_free,
};
/*
* Initialize diag 0c interface for z/VM
*/
int __init hypfs_diag0c_init(void)
{
if (!MACHINE_IS_VM)
return 0;
return hypfs_dbfs_create_file(&dbfs_file_0c);
}
/*
* Shutdown diag 0c interface for z/VM
*/
void hypfs_diag0c_exit(void)
{
if (!MACHINE_IS_VM)
return;
hypfs_dbfs_remove_file(&dbfs_file_0c);
}
|