1 /* 2 * Copyright 2014 IBM Corp. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/device.h> 12 #include <linux/sysfs.h> 13 #include <linux/pci_regs.h> 14 15 #include "cxl.h" 16 17 #define to_afu_chardev_m(d) dev_get_drvdata(d) 18 19 /********* Adapter attributes **********************************************/ 20 21 static ssize_t caia_version_show(struct device *device, 22 struct device_attribute *attr, 23 char *buf) 24 { 25 struct cxl *adapter = to_cxl_adapter(device); 26 27 return scnprintf(buf, PAGE_SIZE, "%i.%i\n", adapter->caia_major, 28 adapter->caia_minor); 29 } 30 31 static ssize_t psl_revision_show(struct device *device, 32 struct device_attribute *attr, 33 char *buf) 34 { 35 struct cxl *adapter = to_cxl_adapter(device); 36 37 return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_rev); 38 } 39 40 static ssize_t base_image_show(struct device *device, 41 struct device_attribute *attr, 42 char *buf) 43 { 44 struct cxl *adapter = to_cxl_adapter(device); 45 46 return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->base_image); 47 } 48 49 static ssize_t image_loaded_show(struct device *device, 50 struct device_attribute *attr, 51 char *buf) 52 { 53 struct cxl *adapter = to_cxl_adapter(device); 54 55 if (adapter->user_image_loaded) 56 return scnprintf(buf, PAGE_SIZE, "user\n"); 57 return scnprintf(buf, PAGE_SIZE, "factory\n"); 58 } 59 60 static ssize_t psl_timebase_synced_show(struct device *device, 61 struct device_attribute *attr, 62 char *buf) 63 { 64 struct cxl *adapter = to_cxl_adapter(device); 65 u64 psl_tb, delta; 66 67 /* Recompute the status only in native mode */ 68 if (cpu_has_feature(CPU_FTR_HVMODE)) { 69 psl_tb = adapter->native->sl_ops->timebase_read(adapter); 70 delta = abs(mftb() - psl_tb); 71 72 /* CORE TB and PSL TB difference <= 16usecs ? */ 73 adapter->psl_timebase_synced = (tb_to_ns(delta) < 16000) ? true : false; 74 pr_devel("PSL timebase %s - delta: 0x%016llx\n", 75 (tb_to_ns(delta) < 16000) ? "synchronized" : 76 "not synchronized", tb_to_ns(delta)); 77 } 78 return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->psl_timebase_synced); 79 } 80 81 static ssize_t reset_adapter_store(struct device *device, 82 struct device_attribute *attr, 83 const char *buf, size_t count) 84 { 85 struct cxl *adapter = to_cxl_adapter(device); 86 int rc; 87 int val; 88 89 rc = sscanf(buf, "%i", &val); 90 if ((rc != 1) || (val != 1 && val != -1)) 91 return -EINVAL; 92 93 /* 94 * See if we can lock the context mapping that's only allowed 95 * when there are no contexts attached to the adapter. Once 96 * taken this will also prevent any context from getting activated. 97 */ 98 if (val == 1) { 99 rc = cxl_adapter_context_lock(adapter); 100 if (rc) 101 goto out; 102 103 rc = cxl_ops->adapter_reset(adapter); 104 /* In case reset failed release context lock */ 105 if (rc) 106 cxl_adapter_context_unlock(adapter); 107 108 } else if (val == -1) { 109 /* Perform a forced adapter reset */ 110 rc = cxl_ops->adapter_reset(adapter); 111 } 112 113 out: 114 return rc ? rc : count; 115 } 116 117 static ssize_t load_image_on_perst_show(struct device *device, 118 struct device_attribute *attr, 119 char *buf) 120 { 121 struct cxl *adapter = to_cxl_adapter(device); 122 123 if (!adapter->perst_loads_image) 124 return scnprintf(buf, PAGE_SIZE, "none\n"); 125 126 if (adapter->perst_select_user) 127 return scnprintf(buf, PAGE_SIZE, "user\n"); 128 return scnprintf(buf, PAGE_SIZE, "factory\n"); 129 } 130 131 static ssize_t load_image_on_perst_store(struct device *device, 132 struct device_attribute *attr, 133 const char *buf, size_t count) 134 { 135 struct cxl *adapter = to_cxl_adapter(device); 136 int rc; 137 138 if (!strncmp(buf, "none", 4)) 139 adapter->perst_loads_image = false; 140 else if (!strncmp(buf, "user", 4)) { 141 adapter->perst_select_user = true; 142 adapter->perst_loads_image = true; 143 } else if (!strncmp(buf, "factory", 7)) { 144 adapter->perst_select_user = false; 145 adapter->perst_loads_image = true; 146 } else 147 return -EINVAL; 148 149 if ((rc = cxl_update_image_control(adapter))) 150 return rc; 151 152 return count; 153 } 154 155 static ssize_t perst_reloads_same_image_show(struct device *device, 156 struct device_attribute *attr, 157 char *buf) 158 { 159 struct cxl *adapter = to_cxl_adapter(device); 160 161 return scnprintf(buf, PAGE_SIZE, "%i\n", adapter->perst_same_image); 162 } 163 164 static ssize_t perst_reloads_same_image_store(struct device *device, 165 struct device_attribute *attr, 166 const char *buf, size_t count) 167 { 168 struct cxl *adapter = to_cxl_adapter(device); 169 int rc; 170 int val; 171 172 rc = sscanf(buf, "%i", &val); 173 if ((rc != 1) || !(val == 1 || val == 0)) 174 return -EINVAL; 175 176 adapter->perst_same_image = (val == 1 ? true : false); 177 return count; 178 } 179 180 static struct device_attribute adapter_attrs[] = { 181 __ATTR_RO(caia_version), 182 __ATTR_RO(psl_revision), 183 __ATTR_RO(base_image), 184 __ATTR_RO(image_loaded), 185 __ATTR_RO(psl_timebase_synced), 186 __ATTR_RW(load_image_on_perst), 187 __ATTR_RW(perst_reloads_same_image), 188 __ATTR(reset, S_IWUSR, NULL, reset_adapter_store), 189 }; 190 191 192 /********* AFU master specific attributes **********************************/ 193 194 static ssize_t mmio_size_show_master(struct device *device, 195 struct device_attribute *attr, 196 char *buf) 197 { 198 struct cxl_afu *afu = to_afu_chardev_m(device); 199 200 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size); 201 } 202 203 static ssize_t pp_mmio_off_show(struct device *device, 204 struct device_attribute *attr, 205 char *buf) 206 { 207 struct cxl_afu *afu = to_afu_chardev_m(device); 208 209 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->native->pp_offset); 210 } 211 212 static ssize_t pp_mmio_len_show(struct device *device, 213 struct device_attribute *attr, 214 char *buf) 215 { 216 struct cxl_afu *afu = to_afu_chardev_m(device); 217 218 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size); 219 } 220 221 static struct device_attribute afu_master_attrs[] = { 222 __ATTR(mmio_size, S_IRUGO, mmio_size_show_master, NULL), 223 __ATTR_RO(pp_mmio_off), 224 __ATTR_RO(pp_mmio_len), 225 }; 226 227 228 /********* AFU attributes **************************************************/ 229 230 static ssize_t mmio_size_show(struct device *device, 231 struct device_attribute *attr, 232 char *buf) 233 { 234 struct cxl_afu *afu = to_cxl_afu(device); 235 236 if (afu->pp_size) 237 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->pp_size); 238 return scnprintf(buf, PAGE_SIZE, "%llu\n", afu->adapter->ps_size); 239 } 240 241 static ssize_t reset_store_afu(struct device *device, 242 struct device_attribute *attr, 243 const char *buf, size_t count) 244 { 245 struct cxl_afu *afu = to_cxl_afu(device); 246 int rc; 247 248 /* Not safe to reset if it is currently in use */ 249 mutex_lock(&afu->contexts_lock); 250 if (!idr_is_empty(&afu->contexts_idr)) { 251 rc = -EBUSY; 252 goto err; 253 } 254 255 if ((rc = cxl_ops->afu_reset(afu))) 256 goto err; 257 258 rc = count; 259 err: 260 mutex_unlock(&afu->contexts_lock); 261 return rc; 262 } 263 264 static ssize_t irqs_min_show(struct device *device, 265 struct device_attribute *attr, 266 char *buf) 267 { 268 struct cxl_afu *afu = to_cxl_afu(device); 269 270 return scnprintf(buf, PAGE_SIZE, "%i\n", afu->pp_irqs); 271 } 272 273 static ssize_t irqs_max_show(struct device *device, 274 struct device_attribute *attr, 275 char *buf) 276 { 277 struct cxl_afu *afu = to_cxl_afu(device); 278 279 return scnprintf(buf, PAGE_SIZE, "%i\n", afu->irqs_max); 280 } 281 282 static ssize_t irqs_max_store(struct device *device, 283 struct device_attribute *attr, 284 const char *buf, size_t count) 285 { 286 struct cxl_afu *afu = to_cxl_afu(device); 287 ssize_t ret; 288 int irqs_max; 289 290 ret = sscanf(buf, "%i", &irqs_max); 291 if (ret != 1) 292 return -EINVAL; 293 294 if (irqs_max < afu->pp_irqs) 295 return -EINVAL; 296 297 if (cpu_has_feature(CPU_FTR_HVMODE)) { 298 if (irqs_max > afu->adapter->user_irqs) 299 return -EINVAL; 300 } else { 301 /* pHyp sets a per-AFU limit */ 302 if (irqs_max > afu->guest->max_ints) 303 return -EINVAL; 304 } 305 306 afu->irqs_max = irqs_max; 307 return count; 308 } 309 310 static ssize_t modes_supported_show(struct device *device, 311 struct device_attribute *attr, char *buf) 312 { 313 struct cxl_afu *afu = to_cxl_afu(device); 314 char *p = buf, *end = buf + PAGE_SIZE; 315 316 if (afu->modes_supported & CXL_MODE_DEDICATED) 317 p += scnprintf(p, end - p, "dedicated_process\n"); 318 if (afu->modes_supported & CXL_MODE_DIRECTED) 319 p += scnprintf(p, end - p, "afu_directed\n"); 320 return (p - buf); 321 } 322 323 static ssize_t prefault_mode_show(struct device *device, 324 struct device_attribute *attr, 325 char *buf) 326 { 327 struct cxl_afu *afu = to_cxl_afu(device); 328 329 switch (afu->prefault_mode) { 330 case CXL_PREFAULT_WED: 331 return scnprintf(buf, PAGE_SIZE, "work_element_descriptor\n"); 332 case CXL_PREFAULT_ALL: 333 return scnprintf(buf, PAGE_SIZE, "all\n"); 334 default: 335 return scnprintf(buf, PAGE_SIZE, "none\n"); 336 } 337 } 338 339 static ssize_t prefault_mode_store(struct device *device, 340 struct device_attribute *attr, 341 const char *buf, size_t count) 342 { 343 struct cxl_afu *afu = to_cxl_afu(device); 344 enum prefault_modes mode = -1; 345 346 if (!strncmp(buf, "work_element_descriptor", 23)) 347 mode = CXL_PREFAULT_WED; 348 if (!strncmp(buf, "all", 3)) 349 mode = CXL_PREFAULT_ALL; 350 if (!strncmp(buf, "none", 4)) 351 mode = CXL_PREFAULT_NONE; 352 353 if (mode == -1) 354 return -EINVAL; 355 356 afu->prefault_mode = mode; 357 return count; 358 } 359 360 static ssize_t mode_show(struct device *device, 361 struct device_attribute *attr, 362 char *buf) 363 { 364 struct cxl_afu *afu = to_cxl_afu(device); 365 366 if (afu->current_mode == CXL_MODE_DEDICATED) 367 return scnprintf(buf, PAGE_SIZE, "dedicated_process\n"); 368 if (afu->current_mode == CXL_MODE_DIRECTED) 369 return scnprintf(buf, PAGE_SIZE, "afu_directed\n"); 370 return scnprintf(buf, PAGE_SIZE, "none\n"); 371 } 372 373 static ssize_t mode_store(struct device *device, struct device_attribute *attr, 374 const char *buf, size_t count) 375 { 376 struct cxl_afu *afu = to_cxl_afu(device); 377 int old_mode, mode = -1; 378 int rc = -EBUSY; 379 380 /* can't change this if we have a user */ 381 mutex_lock(&afu->contexts_lock); 382 if (!idr_is_empty(&afu->contexts_idr)) 383 goto err; 384 385 if (!strncmp(buf, "dedicated_process", 17)) 386 mode = CXL_MODE_DEDICATED; 387 if (!strncmp(buf, "afu_directed", 12)) 388 mode = CXL_MODE_DIRECTED; 389 if (!strncmp(buf, "none", 4)) 390 mode = 0; 391 392 if (mode == -1) { 393 rc = -EINVAL; 394 goto err; 395 } 396 397 /* 398 * afu_deactivate_mode needs to be done outside the lock, prevent 399 * other contexts coming in before we are ready: 400 */ 401 old_mode = afu->current_mode; 402 afu->current_mode = 0; 403 afu->num_procs = 0; 404 405 mutex_unlock(&afu->contexts_lock); 406 407 if ((rc = cxl_ops->afu_deactivate_mode(afu, old_mode))) 408 return rc; 409 if ((rc = cxl_ops->afu_activate_mode(afu, mode))) 410 return rc; 411 412 return count; 413 err: 414 mutex_unlock(&afu->contexts_lock); 415 return rc; 416 } 417 418 static ssize_t api_version_show(struct device *device, 419 struct device_attribute *attr, 420 char *buf) 421 { 422 return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION); 423 } 424 425 static ssize_t api_version_compatible_show(struct device *device, 426 struct device_attribute *attr, 427 char *buf) 428 { 429 return scnprintf(buf, PAGE_SIZE, "%i\n", CXL_API_VERSION_COMPATIBLE); 430 } 431 432 static ssize_t afu_eb_read(struct file *filp, struct kobject *kobj, 433 struct bin_attribute *bin_attr, char *buf, 434 loff_t off, size_t count) 435 { 436 struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj)); 437 438 return cxl_ops->afu_read_err_buffer(afu, buf, off, count); 439 } 440 441 static struct device_attribute afu_attrs[] = { 442 __ATTR_RO(mmio_size), 443 __ATTR_RO(irqs_min), 444 __ATTR_RW(irqs_max), 445 __ATTR_RO(modes_supported), 446 __ATTR_RW(mode), 447 __ATTR_RW(prefault_mode), 448 __ATTR_RO(api_version), 449 __ATTR_RO(api_version_compatible), 450 __ATTR(reset, S_IWUSR, NULL, reset_store_afu), 451 }; 452 453 int cxl_sysfs_adapter_add(struct cxl *adapter) 454 { 455 struct device_attribute *dev_attr; 456 int i, rc; 457 458 for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) { 459 dev_attr = &adapter_attrs[i]; 460 if (cxl_ops->support_attributes(dev_attr->attr.name, 461 CXL_ADAPTER_ATTRS)) { 462 if ((rc = device_create_file(&adapter->dev, dev_attr))) 463 goto err; 464 } 465 } 466 return 0; 467 err: 468 for (i--; i >= 0; i--) { 469 dev_attr = &adapter_attrs[i]; 470 if (cxl_ops->support_attributes(dev_attr->attr.name, 471 CXL_ADAPTER_ATTRS)) 472 device_remove_file(&adapter->dev, dev_attr); 473 } 474 return rc; 475 } 476 477 void cxl_sysfs_adapter_remove(struct cxl *adapter) 478 { 479 struct device_attribute *dev_attr; 480 int i; 481 482 for (i = 0; i < ARRAY_SIZE(adapter_attrs); i++) { 483 dev_attr = &adapter_attrs[i]; 484 if (cxl_ops->support_attributes(dev_attr->attr.name, 485 CXL_ADAPTER_ATTRS)) 486 device_remove_file(&adapter->dev, dev_attr); 487 } 488 } 489 490 struct afu_config_record { 491 struct kobject kobj; 492 struct bin_attribute config_attr; 493 struct list_head list; 494 int cr; 495 u16 device; 496 u16 vendor; 497 u32 class; 498 }; 499 500 #define to_cr(obj) container_of(obj, struct afu_config_record, kobj) 501 502 static ssize_t vendor_show(struct kobject *kobj, 503 struct kobj_attribute *attr, char *buf) 504 { 505 struct afu_config_record *cr = to_cr(kobj); 506 507 return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->vendor); 508 } 509 510 static ssize_t device_show(struct kobject *kobj, 511 struct kobj_attribute *attr, char *buf) 512 { 513 struct afu_config_record *cr = to_cr(kobj); 514 515 return scnprintf(buf, PAGE_SIZE, "0x%.4x\n", cr->device); 516 } 517 518 static ssize_t class_show(struct kobject *kobj, 519 struct kobj_attribute *attr, char *buf) 520 { 521 struct afu_config_record *cr = to_cr(kobj); 522 523 return scnprintf(buf, PAGE_SIZE, "0x%.6x\n", cr->class); 524 } 525 526 static ssize_t afu_read_config(struct file *filp, struct kobject *kobj, 527 struct bin_attribute *bin_attr, char *buf, 528 loff_t off, size_t count) 529 { 530 struct afu_config_record *cr = to_cr(kobj); 531 struct cxl_afu *afu = to_cxl_afu(kobj_to_dev(kobj->parent)); 532 533 u64 i, j, val, rc; 534 535 for (i = 0; i < count;) { 536 rc = cxl_ops->afu_cr_read64(afu, cr->cr, off & ~0x7, &val); 537 if (rc) 538 val = ~0ULL; 539 for (j = off & 0x7; j < 8 && i < count; i++, j++, off++) 540 buf[i] = (val >> (j * 8)) & 0xff; 541 } 542 543 return count; 544 } 545 546 static struct kobj_attribute vendor_attribute = 547 __ATTR_RO(vendor); 548 static struct kobj_attribute device_attribute = 549 __ATTR_RO(device); 550 static struct kobj_attribute class_attribute = 551 __ATTR_RO(class); 552 553 static struct attribute *afu_cr_attrs[] = { 554 &vendor_attribute.attr, 555 &device_attribute.attr, 556 &class_attribute.attr, 557 NULL, 558 }; 559 560 static void release_afu_config_record(struct kobject *kobj) 561 { 562 struct afu_config_record *cr = to_cr(kobj); 563 564 kfree(cr); 565 } 566 567 static struct kobj_type afu_config_record_type = { 568 .sysfs_ops = &kobj_sysfs_ops, 569 .release = release_afu_config_record, 570 .default_attrs = afu_cr_attrs, 571 }; 572 573 static struct afu_config_record *cxl_sysfs_afu_new_cr(struct cxl_afu *afu, int cr_idx) 574 { 575 struct afu_config_record *cr; 576 int rc; 577 578 cr = kzalloc(sizeof(struct afu_config_record), GFP_KERNEL); 579 if (!cr) 580 return ERR_PTR(-ENOMEM); 581 582 cr->cr = cr_idx; 583 584 rc = cxl_ops->afu_cr_read16(afu, cr_idx, PCI_DEVICE_ID, &cr->device); 585 if (rc) 586 goto err; 587 rc = cxl_ops->afu_cr_read16(afu, cr_idx, PCI_VENDOR_ID, &cr->vendor); 588 if (rc) 589 goto err; 590 rc = cxl_ops->afu_cr_read32(afu, cr_idx, PCI_CLASS_REVISION, &cr->class); 591 if (rc) 592 goto err; 593 cr->class >>= 8; 594 595 /* 596 * Export raw AFU PCIe like config record. For now this is read only by 597 * root - we can expand that later to be readable by non-root and maybe 598 * even writable provided we have a good use-case. Once we support 599 * exposing AFUs through a virtual PHB they will get that for free from 600 * Linux' PCI infrastructure, but until then it's not clear that we 601 * need it for anything since the main use case is just identifying 602 * AFUs, which can be done via the vendor, device and class attributes. 603 */ 604 sysfs_bin_attr_init(&cr->config_attr); 605 cr->config_attr.attr.name = "config"; 606 cr->config_attr.attr.mode = S_IRUSR; 607 cr->config_attr.size = afu->crs_len; 608 cr->config_attr.read = afu_read_config; 609 610 rc = kobject_init_and_add(&cr->kobj, &afu_config_record_type, 611 &afu->dev.kobj, "cr%i", cr->cr); 612 if (rc) 613 goto err; 614 615 rc = sysfs_create_bin_file(&cr->kobj, &cr->config_attr); 616 if (rc) 617 goto err1; 618 619 rc = kobject_uevent(&cr->kobj, KOBJ_ADD); 620 if (rc) 621 goto err2; 622 623 return cr; 624 err2: 625 sysfs_remove_bin_file(&cr->kobj, &cr->config_attr); 626 err1: 627 kobject_put(&cr->kobj); 628 return ERR_PTR(rc); 629 err: 630 kfree(cr); 631 return ERR_PTR(rc); 632 } 633 634 void cxl_sysfs_afu_remove(struct cxl_afu *afu) 635 { 636 struct device_attribute *dev_attr; 637 struct afu_config_record *cr, *tmp; 638 int i; 639 640 /* remove the err buffer bin attribute */ 641 if (afu->eb_len) 642 device_remove_bin_file(&afu->dev, &afu->attr_eb); 643 644 for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) { 645 dev_attr = &afu_attrs[i]; 646 if (cxl_ops->support_attributes(dev_attr->attr.name, 647 CXL_AFU_ATTRS)) 648 device_remove_file(&afu->dev, &afu_attrs[i]); 649 } 650 651 list_for_each_entry_safe(cr, tmp, &afu->crs, list) { 652 sysfs_remove_bin_file(&cr->kobj, &cr->config_attr); 653 kobject_put(&cr->kobj); 654 } 655 } 656 657 int cxl_sysfs_afu_add(struct cxl_afu *afu) 658 { 659 struct device_attribute *dev_attr; 660 struct afu_config_record *cr; 661 int i, rc; 662 663 INIT_LIST_HEAD(&afu->crs); 664 665 for (i = 0; i < ARRAY_SIZE(afu_attrs); i++) { 666 dev_attr = &afu_attrs[i]; 667 if (cxl_ops->support_attributes(dev_attr->attr.name, 668 CXL_AFU_ATTRS)) { 669 if ((rc = device_create_file(&afu->dev, &afu_attrs[i]))) 670 goto err; 671 } 672 } 673 674 /* conditionally create the add the binary file for error info buffer */ 675 if (afu->eb_len) { 676 sysfs_attr_init(&afu->attr_eb.attr); 677 678 afu->attr_eb.attr.name = "afu_err_buff"; 679 afu->attr_eb.attr.mode = S_IRUGO; 680 afu->attr_eb.size = afu->eb_len; 681 afu->attr_eb.read = afu_eb_read; 682 683 rc = device_create_bin_file(&afu->dev, &afu->attr_eb); 684 if (rc) { 685 dev_err(&afu->dev, 686 "Unable to create eb attr for the afu. Err(%d)\n", 687 rc); 688 goto err; 689 } 690 } 691 692 for (i = 0; i < afu->crs_num; i++) { 693 cr = cxl_sysfs_afu_new_cr(afu, i); 694 if (IS_ERR(cr)) { 695 rc = PTR_ERR(cr); 696 goto err1; 697 } 698 list_add(&cr->list, &afu->crs); 699 } 700 701 return 0; 702 703 err1: 704 cxl_sysfs_afu_remove(afu); 705 return rc; 706 err: 707 /* reset the eb_len as we havent created the bin attr */ 708 afu->eb_len = 0; 709 710 for (i--; i >= 0; i--) { 711 dev_attr = &afu_attrs[i]; 712 if (cxl_ops->support_attributes(dev_attr->attr.name, 713 CXL_AFU_ATTRS)) 714 device_remove_file(&afu->dev, &afu_attrs[i]); 715 } 716 return rc; 717 } 718 719 int cxl_sysfs_afu_m_add(struct cxl_afu *afu) 720 { 721 struct device_attribute *dev_attr; 722 int i, rc; 723 724 for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) { 725 dev_attr = &afu_master_attrs[i]; 726 if (cxl_ops->support_attributes(dev_attr->attr.name, 727 CXL_AFU_MASTER_ATTRS)) { 728 if ((rc = device_create_file(afu->chardev_m, &afu_master_attrs[i]))) 729 goto err; 730 } 731 } 732 733 return 0; 734 735 err: 736 for (i--; i >= 0; i--) { 737 dev_attr = &afu_master_attrs[i]; 738 if (cxl_ops->support_attributes(dev_attr->attr.name, 739 CXL_AFU_MASTER_ATTRS)) 740 device_remove_file(afu->chardev_m, &afu_master_attrs[i]); 741 } 742 return rc; 743 } 744 745 void cxl_sysfs_afu_m_remove(struct cxl_afu *afu) 746 { 747 struct device_attribute *dev_attr; 748 int i; 749 750 for (i = 0; i < ARRAY_SIZE(afu_master_attrs); i++) { 751 dev_attr = &afu_master_attrs[i]; 752 if (cxl_ops->support_attributes(dev_attr->attr.name, 753 CXL_AFU_MASTER_ATTRS)) 754 device_remove_file(afu->chardev_m, &afu_master_attrs[i]); 755 } 756 } 757