18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Hypervisor filesystem for Linux on s390
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Diag 0C implementation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2014
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/cpu.h>
128c2ecf20Sopenharmony_ci#include <asm/diag.h>
138c2ecf20Sopenharmony_ci#include <asm/hypfs.h>
148c2ecf20Sopenharmony_ci#include "hypfs.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define DBFS_D0C_HDR_VERSION 0
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/*
198c2ecf20Sopenharmony_ci * Get hypfs_diag0c_entry from CPU vector and store diag0c data
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_cistatic void diag0c_fn(void *data)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	diag_stat_inc(DIAG_STAT_X00C);
248c2ecf20Sopenharmony_ci	diag_dma_ops.diag0c(((void **) data)[smp_processor_id()]);
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * Allocate buffer and store diag 0c data
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_cistatic void *diag0c_store(unsigned int *count)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct hypfs_diag0c_data *diag0c_data;
338c2ecf20Sopenharmony_ci	unsigned int cpu_count, cpu, i;
348c2ecf20Sopenharmony_ci	void **cpu_vec;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	get_online_cpus();
378c2ecf20Sopenharmony_ci	cpu_count = num_online_cpus();
388c2ecf20Sopenharmony_ci	cpu_vec = kmalloc_array(num_possible_cpus(), sizeof(*cpu_vec),
398c2ecf20Sopenharmony_ci				GFP_KERNEL);
408c2ecf20Sopenharmony_ci	if (!cpu_vec)
418c2ecf20Sopenharmony_ci		goto fail_put_online_cpus;
428c2ecf20Sopenharmony_ci	/* Note: Diag 0c needs 8 byte alignment and real storage */
438c2ecf20Sopenharmony_ci	diag0c_data = kzalloc(struct_size(diag0c_data, entry, cpu_count),
448c2ecf20Sopenharmony_ci			      GFP_KERNEL | GFP_DMA);
458c2ecf20Sopenharmony_ci	if (!diag0c_data)
468c2ecf20Sopenharmony_ci		goto fail_kfree_cpu_vec;
478c2ecf20Sopenharmony_ci	i = 0;
488c2ecf20Sopenharmony_ci	/* Fill CPU vector for each online CPU */
498c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu) {
508c2ecf20Sopenharmony_ci		diag0c_data->entry[i].cpu = cpu;
518c2ecf20Sopenharmony_ci		cpu_vec[cpu] = &diag0c_data->entry[i++];
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci	/* Collect data all CPUs */
548c2ecf20Sopenharmony_ci	on_each_cpu(diag0c_fn, cpu_vec, 1);
558c2ecf20Sopenharmony_ci	*count = cpu_count;
568c2ecf20Sopenharmony_ci	kfree(cpu_vec);
578c2ecf20Sopenharmony_ci	put_online_cpus();
588c2ecf20Sopenharmony_ci	return diag0c_data;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cifail_kfree_cpu_vec:
618c2ecf20Sopenharmony_ci	kfree(cpu_vec);
628c2ecf20Sopenharmony_cifail_put_online_cpus:
638c2ecf20Sopenharmony_ci	put_online_cpus();
648c2ecf20Sopenharmony_ci	return ERR_PTR(-ENOMEM);
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci/*
688c2ecf20Sopenharmony_ci * Hypfs DBFS callback: Free diag 0c data
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic void dbfs_diag0c_free(const void *data)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	kfree(data);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/*
768c2ecf20Sopenharmony_ci * Hypfs DBFS callback: Create diag 0c data
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_cistatic int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct hypfs_diag0c_data *diag0c_data;
818c2ecf20Sopenharmony_ci	unsigned int count;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	diag0c_data = diag0c_store(&count);
848c2ecf20Sopenharmony_ci	if (IS_ERR(diag0c_data))
858c2ecf20Sopenharmony_ci		return PTR_ERR(diag0c_data);
868c2ecf20Sopenharmony_ci	memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr));
878c2ecf20Sopenharmony_ci	get_tod_clock_ext(diag0c_data->hdr.tod_ext);
888c2ecf20Sopenharmony_ci	diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry);
898c2ecf20Sopenharmony_ci	diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION;
908c2ecf20Sopenharmony_ci	diag0c_data->hdr.count = count;
918c2ecf20Sopenharmony_ci	*data = diag0c_data;
928c2ecf20Sopenharmony_ci	*data_free_ptr = diag0c_data;
938c2ecf20Sopenharmony_ci	*size = diag0c_data->hdr.len + sizeof(struct hypfs_diag0c_hdr);
948c2ecf20Sopenharmony_ci	return 0;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/*
988c2ecf20Sopenharmony_ci * Hypfs DBFS file structure
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_cistatic struct hypfs_dbfs_file dbfs_file_0c = {
1018c2ecf20Sopenharmony_ci	.name		= "diag_0c",
1028c2ecf20Sopenharmony_ci	.data_create	= dbfs_diag0c_create,
1038c2ecf20Sopenharmony_ci	.data_free	= dbfs_diag0c_free,
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/*
1078c2ecf20Sopenharmony_ci * Initialize diag 0c interface for z/VM
1088c2ecf20Sopenharmony_ci */
1098c2ecf20Sopenharmony_ciint __init hypfs_diag0c_init(void)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	if (!MACHINE_IS_VM)
1128c2ecf20Sopenharmony_ci		return 0;
1138c2ecf20Sopenharmony_ci	hypfs_dbfs_create_file(&dbfs_file_0c);
1148c2ecf20Sopenharmony_ci	return 0;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/*
1188c2ecf20Sopenharmony_ci * Shutdown diag 0c interface for z/VM
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_civoid hypfs_diag0c_exit(void)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	if (!MACHINE_IS_VM)
1238c2ecf20Sopenharmony_ci		return;
1248c2ecf20Sopenharmony_ci	hypfs_dbfs_remove_file(&dbfs_file_0c);
1258c2ecf20Sopenharmony_ci}
126