xref: /openbmc/linux/arch/s390/hypfs/hypfs_diag0c.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
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