162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Hypervisor filesystem for Linux on s390 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Diag 0C implementation 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright IBM Corp. 2014 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/cpu.h> 1262306a36Sopenharmony_ci#include <asm/diag.h> 1362306a36Sopenharmony_ci#include <asm/hypfs.h> 1462306a36Sopenharmony_ci#include "hypfs.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define DBFS_D0C_HDR_VERSION 0 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * Get hypfs_diag0c_entry from CPU vector and store diag0c data 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_cistatic void diag0c_fn(void *data) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci diag_stat_inc(DIAG_STAT_X00C); 2462306a36Sopenharmony_ci diag_amode31_ops.diag0c(((void **)data)[smp_processor_id()]); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Allocate buffer and store diag 0c data 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic void *diag0c_store(unsigned int *count) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct hypfs_diag0c_data *diag0c_data; 3362306a36Sopenharmony_ci unsigned int cpu_count, cpu, i; 3462306a36Sopenharmony_ci void **cpu_vec; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci cpus_read_lock(); 3762306a36Sopenharmony_ci cpu_count = num_online_cpus(); 3862306a36Sopenharmony_ci cpu_vec = kmalloc_array(num_possible_cpus(), sizeof(*cpu_vec), 3962306a36Sopenharmony_ci GFP_KERNEL); 4062306a36Sopenharmony_ci if (!cpu_vec) 4162306a36Sopenharmony_ci goto fail_unlock_cpus; 4262306a36Sopenharmony_ci /* Note: Diag 0c needs 8 byte alignment and real storage */ 4362306a36Sopenharmony_ci diag0c_data = kzalloc(struct_size(diag0c_data, entry, cpu_count), 4462306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 4562306a36Sopenharmony_ci if (!diag0c_data) 4662306a36Sopenharmony_ci goto fail_kfree_cpu_vec; 4762306a36Sopenharmony_ci i = 0; 4862306a36Sopenharmony_ci /* Fill CPU vector for each online CPU */ 4962306a36Sopenharmony_ci for_each_online_cpu(cpu) { 5062306a36Sopenharmony_ci diag0c_data->entry[i].cpu = cpu; 5162306a36Sopenharmony_ci cpu_vec[cpu] = &diag0c_data->entry[i++]; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci /* Collect data all CPUs */ 5462306a36Sopenharmony_ci on_each_cpu(diag0c_fn, cpu_vec, 1); 5562306a36Sopenharmony_ci *count = cpu_count; 5662306a36Sopenharmony_ci kfree(cpu_vec); 5762306a36Sopenharmony_ci cpus_read_unlock(); 5862306a36Sopenharmony_ci return diag0c_data; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cifail_kfree_cpu_vec: 6162306a36Sopenharmony_ci kfree(cpu_vec); 6262306a36Sopenharmony_cifail_unlock_cpus: 6362306a36Sopenharmony_ci cpus_read_unlock(); 6462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * Hypfs DBFS callback: Free diag 0c data 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistatic void dbfs_diag0c_free(const void *data) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci kfree(data); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * Hypfs DBFS callback: Create diag 0c data 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_cistatic int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct hypfs_diag0c_data *diag0c_data; 8162306a36Sopenharmony_ci unsigned int count; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci diag0c_data = diag0c_store(&count); 8462306a36Sopenharmony_ci if (IS_ERR(diag0c_data)) 8562306a36Sopenharmony_ci return PTR_ERR(diag0c_data); 8662306a36Sopenharmony_ci memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr)); 8762306a36Sopenharmony_ci store_tod_clock_ext((union tod_clock *)diag0c_data->hdr.tod_ext); 8862306a36Sopenharmony_ci diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry); 8962306a36Sopenharmony_ci diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION; 9062306a36Sopenharmony_ci diag0c_data->hdr.count = count; 9162306a36Sopenharmony_ci *data = diag0c_data; 9262306a36Sopenharmony_ci *data_free_ptr = diag0c_data; 9362306a36Sopenharmony_ci *size = diag0c_data->hdr.len + sizeof(struct hypfs_diag0c_hdr); 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * Hypfs DBFS file structure 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_cistatic struct hypfs_dbfs_file dbfs_file_0c = { 10162306a36Sopenharmony_ci .name = "diag_0c", 10262306a36Sopenharmony_ci .data_create = dbfs_diag0c_create, 10362306a36Sopenharmony_ci .data_free = dbfs_diag0c_free, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* 10762306a36Sopenharmony_ci * Initialize diag 0c interface for z/VM 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ciint __init hypfs_diag0c_init(void) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci if (!MACHINE_IS_VM) 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci hypfs_dbfs_create_file(&dbfs_file_0c); 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * Shutdown diag 0c interface for z/VM 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_civoid hypfs_diag0c_exit(void) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci if (!MACHINE_IS_VM) 12362306a36Sopenharmony_ci return; 12462306a36Sopenharmony_ci hypfs_dbfs_remove_file(&dbfs_file_0c); 12562306a36Sopenharmony_ci} 126