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