1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
234c0dad7SMichael Holzheu /*
334c0dad7SMichael Holzheu * Hypervisor filesystem for Linux on s390
434c0dad7SMichael Holzheu *
534c0dad7SMichael Holzheu * Diag 0C implementation
634c0dad7SMichael Holzheu *
734c0dad7SMichael Holzheu * Copyright IBM Corp. 2014
834c0dad7SMichael Holzheu */
934c0dad7SMichael Holzheu
1034c0dad7SMichael Holzheu #include <linux/slab.h>
1134c0dad7SMichael Holzheu #include <linux/cpu.h>
121ec2772eSMartin Schwidefsky #include <asm/diag.h>
1334c0dad7SMichael Holzheu #include <asm/hypfs.h>
1434c0dad7SMichael Holzheu #include "hypfs.h"
1534c0dad7SMichael Holzheu
1634c0dad7SMichael Holzheu #define DBFS_D0C_HDR_VERSION 0
1734c0dad7SMichael Holzheu
1834c0dad7SMichael Holzheu /*
1934c0dad7SMichael Holzheu * Get hypfs_diag0c_entry from CPU vector and store diag0c data
2034c0dad7SMichael Holzheu */
diag0c_fn(void * data)2134c0dad7SMichael Holzheu static void diag0c_fn(void *data)
2234c0dad7SMichael Holzheu {
23a80313ffSGerald Schaefer diag_stat_inc(DIAG_STAT_X00C);
24*c78d0c74SHeiko Carstens diag_amode31_ops.diag0c(((void **)data)[smp_processor_id()]);
2534c0dad7SMichael Holzheu }
2634c0dad7SMichael Holzheu
2734c0dad7SMichael Holzheu /*
2834c0dad7SMichael Holzheu * Allocate buffer and store diag 0c data
2934c0dad7SMichael Holzheu */
diag0c_store(unsigned int * count)3034c0dad7SMichael Holzheu static void *diag0c_store(unsigned int *count)
3134c0dad7SMichael Holzheu {
3234c0dad7SMichael Holzheu struct hypfs_diag0c_data *diag0c_data;
3334c0dad7SMichael Holzheu unsigned int cpu_count, cpu, i;
3434c0dad7SMichael Holzheu void **cpu_vec;
3534c0dad7SMichael Holzheu
36a73de293SSebastian Andrzej Siewior cpus_read_lock();
3734c0dad7SMichael Holzheu cpu_count = num_online_cpus();
386da2ec56SKees Cook cpu_vec = kmalloc_array(num_possible_cpus(), sizeof(*cpu_vec),
396da2ec56SKees Cook GFP_KERNEL);
4034c0dad7SMichael Holzheu if (!cpu_vec)
41a73de293SSebastian Andrzej Siewior goto fail_unlock_cpus;
4234c0dad7SMichael Holzheu /* Note: Diag 0c needs 8 byte alignment and real storage */
43c6ac8754SGustavo A. R. Silva diag0c_data = kzalloc(struct_size(diag0c_data, entry, cpu_count),
4434c0dad7SMichael Holzheu GFP_KERNEL | GFP_DMA);
4534c0dad7SMichael Holzheu if (!diag0c_data)
4634c0dad7SMichael Holzheu goto fail_kfree_cpu_vec;
4734c0dad7SMichael Holzheu i = 0;
4834c0dad7SMichael Holzheu /* Fill CPU vector for each online CPU */
4934c0dad7SMichael Holzheu for_each_online_cpu(cpu) {
5034c0dad7SMichael Holzheu diag0c_data->entry[i].cpu = cpu;
5134c0dad7SMichael Holzheu cpu_vec[cpu] = &diag0c_data->entry[i++];
5234c0dad7SMichael Holzheu }
5334c0dad7SMichael Holzheu /* Collect data all CPUs */
5434c0dad7SMichael Holzheu on_each_cpu(diag0c_fn, cpu_vec, 1);
5534c0dad7SMichael Holzheu *count = cpu_count;
5634c0dad7SMichael Holzheu kfree(cpu_vec);
57a73de293SSebastian Andrzej Siewior cpus_read_unlock();
5834c0dad7SMichael Holzheu return diag0c_data;
5934c0dad7SMichael Holzheu
6034c0dad7SMichael Holzheu fail_kfree_cpu_vec:
6134c0dad7SMichael Holzheu kfree(cpu_vec);
62a73de293SSebastian Andrzej Siewior fail_unlock_cpus:
63a73de293SSebastian Andrzej Siewior cpus_read_unlock();
6434c0dad7SMichael Holzheu return ERR_PTR(-ENOMEM);
6534c0dad7SMichael Holzheu }
6634c0dad7SMichael Holzheu
6734c0dad7SMichael Holzheu /*
6834c0dad7SMichael Holzheu * Hypfs DBFS callback: Free diag 0c data
6934c0dad7SMichael Holzheu */
dbfs_diag0c_free(const void * data)7034c0dad7SMichael Holzheu static void dbfs_diag0c_free(const void *data)
7134c0dad7SMichael Holzheu {
7234c0dad7SMichael Holzheu kfree(data);
7334c0dad7SMichael Holzheu }
7434c0dad7SMichael Holzheu
7534c0dad7SMichael Holzheu /*
7634c0dad7SMichael Holzheu * Hypfs DBFS callback: Create diag 0c data
7734c0dad7SMichael Holzheu */
dbfs_diag0c_create(void ** data,void ** data_free_ptr,size_t * size)7834c0dad7SMichael Holzheu static int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size)
7934c0dad7SMichael Holzheu {
8034c0dad7SMichael Holzheu struct hypfs_diag0c_data *diag0c_data;
8134c0dad7SMichael Holzheu unsigned int count;
8234c0dad7SMichael Holzheu
8334c0dad7SMichael Holzheu diag0c_data = diag0c_store(&count);
8434c0dad7SMichael Holzheu if (IS_ERR(diag0c_data))
8534c0dad7SMichael Holzheu return PTR_ERR(diag0c_data);
8634c0dad7SMichael Holzheu memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr));
8701f224b9SHeiko Carstens store_tod_clock_ext((union tod_clock *)diag0c_data->hdr.tod_ext);
8834c0dad7SMichael Holzheu diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry);
8934c0dad7SMichael Holzheu diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION;
9034c0dad7SMichael Holzheu diag0c_data->hdr.count = count;
9134c0dad7SMichael Holzheu *data = diag0c_data;
9234c0dad7SMichael Holzheu *data_free_ptr = diag0c_data;
9334c0dad7SMichael Holzheu *size = diag0c_data->hdr.len + sizeof(struct hypfs_diag0c_hdr);
9434c0dad7SMichael Holzheu return 0;
9534c0dad7SMichael Holzheu }
9634c0dad7SMichael Holzheu
9734c0dad7SMichael Holzheu /*
9834c0dad7SMichael Holzheu * Hypfs DBFS file structure
9934c0dad7SMichael Holzheu */
10034c0dad7SMichael Holzheu static struct hypfs_dbfs_file dbfs_file_0c = {
10134c0dad7SMichael Holzheu .name = "diag_0c",
10234c0dad7SMichael Holzheu .data_create = dbfs_diag0c_create,
10334c0dad7SMichael Holzheu .data_free = dbfs_diag0c_free,
10434c0dad7SMichael Holzheu };
10534c0dad7SMichael Holzheu
10634c0dad7SMichael Holzheu /*
10734c0dad7SMichael Holzheu * Initialize diag 0c interface for z/VM
10834c0dad7SMichael Holzheu */
hypfs_diag0c_init(void)10934c0dad7SMichael Holzheu int __init hypfs_diag0c_init(void)
11034c0dad7SMichael Holzheu {
11134c0dad7SMichael Holzheu if (!MACHINE_IS_VM)
11234c0dad7SMichael Holzheu return 0;
113f36108c4SGreg Kroah-Hartman hypfs_dbfs_create_file(&dbfs_file_0c);
114f36108c4SGreg Kroah-Hartman return 0;
11534c0dad7SMichael Holzheu }
11634c0dad7SMichael Holzheu
11734c0dad7SMichael Holzheu /*
11834c0dad7SMichael Holzheu * Shutdown diag 0c interface for z/VM
11934c0dad7SMichael Holzheu */
hypfs_diag0c_exit(void)12034c0dad7SMichael Holzheu void hypfs_diag0c_exit(void)
12134c0dad7SMichael Holzheu {
12234c0dad7SMichael Holzheu if (!MACHINE_IS_VM)
12334c0dad7SMichael Holzheu return;
12434c0dad7SMichael Holzheu hypfs_dbfs_remove_file(&dbfs_file_0c);
12534c0dad7SMichael Holzheu }
126