93794e12527d8d2a9d396f866697118e5689b4c4
[sfrench/cifs-2.6.git] / arch / powerpc / platforms / pseries / vas.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright 2020-21 IBM Corp.
4  */
5
6 #define pr_fmt(fmt) "vas: " fmt
7
8 #include <linux/module.h>
9 #include <linux/kernel.h>
10 #include <linux/export.h>
11 #include <linux/types.h>
12 #include <linux/delay.h>
13 #include <asm/machdep.h>
14 #include <asm/hvcall.h>
15 #include <asm/plpar_wrappers.h>
16 #include <asm/vas.h>
17 #include "vas.h"
18
19 #define VAS_INVALID_WIN_ADDRESS 0xFFFFFFFFFFFFFFFFul
20 #define VAS_DEFAULT_DOMAIN_ID   0xFFFFFFFFFFFFFFFFul
21 /* The hypervisor allows one credit per window right now */
22 #define DEF_WIN_CREDS           1
23
24 static struct vas_all_caps caps_all;
25 static bool copypaste_feat;
26
27 static struct vas_caps vascaps[VAS_MAX_FEAT_TYPE];
28
29 static long hcall_return_busy_check(long rc)
30 {
31         /* Check if we are stalled for some time */
32         if (H_IS_LONG_BUSY(rc)) {
33                 msleep(get_longbusy_msecs(rc));
34                 rc = H_BUSY;
35         } else if (rc == H_BUSY) {
36                 cond_resched();
37         }
38
39         return rc;
40 }
41
42 /*
43  * Allocate VAS window hcall
44  */
45 static int h_allocate_vas_window(struct pseries_vas_window *win, u64 *domain,
46                                      u8 wintype, u16 credits)
47 {
48         long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
49         long rc;
50
51         do {
52                 rc = plpar_hcall9(H_ALLOCATE_VAS_WINDOW, retbuf, wintype,
53                                   credits, domain[0], domain[1], domain[2],
54                                   domain[3], domain[4], domain[5]);
55
56                 rc = hcall_return_busy_check(rc);
57         } while (rc == H_BUSY);
58
59         if (rc == H_SUCCESS) {
60                 if (win->win_addr == VAS_INVALID_WIN_ADDRESS) {
61                         pr_err("H_ALLOCATE_VAS_WINDOW: COPY/PASTE is not supported\n");
62                         return -ENOTSUPP;
63                 }
64                 win->vas_win.winid = retbuf[0];
65                 win->win_addr = retbuf[1];
66                 win->complete_irq = retbuf[2];
67                 win->fault_irq = retbuf[3];
68                 return 0;
69         }
70
71         pr_err("H_ALLOCATE_VAS_WINDOW error: %ld, wintype: %u, credits: %u\n",
72                 rc, wintype, credits);
73
74         return -EIO;
75 }
76
77 /*
78  * Deallocate VAS window hcall.
79  */
80 static int h_deallocate_vas_window(u64 winid)
81 {
82         long rc;
83
84         do {
85                 rc = plpar_hcall_norets(H_DEALLOCATE_VAS_WINDOW, winid);
86
87                 rc = hcall_return_busy_check(rc);
88         } while (rc == H_BUSY);
89
90         if (rc == H_SUCCESS)
91                 return 0;
92
93         pr_err("H_DEALLOCATE_VAS_WINDOW error: %ld, winid: %llu\n",
94                 rc, winid);
95         return -EIO;
96 }
97
98 /*
99  * Modify VAS window.
100  * After the window is opened with allocate window hcall, configure it
101  * with flags and LPAR PID before using.
102  */
103 static int h_modify_vas_window(struct pseries_vas_window *win)
104 {
105         long rc;
106         u32 lpid = mfspr(SPRN_PID);
107
108         /*
109          * AMR value is not supported in Linux VAS implementation.
110          * The hypervisor ignores it if 0 is passed.
111          */
112         do {
113                 rc = plpar_hcall_norets(H_MODIFY_VAS_WINDOW,
114                                         win->vas_win.winid, lpid, 0,
115                                         VAS_MOD_WIN_FLAGS, 0);
116
117                 rc = hcall_return_busy_check(rc);
118         } while (rc == H_BUSY);
119
120         if (rc == H_SUCCESS)
121                 return 0;
122
123         pr_err("H_MODIFY_VAS_WINDOW error: %ld, winid %u lpid %u\n",
124                         rc, win->vas_win.winid, lpid);
125         return -EIO;
126 }
127
128 /*
129  * This hcall is used to determine the capabilities from the hypervisor.
130  * @hcall: H_QUERY_VAS_CAPABILITIES or H_QUERY_NX_CAPABILITIES
131  * @query_type: If 0 is passed, the hypervisor returns the overall
132  *              capabilities which provides all feature(s) that are
133  *              available. Then query the hypervisor to get the
134  *              corresponding capabilities for the specific feature.
135  *              Example: H_QUERY_VAS_CAPABILITIES provides VAS GZIP QoS
136  *                      and VAS GZIP Default capabilities.
137  *                      H_QUERY_NX_CAPABILITIES provides NX GZIP
138  *                      capabilities.
139  * @result: Return buffer to save capabilities.
140  */
141 int h_query_vas_capabilities(const u64 hcall, u8 query_type, u64 result)
142 {
143         long rc;
144
145         rc = plpar_hcall_norets(hcall, query_type, result);
146
147         if (rc == H_SUCCESS)
148                 return 0;
149
150         pr_err("HCALL(%llx) error %ld, query_type %u, result buffer 0x%llx\n",
151                         hcall, rc, query_type, result);
152         return -EIO;
153 }
154
155 /*
156  * Get the specific capabilities based on the feature type.
157  * Right now supports GZIP default and GZIP QoS capabilities.
158  */
159 static int get_vas_capabilities(u8 feat, enum vas_cop_feat_type type,
160                                 struct hv_vas_cop_feat_caps *hv_caps)
161 {
162         struct vas_cop_feat_caps *caps;
163         struct vas_caps *vcaps;
164         int rc = 0;
165
166         vcaps = &vascaps[type];
167         memset(vcaps, 0, sizeof(*vcaps));
168         INIT_LIST_HEAD(&vcaps->list);
169
170         caps = &vcaps->caps;
171
172         rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, feat,
173                                           (u64)virt_to_phys(hv_caps));
174         if (rc)
175                 return rc;
176
177         caps->user_mode = hv_caps->user_mode;
178         if (!(caps->user_mode & VAS_COPY_PASTE_USER_MODE)) {
179                 pr_err("User space COPY/PASTE is not supported\n");
180                 return -ENOTSUPP;
181         }
182
183         caps->descriptor = be64_to_cpu(hv_caps->descriptor);
184         caps->win_type = hv_caps->win_type;
185         if (caps->win_type >= VAS_MAX_FEAT_TYPE) {
186                 pr_err("Unsupported window type %u\n", caps->win_type);
187                 return -EINVAL;
188         }
189         caps->max_lpar_creds = be16_to_cpu(hv_caps->max_lpar_creds);
190         caps->max_win_creds = be16_to_cpu(hv_caps->max_win_creds);
191         atomic_set(&caps->target_lpar_creds,
192                    be16_to_cpu(hv_caps->target_lpar_creds));
193         if (feat == VAS_GZIP_DEF_FEAT) {
194                 caps->def_lpar_creds = be16_to_cpu(hv_caps->def_lpar_creds);
195
196                 if (caps->max_win_creds < DEF_WIN_CREDS) {
197                         pr_err("Window creds(%u) > max allowed window creds(%u)\n",
198                                DEF_WIN_CREDS, caps->max_win_creds);
199                         return -EINVAL;
200                 }
201         }
202
203         copypaste_feat = true;
204
205         return 0;
206 }
207
208 static int __init pseries_vas_init(void)
209 {
210         struct hv_vas_cop_feat_caps *hv_cop_caps;
211         struct hv_vas_all_caps *hv_caps;
212         int rc;
213
214         /*
215          * Linux supports user space COPY/PASTE only with Radix
216          */
217         if (!radix_enabled()) {
218                 pr_err("API is supported only with radix page tables\n");
219                 return -ENOTSUPP;
220         }
221
222         hv_caps = kmalloc(sizeof(*hv_caps), GFP_KERNEL);
223         if (!hv_caps)
224                 return -ENOMEM;
225         /*
226          * Get VAS overall capabilities by passing 0 to feature type.
227          */
228         rc = h_query_vas_capabilities(H_QUERY_VAS_CAPABILITIES, 0,
229                                           (u64)virt_to_phys(hv_caps));
230         if (rc)
231                 goto out;
232
233         caps_all.descriptor = be64_to_cpu(hv_caps->descriptor);
234         caps_all.feat_type = be64_to_cpu(hv_caps->feat_type);
235
236         hv_cop_caps = kmalloc(sizeof(*hv_cop_caps), GFP_KERNEL);
237         if (!hv_cop_caps) {
238                 rc = -ENOMEM;
239                 goto out;
240         }
241         /*
242          * QOS capabilities available
243          */
244         if (caps_all.feat_type & VAS_GZIP_QOS_FEAT_BIT) {
245                 rc = get_vas_capabilities(VAS_GZIP_QOS_FEAT,
246                                           VAS_GZIP_QOS_FEAT_TYPE, hv_cop_caps);
247
248                 if (rc)
249                         goto out_cop;
250         }
251         /*
252          * Default capabilities available
253          */
254         if (caps_all.feat_type & VAS_GZIP_DEF_FEAT_BIT) {
255                 rc = get_vas_capabilities(VAS_GZIP_DEF_FEAT,
256                                           VAS_GZIP_DEF_FEAT_TYPE, hv_cop_caps);
257                 if (rc)
258                         goto out_cop;
259         }
260
261         pr_info("GZIP feature is available\n");
262
263 out_cop:
264         kfree(hv_cop_caps);
265 out:
266         kfree(hv_caps);
267         return rc;
268 }
269 machine_device_initcall(pseries, pseries_vas_init);