Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | // SPDX-License-Identifier: GPL-2.0 /* * Hypervisor filesystem for Linux on s390 * * Diag 0C implementation * * Copyright IBM Corp. 2014 */ #include <linux/slab.h> #include <linux/cpu.h> #include <asm/diag.h> #include <asm/hypfs.h> #include "hypfs.h" #define DBFS_D0C_HDR_VERSION 0 /* * Get hypfs_diag0c_entry from CPU vector and store diag0c data */ static void diag0c_fn(void *data) { diag_stat_inc(DIAG_STAT_X00C); diag_amode31_ops.diag0c(((void **)data)[smp_processor_id()]); } /* * Allocate buffer and store diag 0c data */ static void *diag0c_store(unsigned int *count) { struct hypfs_diag0c_data *diag0c_data; unsigned int cpu_count, cpu, i; void **cpu_vec; cpus_read_lock(); cpu_count = num_online_cpus(); cpu_vec = kmalloc_array(num_possible_cpus(), sizeof(*cpu_vec), GFP_KERNEL); if (!cpu_vec) goto fail_unlock_cpus; /* Note: Diag 0c needs 8 byte alignment and real storage */ diag0c_data = kzalloc(struct_size(diag0c_data, entry, cpu_count), GFP_KERNEL | GFP_DMA); if (!diag0c_data) goto fail_kfree_cpu_vec; i = 0; /* Fill CPU vector for each online CPU */ for_each_online_cpu(cpu) { diag0c_data->entry[i].cpu = cpu; cpu_vec[cpu] = &diag0c_data->entry[i++]; } /* Collect data all CPUs */ on_each_cpu(diag0c_fn, cpu_vec, 1); *count = cpu_count; kfree(cpu_vec); cpus_read_unlock(); return diag0c_data; fail_kfree_cpu_vec: kfree(cpu_vec); fail_unlock_cpus: cpus_read_unlock(); return ERR_PTR(-ENOMEM); } /* * Hypfs DBFS callback: Free diag 0c data */ static void dbfs_diag0c_free(const void *data) { kfree(data); } /* * Hypfs DBFS callback: Create diag 0c data */ static int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size) { struct hypfs_diag0c_data *diag0c_data; unsigned int count; diag0c_data = diag0c_store(&count); if (IS_ERR(diag0c_data)) return PTR_ERR(diag0c_data); memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr)); store_tod_clock_ext((union tod_clock *)diag0c_data->hdr.tod_ext); diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry); diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION; diag0c_data->hdr.count = count; *data = diag0c_data; *data_free_ptr = diag0c_data; *size = diag0c_data->hdr.len + sizeof(struct hypfs_diag0c_hdr); return 0; } /* * Hypfs DBFS file structure */ static struct hypfs_dbfs_file dbfs_file_0c = { .name = "diag_0c", .data_create = dbfs_diag0c_create, .data_free = dbfs_diag0c_free, }; /* * Initialize diag 0c interface for z/VM */ int __init hypfs_diag0c_init(void) { if (!MACHINE_IS_VM) return 0; hypfs_dbfs_create_file(&dbfs_file_0c); return 0; } /* * Shutdown diag 0c interface for z/VM */ void hypfs_diag0c_exit(void) { if (!MACHINE_IS_VM) return; hypfs_dbfs_remove_file(&dbfs_file_0c); } |