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