linux/arch/s390/hypfs/hypfs_vm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 *    Hypervisor filesystem for Linux on s390. z/VM implementation.
   4 *
   5 *    Copyright IBM Corp. 2006
   6 *    Author(s): Michael Holzheu <holzheu@de.ibm.com>
   7 */
   8
   9#include <linux/types.h>
  10#include <linux/errno.h>
  11#include <linux/string.h>
  12#include <linux/vmalloc.h>
  13#include <asm/diag.h>
  14#include <asm/ebcdic.h>
  15#include <asm/timex.h>
  16#include "hypfs.h"
  17
  18#define NAME_LEN 8
  19#define DBFS_D2FC_HDR_VERSION 0
  20
  21static char local_guest[] = "        ";
  22static char all_guests[] = "*       ";
  23static char *guest_query;
  24
  25struct diag2fc_data {
  26        __u32 version;
  27        __u32 flags;
  28        __u64 used_cpu;
  29        __u64 el_time;
  30        __u64 mem_min_kb;
  31        __u64 mem_max_kb;
  32        __u64 mem_share_kb;
  33        __u64 mem_used_kb;
  34        __u32 pcpus;
  35        __u32 lcpus;
  36        __u32 vcpus;
  37        __u32 ocpus;
  38        __u32 cpu_max;
  39        __u32 cpu_shares;
  40        __u32 cpu_use_samp;
  41        __u32 cpu_delay_samp;
  42        __u32 page_wait_samp;
  43        __u32 idle_samp;
  44        __u32 other_samp;
  45        __u32 total_samp;
  46        char  guest_name[NAME_LEN];
  47};
  48
  49struct diag2fc_parm_list {
  50        char userid[NAME_LEN];
  51        char aci_grp[NAME_LEN];
  52        __u64 addr;
  53        __u32 size;
  54        __u32 fmt;
  55};
  56
  57static int diag2fc(int size, char* query, void *addr)
  58{
  59        unsigned long residual_cnt;
  60        unsigned long rc;
  61        struct diag2fc_parm_list parm_list;
  62
  63        memcpy(parm_list.userid, query, NAME_LEN);
  64        ASCEBC(parm_list.userid, NAME_LEN);
  65        parm_list.addr = (unsigned long) addr ;
  66        parm_list.size = size;
  67        parm_list.fmt = 0x02;
  68        memset(parm_list.aci_grp, 0x40, NAME_LEN);
  69        rc = -1;
  70
  71        diag_stat_inc(DIAG_STAT_X2FC);
  72        asm volatile(
  73                "       diag    %0,%1,0x2fc\n"
  74                "0:     nopr    %%r7\n"
  75                EX_TABLE(0b,0b)
  76                : "=d" (residual_cnt), "+d" (rc) : "0" (&parm_list) : "memory");
  77
  78        if ((rc != 0 ) && (rc != -2))
  79                return rc;
  80        else
  81                return -residual_cnt;
  82}
  83
  84/*
  85 * Allocate buffer for "query" and store diag 2fc at "offset"
  86 */
  87static void *diag2fc_store(char *query, unsigned int *count, int offset)
  88{
  89        void *data;
  90        int size;
  91
  92        do {
  93                size = diag2fc(0, query, NULL);
  94                if (size < 0)
  95                        return ERR_PTR(-EACCES);
  96                data = vmalloc(size + offset);
  97                if (!data)
  98                        return ERR_PTR(-ENOMEM);
  99                if (diag2fc(size, query, data + offset) == 0)
 100                        break;
 101                vfree(data);
 102        } while (1);
 103        *count = (size / sizeof(struct diag2fc_data));
 104
 105        return data;
 106}
 107
 108static void diag2fc_free(const void *data)
 109{
 110        vfree(data);
 111}
 112
 113#define ATTRIBUTE(dir, name, member) \
 114do { \
 115        void *rc; \
 116        rc = hypfs_create_u64(dir, name, member); \
 117        if (IS_ERR(rc)) \
 118                return PTR_ERR(rc); \
 119} while(0)
 120
 121static int hpyfs_vm_create_guest(struct dentry *systems_dir,
 122                                 struct diag2fc_data *data)
 123{
 124        char guest_name[NAME_LEN + 1] = {};
 125        struct dentry *guest_dir, *cpus_dir, *samples_dir, *mem_dir;
 126        int dedicated_flag, capped_value;
 127
 128        capped_value = (data->flags & 0x00000006) >> 1;
 129        dedicated_flag = (data->flags & 0x00000008) >> 3;
 130
 131        /* guest dir */
 132        memcpy(guest_name, data->guest_name, NAME_LEN);
 133        EBCASC(guest_name, NAME_LEN);
 134        strim(guest_name);
 135        guest_dir = hypfs_mkdir(systems_dir, guest_name);
 136        if (IS_ERR(guest_dir))
 137                return PTR_ERR(guest_dir);
 138        ATTRIBUTE(guest_dir, "onlinetime_us", data->el_time);
 139
 140        /* logical cpu information */
 141        cpus_dir = hypfs_mkdir(guest_dir, "cpus");
 142        if (IS_ERR(cpus_dir))
 143                return PTR_ERR(cpus_dir);
 144        ATTRIBUTE(cpus_dir, "cputime_us", data->used_cpu);
 145        ATTRIBUTE(cpus_dir, "capped", capped_value);
 146        ATTRIBUTE(cpus_dir, "dedicated", dedicated_flag);
 147        ATTRIBUTE(cpus_dir, "count", data->vcpus);
 148        /*
 149         * Note: The "weight_min" attribute got the wrong name.
 150         * The value represents the number of non-stopped (operating)
 151         * CPUS.
 152         */
 153        ATTRIBUTE(cpus_dir, "weight_min", data->ocpus);
 154        ATTRIBUTE(cpus_dir, "weight_max", data->cpu_max);
 155        ATTRIBUTE(cpus_dir, "weight_cur", data->cpu_shares);
 156
 157        /* memory information */
 158        mem_dir = hypfs_mkdir(guest_dir, "mem");
 159        if (IS_ERR(mem_dir))
 160                return PTR_ERR(mem_dir);
 161        ATTRIBUTE(mem_dir, "min_KiB", data->mem_min_kb);
 162        ATTRIBUTE(mem_dir, "max_KiB", data->mem_max_kb);
 163        ATTRIBUTE(mem_dir, "used_KiB", data->mem_used_kb);
 164        ATTRIBUTE(mem_dir, "share_KiB", data->mem_share_kb);
 165
 166        /* samples */
 167        samples_dir = hypfs_mkdir(guest_dir, "samples");
 168        if (IS_ERR(samples_dir))
 169                return PTR_ERR(samples_dir);
 170        ATTRIBUTE(samples_dir, "cpu_using", data->cpu_use_samp);
 171        ATTRIBUTE(samples_dir, "cpu_delay", data->cpu_delay_samp);
 172        ATTRIBUTE(samples_dir, "mem_delay", data->page_wait_samp);
 173        ATTRIBUTE(samples_dir, "idle", data->idle_samp);
 174        ATTRIBUTE(samples_dir, "other", data->other_samp);
 175        ATTRIBUTE(samples_dir, "total", data->total_samp);
 176        return 0;
 177}
 178
 179int hypfs_vm_create_files(struct dentry *root)
 180{
 181        struct dentry *dir, *file;
 182        struct diag2fc_data *data;
 183        unsigned int count = 0;
 184        int rc, i;
 185
 186        data = diag2fc_store(guest_query, &count, 0);
 187        if (IS_ERR(data))
 188                return PTR_ERR(data);
 189
 190        /* Hpervisor Info */
 191        dir = hypfs_mkdir(root, "hyp");
 192        if (IS_ERR(dir)) {
 193                rc = PTR_ERR(dir);
 194                goto failed;
 195        }
 196        file = hypfs_create_str(dir, "type", "z/VM Hypervisor");
 197        if (IS_ERR(file)) {
 198                rc = PTR_ERR(file);
 199                goto failed;
 200        }
 201
 202        /* physical cpus */
 203        dir = hypfs_mkdir(root, "cpus");
 204        if (IS_ERR(dir)) {
 205                rc = PTR_ERR(dir);
 206                goto failed;
 207        }
 208        file = hypfs_create_u64(dir, "count", data->lcpus);
 209        if (IS_ERR(file)) {
 210                rc = PTR_ERR(file);
 211                goto failed;
 212        }
 213
 214        /* guests */
 215        dir = hypfs_mkdir(root, "systems");
 216        if (IS_ERR(dir)) {
 217                rc = PTR_ERR(dir);
 218                goto failed;
 219        }
 220
 221        for (i = 0; i < count; i++) {
 222                rc = hpyfs_vm_create_guest(dir, &(data[i]));
 223                if (rc)
 224                        goto failed;
 225        }
 226        diag2fc_free(data);
 227        return 0;
 228
 229failed:
 230        diag2fc_free(data);
 231        return rc;
 232}
 233
 234struct dbfs_d2fc_hdr {
 235        u64     len;            /* Length of d2fc buffer without header */
 236        u16     version;        /* Version of header */
 237        char    tod_ext[STORE_CLOCK_EXT_SIZE]; /* TOD clock for d2fc */
 238        u64     count;          /* Number of VM guests in d2fc buffer */
 239        char    reserved[30];
 240} __attribute__ ((packed));
 241
 242struct dbfs_d2fc {
 243        struct dbfs_d2fc_hdr    hdr;    /* 64 byte header */
 244        char                    buf[];  /* d2fc buffer */
 245} __attribute__ ((packed));
 246
 247static int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size)
 248{
 249        struct dbfs_d2fc *d2fc;
 250        unsigned int count;
 251
 252        d2fc = diag2fc_store(guest_query, &count, sizeof(d2fc->hdr));
 253        if (IS_ERR(d2fc))
 254                return PTR_ERR(d2fc);
 255        get_tod_clock_ext(d2fc->hdr.tod_ext);
 256        d2fc->hdr.len = count * sizeof(struct diag2fc_data);
 257        d2fc->hdr.version = DBFS_D2FC_HDR_VERSION;
 258        d2fc->hdr.count = count;
 259        memset(&d2fc->hdr.reserved, 0, sizeof(d2fc->hdr.reserved));
 260        *data = d2fc;
 261        *data_free_ptr = d2fc;
 262        *size = d2fc->hdr.len + sizeof(struct dbfs_d2fc_hdr);
 263        return 0;
 264}
 265
 266static struct hypfs_dbfs_file dbfs_file_2fc = {
 267        .name           = "diag_2fc",
 268        .data_create    = dbfs_diag2fc_create,
 269        .data_free      = diag2fc_free,
 270};
 271
 272int hypfs_vm_init(void)
 273{
 274        if (!MACHINE_IS_VM)
 275                return 0;
 276        if (diag2fc(0, all_guests, NULL) > 0)
 277                guest_query = all_guests;
 278        else if (diag2fc(0, local_guest, NULL) > 0)
 279                guest_query = local_guest;
 280        else
 281                return -EACCES;
 282        hypfs_dbfs_create_file(&dbfs_file_2fc);
 283        return 0;
 284}
 285
 286void hypfs_vm_exit(void)
 287{
 288        if (!MACHINE_IS_VM)
 289                return;
 290        hypfs_dbfs_remove_file(&dbfs_file_2fc);
 291}
 292