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 */ 2134c0dad7SMichael Holzheu static void diag0c_fn(void *data) 2234c0dad7SMichael Holzheu { 23a80313ffSGerald Schaefer diag_stat_inc(DIAG_STAT_X00C); 24a80313ffSGerald Schaefer diag_dma_ops.diag0c(((void **) data)[smp_processor_id()]); 2534c0dad7SMichael Holzheu } 2634c0dad7SMichael Holzheu 2734c0dad7SMichael Holzheu /* 2834c0dad7SMichael Holzheu * Allocate buffer and store diag 0c data 2934c0dad7SMichael Holzheu */ 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 36*a73de293SSebastian 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) 41*a73de293SSebastian 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); 57*a73de293SSebastian Andrzej Siewior cpus_read_unlock(); 5834c0dad7SMichael Holzheu return diag0c_data; 5934c0dad7SMichael Holzheu 6034c0dad7SMichael Holzheu fail_kfree_cpu_vec: 6134c0dad7SMichael Holzheu kfree(cpu_vec); 62*a73de293SSebastian Andrzej Siewior fail_unlock_cpus: 63*a73de293SSebastian 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 */ 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 */ 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 */ 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 */ 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