]>
Commit | Line | Data |
---|---|---|
76ebe01f UK |
1 | /* |
2 | * CXL Flash Device Driver | |
3 | * | |
4 | * Written by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>, IBM Corporation | |
5 | * Uma Krishnan <ukrishn@linux.vnet.ibm.com>, IBM Corporation | |
6 | * | |
7 | * Copyright (C) 2018 IBM Corporation | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License | |
11 | * as published by the Free Software Foundation; either version | |
12 | * 2 of the License, or (at your option) any later version. | |
13 | */ | |
14 | ||
926a62f9 | 15 | #include <linux/file.h> |
429ebfa6 | 16 | #include <linux/idr.h> |
926a62f9 UK |
17 | #include <linux/module.h> |
18 | #include <linux/mount.h> | |
429ebfa6 | 19 | |
76ebe01f UK |
20 | #include <misc/ocxl.h> |
21 | ||
22 | #include "backend.h" | |
48e077db UK |
23 | #include "ocxl_hw.h" |
24 | ||
926a62f9 UK |
25 | /* |
26 | * Pseudo-filesystem to allocate inodes. | |
27 | */ | |
28 | ||
29 | #define OCXLFLASH_FS_MAGIC 0x1697698f | |
30 | ||
31 | static int ocxlflash_fs_cnt; | |
32 | static struct vfsmount *ocxlflash_vfs_mount; | |
33 | ||
34 | static const struct dentry_operations ocxlflash_fs_dops = { | |
35 | .d_dname = simple_dname, | |
36 | }; | |
37 | ||
38 | /* | |
39 | * ocxlflash_fs_mount() - mount the pseudo-filesystem | |
40 | * @fs_type: File system type. | |
41 | * @flags: Flags for the filesystem. | |
42 | * @dev_name: Device name associated with the filesystem. | |
43 | * @data: Data pointer. | |
44 | * | |
45 | * Return: pointer to the directory entry structure | |
46 | */ | |
47 | static struct dentry *ocxlflash_fs_mount(struct file_system_type *fs_type, | |
48 | int flags, const char *dev_name, | |
49 | void *data) | |
50 | { | |
51 | return mount_pseudo(fs_type, "ocxlflash:", NULL, &ocxlflash_fs_dops, | |
52 | OCXLFLASH_FS_MAGIC); | |
53 | } | |
54 | ||
55 | static struct file_system_type ocxlflash_fs_type = { | |
56 | .name = "ocxlflash", | |
57 | .owner = THIS_MODULE, | |
58 | .mount = ocxlflash_fs_mount, | |
59 | .kill_sb = kill_anon_super, | |
60 | }; | |
61 | ||
62 | /* | |
63 | * ocxlflash_release_mapping() - release the memory mapping | |
64 | * @ctx: Context whose mapping is to be released. | |
65 | */ | |
66 | static void ocxlflash_release_mapping(struct ocxlflash_context *ctx) | |
67 | { | |
68 | if (ctx->mapping) | |
69 | simple_release_fs(&ocxlflash_vfs_mount, &ocxlflash_fs_cnt); | |
70 | ctx->mapping = NULL; | |
71 | } | |
72 | ||
73 | /* | |
74 | * ocxlflash_getfile() - allocate pseudo filesystem, inode, and the file | |
75 | * @dev: Generic device of the host. | |
76 | * @name: Name of the pseudo filesystem. | |
77 | * @fops: File operations. | |
78 | * @priv: Private data. | |
79 | * @flags: Flags for the file. | |
80 | * | |
81 | * Return: pointer to the file on success, ERR_PTR on failure | |
82 | */ | |
83 | static struct file *ocxlflash_getfile(struct device *dev, const char *name, | |
84 | const struct file_operations *fops, | |
85 | void *priv, int flags) | |
86 | { | |
87 | struct qstr this; | |
88 | struct path path; | |
89 | struct file *file; | |
90 | struct inode *inode = NULL; | |
91 | int rc; | |
92 | ||
93 | if (fops->owner && !try_module_get(fops->owner)) { | |
94 | dev_err(dev, "%s: Owner does not exist\n", __func__); | |
95 | rc = -ENOENT; | |
96 | goto err1; | |
97 | } | |
98 | ||
99 | rc = simple_pin_fs(&ocxlflash_fs_type, &ocxlflash_vfs_mount, | |
100 | &ocxlflash_fs_cnt); | |
101 | if (unlikely(rc < 0)) { | |
102 | dev_err(dev, "%s: Cannot mount ocxlflash pseudofs rc=%d\n", | |
103 | __func__, rc); | |
104 | goto err2; | |
105 | } | |
106 | ||
107 | inode = alloc_anon_inode(ocxlflash_vfs_mount->mnt_sb); | |
108 | if (IS_ERR(inode)) { | |
109 | rc = PTR_ERR(inode); | |
110 | dev_err(dev, "%s: alloc_anon_inode failed rc=%d\n", | |
111 | __func__, rc); | |
112 | goto err3; | |
113 | } | |
114 | ||
115 | this.name = name; | |
116 | this.len = strlen(name); | |
117 | this.hash = 0; | |
118 | path.dentry = d_alloc_pseudo(ocxlflash_vfs_mount->mnt_sb, &this); | |
119 | if (!path.dentry) { | |
120 | dev_err(dev, "%s: d_alloc_pseudo failed\n", __func__); | |
121 | rc = -ENOMEM; | |
122 | goto err4; | |
123 | } | |
124 | ||
125 | path.mnt = mntget(ocxlflash_vfs_mount); | |
126 | d_instantiate(path.dentry, inode); | |
127 | ||
128 | file = alloc_file(&path, OPEN_FMODE(flags), fops); | |
129 | if (IS_ERR(file)) { | |
130 | rc = PTR_ERR(file); | |
131 | dev_err(dev, "%s: alloc_file failed rc=%d\n", | |
132 | __func__, rc); | |
133 | goto err5; | |
134 | } | |
135 | ||
136 | file->f_flags = flags & (O_ACCMODE | O_NONBLOCK); | |
137 | file->private_data = priv; | |
138 | out: | |
139 | return file; | |
140 | err5: | |
141 | path_put(&path); | |
142 | err4: | |
143 | iput(inode); | |
144 | err3: | |
145 | simple_release_fs(&ocxlflash_vfs_mount, &ocxlflash_fs_cnt); | |
146 | err2: | |
147 | module_put(fops->owner); | |
148 | err1: | |
149 | file = ERR_PTR(rc); | |
150 | goto out; | |
151 | } | |
152 | ||
012f394c UK |
153 | /** |
154 | * ocxlflash_psa_map() - map the process specific MMIO space | |
155 | * @ctx_cookie: Adapter context for which the mapping needs to be done. | |
156 | * | |
157 | * Return: MMIO pointer of the mapped region | |
158 | */ | |
159 | static void __iomem *ocxlflash_psa_map(void *ctx_cookie) | |
160 | { | |
161 | struct ocxlflash_context *ctx = ctx_cookie; | |
162 | ||
163 | return ioremap(ctx->psn_phys, ctx->psn_size); | |
164 | } | |
165 | ||
166 | /** | |
167 | * ocxlflash_psa_unmap() - unmap the process specific MMIO space | |
168 | * @addr: MMIO pointer to unmap. | |
169 | */ | |
170 | static void ocxlflash_psa_unmap(void __iomem *addr) | |
171 | { | |
172 | iounmap(addr); | |
173 | } | |
174 | ||
b18718c6 UK |
175 | /** |
176 | * ocxlflash_process_element() - get process element of the adapter context | |
177 | * @ctx_cookie: Adapter context associated with the process element. | |
178 | * | |
179 | * Return: process element of the adapter context | |
180 | */ | |
181 | static int ocxlflash_process_element(void *ctx_cookie) | |
182 | { | |
183 | struct ocxlflash_context *ctx = ctx_cookie; | |
184 | ||
185 | return ctx->pe; | |
186 | } | |
187 | ||
6b938ac9 UK |
188 | /** |
189 | * start_context() - local routine to start a context | |
190 | * @ctx: Adapter context to be started. | |
191 | * | |
192 | * Assign the context specific MMIO space. | |
193 | * | |
194 | * Return: 0 on success, -errno on failure | |
195 | */ | |
196 | static int start_context(struct ocxlflash_context *ctx) | |
197 | { | |
198 | struct ocxl_hw_afu *afu = ctx->hw_afu; | |
199 | struct ocxl_afu_config *acfg = &afu->acfg; | |
200 | bool master = ctx->master; | |
201 | ||
202 | if (master) { | |
203 | ctx->psn_size = acfg->global_mmio_size; | |
204 | ctx->psn_phys = afu->gmmio_phys; | |
205 | } else { | |
206 | ctx->psn_size = acfg->pp_mmio_stride; | |
207 | ctx->psn_phys = afu->ppmmio_phys + (ctx->pe * ctx->psn_size); | |
208 | } | |
209 | ||
210 | return 0; | |
211 | } | |
212 | ||
213 | /** | |
214 | * ocxlflash_start_context() - start a kernel context | |
215 | * @ctx_cookie: Adapter context to be started. | |
216 | * | |
217 | * Return: 0 on success, -errno on failure | |
218 | */ | |
219 | static int ocxlflash_start_context(void *ctx_cookie) | |
220 | { | |
221 | struct ocxlflash_context *ctx = ctx_cookie; | |
222 | ||
223 | return start_context(ctx); | |
224 | } | |
225 | ||
f6b4557c UK |
226 | /** |
227 | * ocxlflash_set_master() - sets the context as master | |
228 | * @ctx_cookie: Adapter context to set as master. | |
229 | */ | |
230 | static void ocxlflash_set_master(void *ctx_cookie) | |
231 | { | |
232 | struct ocxlflash_context *ctx = ctx_cookie; | |
233 | ||
234 | ctx->master = true; | |
235 | } | |
236 | ||
237 | /** | |
238 | * ocxlflash_get_context() - obtains the context associated with the host | |
239 | * @pdev: PCI device associated with the host. | |
240 | * @afu_cookie: Hardware AFU associated with the host. | |
241 | * | |
242 | * Return: returns the pointer to host adapter context | |
243 | */ | |
244 | static void *ocxlflash_get_context(struct pci_dev *pdev, void *afu_cookie) | |
245 | { | |
246 | struct ocxl_hw_afu *afu = afu_cookie; | |
247 | ||
248 | return afu->ocxl_ctx; | |
249 | } | |
250 | ||
251 | /** | |
252 | * ocxlflash_dev_context_init() - allocate and initialize an adapter context | |
253 | * @pdev: PCI device associated with the host. | |
254 | * @afu_cookie: Hardware AFU associated with the host. | |
255 | * | |
256 | * Return: returns the adapter context on success, ERR_PTR on failure | |
257 | */ | |
258 | static void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) | |
259 | { | |
260 | struct ocxl_hw_afu *afu = afu_cookie; | |
261 | struct device *dev = afu->dev; | |
262 | struct ocxlflash_context *ctx; | |
263 | int rc; | |
264 | ||
265 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); | |
266 | if (unlikely(!ctx)) { | |
267 | dev_err(dev, "%s: Context allocation failed\n", __func__); | |
268 | rc = -ENOMEM; | |
429ebfa6 UK |
269 | goto err1; |
270 | } | |
271 | ||
272 | idr_preload(GFP_KERNEL); | |
273 | rc = idr_alloc(&afu->idr, ctx, 0, afu->max_pasid, GFP_NOWAIT); | |
274 | idr_preload_end(); | |
275 | if (unlikely(rc < 0)) { | |
276 | dev_err(dev, "%s: idr_alloc failed rc=%d\n", __func__, rc); | |
277 | goto err2; | |
f6b4557c UK |
278 | } |
279 | ||
429ebfa6 | 280 | ctx->pe = rc; |
f6b4557c | 281 | ctx->master = false; |
926a62f9 | 282 | ctx->mapping = NULL; |
f6b4557c UK |
283 | ctx->hw_afu = afu; |
284 | out: | |
285 | return ctx; | |
429ebfa6 UK |
286 | err2: |
287 | kfree(ctx); | |
288 | err1: | |
f6b4557c UK |
289 | ctx = ERR_PTR(rc); |
290 | goto out; | |
291 | } | |
292 | ||
293 | /** | |
294 | * ocxlflash_release_context() - releases an adapter context | |
295 | * @ctx_cookie: Adapter context to be released. | |
296 | * | |
297 | * Return: 0 on success, -errno on failure | |
298 | */ | |
299 | static int ocxlflash_release_context(void *ctx_cookie) | |
300 | { | |
301 | struct ocxlflash_context *ctx = ctx_cookie; | |
302 | int rc = 0; | |
303 | ||
304 | if (!ctx) | |
305 | goto out; | |
306 | ||
429ebfa6 | 307 | idr_remove(&ctx->hw_afu->idr, ctx->pe); |
926a62f9 | 308 | ocxlflash_release_mapping(ctx); |
f6b4557c UK |
309 | kfree(ctx); |
310 | out: | |
311 | return rc; | |
312 | } | |
313 | ||
8b7a5521 UK |
314 | /** |
315 | * ocxlflash_perst_reloads_same_image() - sets the image reload policy | |
316 | * @afu_cookie: Hardware AFU associated with the host. | |
317 | * @image: Whether to load the same image on PERST. | |
318 | */ | |
319 | static void ocxlflash_perst_reloads_same_image(void *afu_cookie, bool image) | |
320 | { | |
321 | struct ocxl_hw_afu *afu = afu_cookie; | |
322 | ||
323 | afu->perst_same_image = image; | |
324 | } | |
325 | ||
54370503 UK |
326 | /** |
327 | * ocxlflash_unconfig_afu() - unconfigure the AFU | |
328 | * @afu: AFU associated with the host. | |
329 | */ | |
330 | static void ocxlflash_unconfig_afu(struct ocxl_hw_afu *afu) | |
331 | { | |
332 | if (afu->gmmio_virt) { | |
333 | iounmap(afu->gmmio_virt); | |
334 | afu->gmmio_virt = NULL; | |
335 | } | |
336 | } | |
337 | ||
48e077db UK |
338 | /** |
339 | * ocxlflash_destroy_afu() - destroy the AFU structure | |
340 | * @afu_cookie: AFU to be freed. | |
341 | */ | |
342 | static void ocxlflash_destroy_afu(void *afu_cookie) | |
343 | { | |
344 | struct ocxl_hw_afu *afu = afu_cookie; | |
345 | ||
346 | if (!afu) | |
347 | return; | |
348 | ||
f6b4557c | 349 | ocxlflash_release_context(afu->ocxl_ctx); |
429ebfa6 | 350 | idr_destroy(&afu->idr); |
54370503 | 351 | ocxlflash_unconfig_afu(afu); |
48e077db UK |
352 | kfree(afu); |
353 | } | |
354 | ||
e9dfceda UK |
355 | /** |
356 | * ocxlflash_config_fn() - configure the host function | |
357 | * @pdev: PCI device associated with the host. | |
358 | * @afu: AFU associated with the host. | |
359 | * | |
360 | * Return: 0 on success, -errno on failure | |
361 | */ | |
362 | static int ocxlflash_config_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) | |
363 | { | |
364 | struct ocxl_fn_config *fcfg = &afu->fcfg; | |
365 | struct device *dev = &pdev->dev; | |
2e222779 | 366 | u16 base, enabled, supported; |
e9dfceda UK |
367 | int rc = 0; |
368 | ||
369 | /* Read DVSEC config of the function */ | |
370 | rc = ocxl_config_read_function(pdev, fcfg); | |
371 | if (unlikely(rc)) { | |
372 | dev_err(dev, "%s: ocxl_config_read_function failed rc=%d\n", | |
373 | __func__, rc); | |
374 | goto out; | |
375 | } | |
376 | ||
377 | /* Check if function has AFUs defined, only 1 per function supported */ | |
378 | if (fcfg->max_afu_index >= 0) { | |
379 | afu->is_present = true; | |
380 | if (fcfg->max_afu_index != 0) | |
381 | dev_warn(dev, "%s: Unexpected AFU index value %d\n", | |
382 | __func__, fcfg->max_afu_index); | |
383 | } | |
2e222779 UK |
384 | |
385 | rc = ocxl_config_get_actag_info(pdev, &base, &enabled, &supported); | |
386 | if (unlikely(rc)) { | |
387 | dev_err(dev, "%s: ocxl_config_get_actag_info failed rc=%d\n", | |
388 | __func__, rc); | |
389 | goto out; | |
390 | } | |
391 | ||
392 | afu->fn_actag_base = base; | |
393 | afu->fn_actag_enabled = enabled; | |
394 | ||
395 | ocxl_config_set_actag(pdev, fcfg->dvsec_function_pos, base, enabled); | |
396 | dev_dbg(dev, "%s: Function acTag range base=%u enabled=%u\n", | |
397 | __func__, base, enabled); | |
e9dfceda UK |
398 | out: |
399 | return rc; | |
400 | } | |
401 | ||
54370503 UK |
402 | /** |
403 | * ocxlflash_map_mmio() - map the AFU MMIO space | |
404 | * @afu: AFU associated with the host. | |
405 | * | |
406 | * Return: 0 on success, -errno on failure | |
407 | */ | |
408 | static int ocxlflash_map_mmio(struct ocxl_hw_afu *afu) | |
409 | { | |
410 | struct ocxl_afu_config *acfg = &afu->acfg; | |
411 | struct pci_dev *pdev = afu->pdev; | |
412 | struct device *dev = afu->dev; | |
413 | phys_addr_t gmmio, ppmmio; | |
414 | int rc = 0; | |
415 | ||
416 | rc = pci_request_region(pdev, acfg->global_mmio_bar, "ocxlflash"); | |
417 | if (unlikely(rc)) { | |
418 | dev_err(dev, "%s: pci_request_region for global failed rc=%d\n", | |
419 | __func__, rc); | |
420 | goto out; | |
421 | } | |
422 | gmmio = pci_resource_start(pdev, acfg->global_mmio_bar); | |
423 | gmmio += acfg->global_mmio_offset; | |
424 | ||
425 | rc = pci_request_region(pdev, acfg->pp_mmio_bar, "ocxlflash"); | |
426 | if (unlikely(rc)) { | |
427 | dev_err(dev, "%s: pci_request_region for pp bar failed rc=%d\n", | |
428 | __func__, rc); | |
429 | goto err1; | |
430 | } | |
431 | ppmmio = pci_resource_start(pdev, acfg->pp_mmio_bar); | |
432 | ppmmio += acfg->pp_mmio_offset; | |
433 | ||
434 | afu->gmmio_virt = ioremap(gmmio, acfg->global_mmio_size); | |
435 | if (unlikely(!afu->gmmio_virt)) { | |
436 | dev_err(dev, "%s: MMIO mapping failed\n", __func__); | |
437 | rc = -ENOMEM; | |
438 | goto err2; | |
439 | } | |
440 | ||
441 | afu->gmmio_phys = gmmio; | |
442 | afu->ppmmio_phys = ppmmio; | |
443 | out: | |
444 | return rc; | |
445 | err2: | |
446 | pci_release_region(pdev, acfg->pp_mmio_bar); | |
447 | err1: | |
448 | pci_release_region(pdev, acfg->global_mmio_bar); | |
449 | goto out; | |
450 | } | |
451 | ||
9cc84291 UK |
452 | /** |
453 | * ocxlflash_config_afu() - configure the host AFU | |
454 | * @pdev: PCI device associated with the host. | |
455 | * @afu: AFU associated with the host. | |
456 | * | |
457 | * Must be called _after_ host function configuration. | |
458 | * | |
459 | * Return: 0 on success, -errno on failure | |
460 | */ | |
461 | static int ocxlflash_config_afu(struct pci_dev *pdev, struct ocxl_hw_afu *afu) | |
462 | { | |
463 | struct ocxl_afu_config *acfg = &afu->acfg; | |
464 | struct ocxl_fn_config *fcfg = &afu->fcfg; | |
465 | struct device *dev = &pdev->dev; | |
d926519e UK |
466 | int count; |
467 | int base; | |
468 | int pos; | |
9cc84291 UK |
469 | int rc = 0; |
470 | ||
471 | /* This HW AFU function does not have any AFUs defined */ | |
472 | if (!afu->is_present) | |
473 | goto out; | |
474 | ||
475 | /* Read AFU config at index 0 */ | |
476 | rc = ocxl_config_read_afu(pdev, fcfg, acfg, 0); | |
477 | if (unlikely(rc)) { | |
478 | dev_err(dev, "%s: ocxl_config_read_afu failed rc=%d\n", | |
479 | __func__, rc); | |
480 | goto out; | |
481 | } | |
d926519e UK |
482 | |
483 | /* Only one AFU per function is supported, so actag_base is same */ | |
484 | base = afu->fn_actag_base; | |
485 | count = min_t(int, acfg->actag_supported, afu->fn_actag_enabled); | |
486 | pos = acfg->dvsec_afu_control_pos; | |
487 | ||
488 | ocxl_config_set_afu_actag(pdev, pos, base, count); | |
489 | dev_dbg(dev, "%s: acTag base=%d enabled=%d\n", __func__, base, count); | |
490 | afu->afu_actag_base = base; | |
491 | afu->afu_actag_enabled = count; | |
41df40d8 UK |
492 | afu->max_pasid = 1 << acfg->pasid_supported_log; |
493 | ||
494 | ocxl_config_set_afu_pasid(pdev, pos, 0, acfg->pasid_supported_log); | |
54370503 UK |
495 | |
496 | rc = ocxlflash_map_mmio(afu); | |
497 | if (unlikely(rc)) { | |
498 | dev_err(dev, "%s: ocxlflash_map_mmio failed rc=%d\n", | |
499 | __func__, rc); | |
500 | goto out; | |
501 | } | |
9cc84291 UK |
502 | out: |
503 | return rc; | |
504 | } | |
505 | ||
48e077db UK |
506 | /** |
507 | * ocxlflash_create_afu() - create the AFU for OCXL | |
508 | * @pdev: PCI device associated with the host. | |
509 | * | |
510 | * Return: AFU on success, NULL on failure | |
511 | */ | |
512 | static void *ocxlflash_create_afu(struct pci_dev *pdev) | |
513 | { | |
514 | struct device *dev = &pdev->dev; | |
f6b4557c | 515 | struct ocxlflash_context *ctx; |
48e077db | 516 | struct ocxl_hw_afu *afu; |
e9dfceda | 517 | int rc; |
48e077db UK |
518 | |
519 | afu = kzalloc(sizeof(*afu), GFP_KERNEL); | |
520 | if (unlikely(!afu)) { | |
521 | dev_err(dev, "%s: HW AFU allocation failed\n", __func__); | |
522 | goto out; | |
523 | } | |
524 | ||
525 | afu->pdev = pdev; | |
526 | afu->dev = dev; | |
429ebfa6 | 527 | idr_init(&afu->idr); |
e9dfceda UK |
528 | |
529 | rc = ocxlflash_config_fn(pdev, afu); | |
530 | if (unlikely(rc)) { | |
531 | dev_err(dev, "%s: Function configuration failed rc=%d\n", | |
532 | __func__, rc); | |
533 | goto err1; | |
534 | } | |
9cc84291 UK |
535 | |
536 | rc = ocxlflash_config_afu(pdev, afu); | |
537 | if (unlikely(rc)) { | |
538 | dev_err(dev, "%s: AFU configuration failed rc=%d\n", | |
539 | __func__, rc); | |
540 | goto err1; | |
541 | } | |
f6b4557c UK |
542 | |
543 | ctx = ocxlflash_dev_context_init(pdev, afu); | |
544 | if (IS_ERR(ctx)) { | |
545 | rc = PTR_ERR(ctx); | |
546 | dev_err(dev, "%s: ocxlflash_dev_context_init failed rc=%d\n", | |
547 | __func__, rc); | |
54370503 | 548 | goto err2; |
f6b4557c UK |
549 | } |
550 | ||
551 | afu->ocxl_ctx = ctx; | |
48e077db UK |
552 | out: |
553 | return afu; | |
54370503 UK |
554 | err2: |
555 | ocxlflash_unconfig_afu(afu); | |
e9dfceda | 556 | err1: |
429ebfa6 | 557 | idr_destroy(&afu->idr); |
e9dfceda UK |
558 | kfree(afu); |
559 | afu = NULL; | |
560 | goto out; | |
48e077db | 561 | } |
76ebe01f | 562 | |
926a62f9 UK |
563 | static const struct file_operations ocxl_afu_fops = { |
564 | .owner = THIS_MODULE, | |
565 | }; | |
566 | ||
567 | /** | |
568 | * ocxlflash_get_fd() - get file descriptor for an adapter context | |
569 | * @ctx_cookie: Adapter context. | |
570 | * @fops: File operations to be associated. | |
571 | * @fd: File descriptor to be returned back. | |
572 | * | |
573 | * Return: pointer to the file on success, ERR_PTR on failure | |
574 | */ | |
575 | static struct file *ocxlflash_get_fd(void *ctx_cookie, | |
576 | struct file_operations *fops, int *fd) | |
577 | { | |
578 | struct ocxlflash_context *ctx = ctx_cookie; | |
579 | struct device *dev = ctx->hw_afu->dev; | |
580 | struct file *file; | |
581 | int flags, fdtmp; | |
582 | int rc = 0; | |
583 | char *name = NULL; | |
584 | ||
585 | /* Only allow one fd per context */ | |
586 | if (ctx->mapping) { | |
587 | dev_err(dev, "%s: Context is already mapped to an fd\n", | |
588 | __func__); | |
589 | rc = -EEXIST; | |
590 | goto err1; | |
591 | } | |
592 | ||
593 | flags = O_RDWR | O_CLOEXEC; | |
594 | ||
595 | /* This code is similar to anon_inode_getfd() */ | |
596 | rc = get_unused_fd_flags(flags); | |
597 | if (unlikely(rc < 0)) { | |
598 | dev_err(dev, "%s: get_unused_fd_flags failed rc=%d\n", | |
599 | __func__, rc); | |
600 | goto err1; | |
601 | } | |
602 | fdtmp = rc; | |
603 | ||
604 | /* Use default ops if there is no fops */ | |
605 | if (!fops) | |
606 | fops = (struct file_operations *)&ocxl_afu_fops; | |
607 | ||
608 | name = kasprintf(GFP_KERNEL, "ocxlflash:%d", ctx->pe); | |
609 | file = ocxlflash_getfile(dev, name, fops, ctx, flags); | |
610 | kfree(name); | |
611 | if (IS_ERR(file)) { | |
612 | rc = PTR_ERR(file); | |
613 | dev_err(dev, "%s: ocxlflash_getfile failed rc=%d\n", | |
614 | __func__, rc); | |
615 | goto err2; | |
616 | } | |
617 | ||
618 | ctx->mapping = file->f_mapping; | |
619 | *fd = fdtmp; | |
620 | out: | |
621 | return file; | |
622 | err2: | |
623 | put_unused_fd(fdtmp); | |
624 | err1: | |
625 | file = ERR_PTR(rc); | |
626 | goto out; | |
627 | } | |
628 | ||
b18718c6 UK |
629 | /** |
630 | * ocxlflash_fops_get_context() - get the context associated with the file | |
631 | * @file: File associated with the adapter context. | |
632 | * | |
633 | * Return: pointer to the context | |
634 | */ | |
635 | static void *ocxlflash_fops_get_context(struct file *file) | |
636 | { | |
637 | return file->private_data; | |
638 | } | |
639 | ||
76ebe01f UK |
640 | /* Backend ops to ocxlflash services */ |
641 | const struct cxlflash_backend_ops cxlflash_ocxl_ops = { | |
642 | .module = THIS_MODULE, | |
012f394c UK |
643 | .psa_map = ocxlflash_psa_map, |
644 | .psa_unmap = ocxlflash_psa_unmap, | |
b18718c6 | 645 | .process_element = ocxlflash_process_element, |
6b938ac9 | 646 | .start_context = ocxlflash_start_context, |
f6b4557c UK |
647 | .set_master = ocxlflash_set_master, |
648 | .get_context = ocxlflash_get_context, | |
649 | .dev_context_init = ocxlflash_dev_context_init, | |
650 | .release_context = ocxlflash_release_context, | |
8b7a5521 | 651 | .perst_reloads_same_image = ocxlflash_perst_reloads_same_image, |
48e077db UK |
652 | .create_afu = ocxlflash_create_afu, |
653 | .destroy_afu = ocxlflash_destroy_afu, | |
926a62f9 | 654 | .get_fd = ocxlflash_get_fd, |
b18718c6 | 655 | .fops_get_context = ocxlflash_fops_get_context, |
76ebe01f | 656 | }; |