aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Barrat <fbarrat@linux.vnet.ibm.com>2018-01-23 06:31:40 -0500
committerMichael Ellerman <mpe@ellerman.id.au>2018-01-23 19:42:57 -0500
commit2cb3d64b26984703a6bb80e66adcc3727ad37f9f (patch)
tree60e6328bbe1f183dc87704366a1723cc1d81f34f
parent6914c757118e2a60ba826d9959ccf5532779781b (diff)
powerpc/powernv: Capture actag information for the device
In the opencapi protocol, host memory contexts are referenced by a 'actag'. During setup, a driver must tell the device how many actags it can used, and what values are acceptable. On POWER9, the NPU can handle 64 actags per link, so they must be shared between all the PCI functions of the link. To get a global picture of how many actags are used by each AFU of every function, we capture some data at the end of PCI enumeration, so that actags can be shared fairly if needed. This is not powernv specific per say, but rather a consequence of the opencapi configuration specification being quite general. The number of available actags on POWER9 makes it more likely to be hit. This is somewhat mitigated by the fact that existing AFUs are coded by requesting a reasonable count of actags and existing devices carry only one AFU. Signed-off-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r--arch/powerpc/include/asm/pnv-ocxl.h4
-rw-r--r--arch/powerpc/platforms/powernv/ocxl.c305
-rw-r--r--include/misc/ocxl-config.h45
3 files changed, 354 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/pnv-ocxl.h b/arch/powerpc/include/asm/pnv-ocxl.h
index 36868d49aeed..398d05b30600 100644
--- a/arch/powerpc/include/asm/pnv-ocxl.h
+++ b/arch/powerpc/include/asm/pnv-ocxl.h
@@ -9,6 +9,10 @@
9#define PNV_OCXL_TL_BITS_PER_RATE 4 9#define PNV_OCXL_TL_BITS_PER_RATE 4
10#define PNV_OCXL_TL_RATE_BUF_SIZE ((PNV_OCXL_TL_MAX_TEMPLATE+1) * PNV_OCXL_TL_BITS_PER_RATE / 8) 10#define PNV_OCXL_TL_RATE_BUF_SIZE ((PNV_OCXL_TL_MAX_TEMPLATE+1) * PNV_OCXL_TL_BITS_PER_RATE / 8)
11 11
12extern int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled,
13 u16 *supported);
14extern int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count);
15
12extern int pnv_ocxl_get_tl_cap(struct pci_dev *dev, long *cap, 16extern int pnv_ocxl_get_tl_cap(struct pci_dev *dev, long *cap,
13 char *rate_buf, int rate_buf_size); 17 char *rate_buf, int rate_buf_size);
14extern int pnv_ocxl_set_tl_conf(struct pci_dev *dev, long cap, 18extern int pnv_ocxl_set_tl_conf(struct pci_dev *dev, long cap,
diff --git a/arch/powerpc/platforms/powernv/ocxl.c b/arch/powerpc/platforms/powernv/ocxl.c
index d61186805a07..1faaa4ef6903 100644
--- a/arch/powerpc/platforms/powernv/ocxl.c
+++ b/arch/powerpc/platforms/powernv/ocxl.c
@@ -2,13 +2,318 @@
2// Copyright 2017 IBM Corp. 2// Copyright 2017 IBM Corp.
3#include <asm/pnv-ocxl.h> 3#include <asm/pnv-ocxl.h>
4#include <asm/opal.h> 4#include <asm/opal.h>
5#include <misc/ocxl-config.h>
5#include "pci.h" 6#include "pci.h"
6 7
7#define PNV_OCXL_TL_P9_RECV_CAP 0x000000000000000Full 8#define PNV_OCXL_TL_P9_RECV_CAP 0x000000000000000Full
9#define PNV_OCXL_ACTAG_MAX 64
8/* PASIDs are 20-bit, but on P9, NPU can only handle 15 bits */ 10/* PASIDs are 20-bit, but on P9, NPU can only handle 15 bits */
9#define PNV_OCXL_PASID_BITS 15 11#define PNV_OCXL_PASID_BITS 15
10#define PNV_OCXL_PASID_MAX ((1 << PNV_OCXL_PASID_BITS) - 1) 12#define PNV_OCXL_PASID_MAX ((1 << PNV_OCXL_PASID_BITS) - 1)
11 13
14#define AFU_PRESENT (1 << 31)
15#define AFU_INDEX_MASK 0x3F000000
16#define AFU_INDEX_SHIFT 24
17#define ACTAG_MASK 0xFFF
18
19
20struct actag_range {
21 u16 start;
22 u16 count;
23};
24
25struct npu_link {
26 struct list_head list;
27 int domain;
28 int bus;
29 int dev;
30 u16 fn_desired_actags[8];
31 struct actag_range fn_actags[8];
32 bool assignment_done;
33};
34static struct list_head links_list = LIST_HEAD_INIT(links_list);
35static DEFINE_MUTEX(links_list_lock);
36
37
38/*
39 * opencapi actags handling:
40 *
41 * When sending commands, the opencapi device references the memory
42 * context it's targeting with an 'actag', which is really an alias
43 * for a (BDF, pasid) combination. When it receives a command, the NPU
44 * must do a lookup of the actag to identify the memory context. The
45 * hardware supports a finite number of actags per link (64 for
46 * POWER9).
47 *
48 * The device can carry multiple functions, and each function can have
49 * multiple AFUs. Each AFU advertises in its config space the number
50 * of desired actags. The host must configure in the config space of
51 * the AFU how many actags the AFU is really allowed to use (which can
52 * be less than what the AFU desires).
53 *
54 * When a PCI function is probed by the driver, it has no visibility
55 * about the other PCI functions and how many actags they'd like,
56 * which makes it impossible to distribute actags fairly among AFUs.
57 *
58 * Unfortunately, the only way to know how many actags a function
59 * desires is by looking at the data for each AFU in the config space
60 * and add them up. Similarly, the only way to know how many actags
61 * all the functions of the physical device desire is by adding the
62 * previously computed function counts. Then we can match that against
63 * what the hardware supports.
64 *
65 * To get a comprehensive view, we use a 'pci fixup': at the end of
66 * PCI enumeration, each function counts how many actags its AFUs
67 * desire and we save it in a 'npu_link' structure, shared between all
68 * the PCI functions of a same device. Therefore, when the first
69 * function is probed by the driver, we can get an idea of the total
70 * count of desired actags for the device, and assign the actags to
71 * the AFUs, by pro-rating if needed.
72 */
73
74static int find_dvsec_from_pos(struct pci_dev *dev, int dvsec_id, int pos)
75{
76 int vsec = pos;
77 u16 vendor, id;
78
79 while ((vsec = pci_find_next_ext_capability(dev, vsec,
80 OCXL_EXT_CAP_ID_DVSEC))) {
81 pci_read_config_word(dev, vsec + OCXL_DVSEC_VENDOR_OFFSET,
82 &vendor);
83 pci_read_config_word(dev, vsec + OCXL_DVSEC_ID_OFFSET, &id);
84 if (vendor == PCI_VENDOR_ID_IBM && id == dvsec_id)
85 return vsec;
86 }
87 return 0;
88}
89
90static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx)
91{
92 int vsec = 0;
93 u8 idx;
94
95 while ((vsec = find_dvsec_from_pos(dev, OCXL_DVSEC_AFU_CTRL_ID,
96 vsec))) {
97 pci_read_config_byte(dev, vsec + OCXL_DVSEC_AFU_CTRL_AFU_IDX,
98 &idx);
99 if (idx == afu_idx)
100 return vsec;
101 }
102 return 0;
103}
104
105static int get_max_afu_index(struct pci_dev *dev, int *afu_idx)
106{
107 int pos;
108 u32 val;
109
110 pos = find_dvsec_from_pos(dev, OCXL_DVSEC_FUNC_ID, 0);
111 if (!pos)
112 return -ESRCH;
113
114 pci_read_config_dword(dev, pos + OCXL_DVSEC_FUNC_OFF_INDEX, &val);
115 if (val & AFU_PRESENT)
116 *afu_idx = (val & AFU_INDEX_MASK) >> AFU_INDEX_SHIFT;
117 else
118 *afu_idx = -1;
119 return 0;
120}
121
122static int get_actag_count(struct pci_dev *dev, int afu_idx, int *actag)
123{
124 int pos;
125 u16 actag_sup;
126
127 pos = find_dvsec_afu_ctrl(dev, afu_idx);
128 if (!pos)
129 return -ESRCH;
130
131 pci_read_config_word(dev, pos + OCXL_DVSEC_AFU_CTRL_ACTAG_SUP,
132 &actag_sup);
133 *actag = actag_sup & ACTAG_MASK;
134 return 0;
135}
136
137static struct npu_link *find_link(struct pci_dev *dev)
138{
139 struct npu_link *link;
140
141 list_for_each_entry(link, &links_list, list) {
142 /* The functions of a device all share the same link */
143 if (link->domain == pci_domain_nr(dev->bus) &&
144 link->bus == dev->bus->number &&
145 link->dev == PCI_SLOT(dev->devfn)) {
146 return link;
147 }
148 }
149
150 /* link doesn't exist yet. Allocate one */
151 link = kzalloc(sizeof(struct npu_link), GFP_KERNEL);
152 if (!link)
153 return NULL;
154 link->domain = pci_domain_nr(dev->bus);
155 link->bus = dev->bus->number;
156 link->dev = PCI_SLOT(dev->devfn);
157 list_add(&link->list, &links_list);
158 return link;
159}
160
161static void pnv_ocxl_fixup_actag(struct pci_dev *dev)
162{
163 struct pci_controller *hose = pci_bus_to_host(dev->bus);
164 struct pnv_phb *phb = hose->private_data;
165 struct npu_link *link;
166 int rc, afu_idx = -1, i, actag;
167
168 if (!machine_is(powernv))
169 return;
170
171 if (phb->type != PNV_PHB_NPU_OCAPI)
172 return;
173
174 mutex_lock(&links_list_lock);
175
176 link = find_link(dev);
177 if (!link) {
178 dev_warn(&dev->dev, "couldn't update actag information\n");
179 mutex_unlock(&links_list_lock);
180 return;
181 }
182
183 /*
184 * Check how many actags are desired for the AFUs under that
185 * function and add it to the count for the link
186 */
187 rc = get_max_afu_index(dev, &afu_idx);
188 if (rc) {
189 /* Most likely an invalid config space */
190 dev_dbg(&dev->dev, "couldn't find AFU information\n");
191 afu_idx = -1;
192 }
193
194 link->fn_desired_actags[PCI_FUNC(dev->devfn)] = 0;
195 for (i = 0; i <= afu_idx; i++) {
196 /*
197 * AFU index 'holes' are allowed. So don't fail if we
198 * can't read the actag info for an index
199 */
200 rc = get_actag_count(dev, i, &actag);
201 if (rc)
202 continue;
203 link->fn_desired_actags[PCI_FUNC(dev->devfn)] += actag;
204 }
205 dev_dbg(&dev->dev, "total actags for function: %d\n",
206 link->fn_desired_actags[PCI_FUNC(dev->devfn)]);
207
208 mutex_unlock(&links_list_lock);
209}
210DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pnv_ocxl_fixup_actag);
211
212static u16 assign_fn_actags(u16 desired, u16 total)
213{
214 u16 count;
215
216 if (total <= PNV_OCXL_ACTAG_MAX)
217 count = desired;
218 else
219 count = PNV_OCXL_ACTAG_MAX * desired / total;
220
221 return count;
222}
223
224static void assign_actags(struct npu_link *link)
225{
226 u16 actag_count, range_start = 0, total_desired = 0;
227 int i;
228
229 for (i = 0; i < 8; i++)
230 total_desired += link->fn_desired_actags[i];
231
232 for (i = 0; i < 8; i++) {
233 if (link->fn_desired_actags[i]) {
234 actag_count = assign_fn_actags(
235 link->fn_desired_actags[i],
236 total_desired);
237 link->fn_actags[i].start = range_start;
238 link->fn_actags[i].count = actag_count;
239 range_start += actag_count;
240 WARN_ON(range_start >= PNV_OCXL_ACTAG_MAX);
241 }
242 pr_debug("link %x:%x:%x fct %d actags: start=%d count=%d (desired=%d)\n",
243 link->domain, link->bus, link->dev, i,
244 link->fn_actags[i].start, link->fn_actags[i].count,
245 link->fn_desired_actags[i]);
246 }
247 link->assignment_done = true;
248}
249
250int pnv_ocxl_get_actag(struct pci_dev *dev, u16 *base, u16 *enabled,
251 u16 *supported)
252{
253 struct npu_link *link;
254
255 mutex_lock(&links_list_lock);
256
257 link = find_link(dev);
258 if (!link) {
259 dev_err(&dev->dev, "actag information not found\n");
260 mutex_unlock(&links_list_lock);
261 return -ENODEV;
262 }
263 /*
264 * On p9, we only have 64 actags per link, so they must be
265 * shared by all the functions of the same adapter. We counted
266 * the desired actag counts during PCI enumeration, so that we
267 * can allocate a pro-rated number of actags to each function.
268 */
269 if (!link->assignment_done)
270 assign_actags(link);
271
272 *base = link->fn_actags[PCI_FUNC(dev->devfn)].start;
273 *enabled = link->fn_actags[PCI_FUNC(dev->devfn)].count;
274 *supported = link->fn_desired_actags[PCI_FUNC(dev->devfn)];
275
276 mutex_unlock(&links_list_lock);
277 return 0;
278}
279EXPORT_SYMBOL_GPL(pnv_ocxl_get_actag);
280
281int pnv_ocxl_get_pasid_count(struct pci_dev *dev, int *count)
282{
283 struct npu_link *link;
284 int i, rc = -EINVAL;
285
286 /*
287 * The number of PASIDs (process address space ID) which can
288 * be used by a function depends on how many functions exist
289 * on the device. The NPU needs to be configured to know how
290 * many bits are available to PASIDs and how many are to be
291 * used by the function BDF indentifier.
292 *
293 * We only support one AFU-carrying function for now.
294 */
295 mutex_lock(&links_list_lock);
296
297 link = find_link(dev);
298 if (!link) {
299 dev_err(&dev->dev, "actag information not found\n");
300 mutex_unlock(&links_list_lock);
301 return -ENODEV;
302 }
303
304 for (i = 0; i < 8; i++)
305 if (link->fn_desired_actags[i] && (i == PCI_FUNC(dev->devfn))) {
306 *count = PNV_OCXL_PASID_MAX;
307 rc = 0;
308 break;
309 }
310
311 mutex_unlock(&links_list_lock);
312 dev_dbg(&dev->dev, "%d PASIDs available for function\n",
313 rc ? 0 : *count);
314 return rc;
315}
316EXPORT_SYMBOL_GPL(pnv_ocxl_get_pasid_count);
12 317
13static void set_templ_rate(unsigned int templ, unsigned int rate, char *buf) 318static void set_templ_rate(unsigned int templ, unsigned int rate, char *buf)
14{ 319{
diff --git a/include/misc/ocxl-config.h b/include/misc/ocxl-config.h
new file mode 100644
index 000000000000..3526fa996a22
--- /dev/null
+++ b/include/misc/ocxl-config.h
@@ -0,0 +1,45 @@
1// SPDX-License-Identifier: GPL-2.0+
2// Copyright 2017 IBM Corp.
3#ifndef _OCXL_CONFIG_H_
4#define _OCXL_CONFIG_H_
5
6/*
7 * This file lists the various constants used to read the
8 * configuration space of an opencapi adapter.
9 *
10 * It follows the specification for opencapi 3.0
11 */
12
13#define OCXL_EXT_CAP_ID_DVSEC 0x23
14
15#define OCXL_DVSEC_VENDOR_OFFSET 0x4
16#define OCXL_DVSEC_ID_OFFSET 0x8
17#define OCXL_DVSEC_TL_ID 0xF000
18#define OCXL_DVSEC_TL_BACKOFF_TIMERS 0x10
19#define OCXL_DVSEC_TL_RECV_CAP 0x18
20#define OCXL_DVSEC_TL_SEND_CAP 0x20
21#define OCXL_DVSEC_TL_RECV_RATE 0x30
22#define OCXL_DVSEC_TL_SEND_RATE 0x50
23#define OCXL_DVSEC_FUNC_ID 0xF001
24#define OCXL_DVSEC_FUNC_OFF_INDEX 0x08
25#define OCXL_DVSEC_FUNC_OFF_ACTAG 0x0C
26#define OCXL_DVSEC_AFU_INFO_ID 0xF003
27#define OCXL_DVSEC_AFU_INFO_AFU_IDX 0x0A
28#define OCXL_DVSEC_AFU_INFO_OFF 0x0C
29#define OCXL_DVSEC_AFU_INFO_DATA 0x10
30#define OCXL_DVSEC_AFU_CTRL_ID 0xF004
31#define OCXL_DVSEC_AFU_CTRL_AFU_IDX 0x0A
32#define OCXL_DVSEC_AFU_CTRL_TERM_PASID 0x0C
33#define OCXL_DVSEC_AFU_CTRL_ENABLE 0x0F
34#define OCXL_DVSEC_AFU_CTRL_PASID_SUP 0x10
35#define OCXL_DVSEC_AFU_CTRL_PASID_EN 0x11
36#define OCXL_DVSEC_AFU_CTRL_PASID_BASE 0x14
37#define OCXL_DVSEC_AFU_CTRL_ACTAG_SUP 0x18
38#define OCXL_DVSEC_AFU_CTRL_ACTAG_EN 0x1A
39#define OCXL_DVSEC_AFU_CTRL_ACTAG_BASE 0x1C
40#define OCXL_DVSEC_VENDOR_ID 0xF0F0
41#define OCXL_DVSEC_VENDOR_CFG_VERS 0x0C
42#define OCXL_DVSEC_VENDOR_TLX_VERS 0x10
43#define OCXL_DVSEC_VENDOR_DLX_VERS 0x20
44
45#endif /* _OCXL_CONFIG_H_ */