hc
2023-12-06 d38611ca164021d018c1b23eee65bbebc09c63e0
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_dma_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;
 
   get_online_cpus();
   cpu_count = num_online_cpus();
   cpu_vec = kmalloc_array(num_possible_cpus(), sizeof(*cpu_vec),
               GFP_KERNEL);
   if (!cpu_vec)
       goto fail_put_online_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);
   put_online_cpus();
   return diag0c_data;
 
fail_kfree_cpu_vec:
   kfree(cpu_vec);
fail_put_online_cpus:
   put_online_cpus();
   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));
   get_tod_clock_ext(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);
}