What is the intended usage of DEVICE_INT_ATTR?

486 Views Asked by At

I would like to expose some settings of my device via sysfs. If I understand it right, a driver can have multiple devices, so there should be one instance of the settings variable per device. This should be easy enough using DEVICE_ATTR macro.

Checking the sources I noticed there is also DEVICE_INT_ATTR and other with different type. I wonder what is the intended usage, as they use device_show_int functions that get pointer to device, but don't actually use it:

ssize_t device_store_int(struct device *dev,
             struct device_attribute *attr,
             const char *buf, size_t size)
{
    struct dev_ext_attribute *ea = to_ext_attr(attr);
    int ret;
    long new;

    ret = kstrtol(buf, 0, &new);
    if (ret)
        return ret;

    if (new > INT_MAX || new < INT_MIN)
        return -EINVAL;
    *(int *)(ea->var) = new;
    /* Always return full write size even if we didn't consume all */
    return size;
}
EXPORT_SYMBOL_GPL(device_store_int);

I searched kernel sources for those macros, and it seems that they work with a global variable. For example DEVICE_INT_ATTR is used in drivers/base/core.c for mca_cfg.tolerant:

static DEVICE_INT_ATTR(tolerant, 0644, mca_cfg.tolerant);

but the mca_cfg varaible is actually global, not tied to a device:

struct mca_config mca_cfg __read_mostly = {
    .bootlog  = -1,
    /* ... */
    .tolerant = 1,
    .monarch_timeout = -1
};

which makes it look like a driver (not device) attribute.

I also checked commit that adds these macros but it did not help me much.

1

There are 1 best solutions below

2
On

You correctly take that DEVICE_INT_ATTR and other macros from that family are for "global" attributes, which store and show methods doesn't use dev parameter.

If you want to define attribute, which can be bound to several devices, then you could write your own store and show methods which gets information about the value from dev.

E.g. by having device

struct my_device
{
  struct device base;

  int repetition;
};

you could expose its repetition field in the attribute using following show method:

// Shows value of 'repetition' field of my_device.
static ssize_t repetition_show(struct device *dev, struct device_attribute *attr, char *buf)
{
  // Obtain pointer to the real device structure.
  struct my_device* my_dev = container_of(dev, struct my_device, base);
  return sprintf(buf, "%d\n", my_dev->repetition);
}

Structure of such attribute could be initialized using __ATTR macro:

static struct device_attribute repetition_attr =
  __ATTR(repetition, S_IRUGO, repetition_show, NULL);

Making "generic" attributes

Assume your device struct contains many int fields, which you want to expose via attributes:

struct my_device
{
  struct device base;

  int repetition;
  int counter;
  int value;
};

In that case you could generalize attribute definition, so you don't need to create many show (and store) functions.

E.g. you could store offset of the exposed field in your attribute structure:

struct device_bounded_attr
{
  struct device_attribute base_attr;
  size_t field_offset;
};

// Initializer for struct device_bounded_attr
//
// - real_device_type - type of the actual device structure
// - device_member - member of type 'struct device' in the actual device structure
// - field_member - member in actual device structure which you want to expose as attribute.
#define BOUNDED_ATTR(name, mode, show, store, real_device_type, device_member, field_member) { \
  .base_attr = __ATTR(name, mode, show, store), \
  .field_offset = offsetof(real_device_type, field_member) - offsetof(real_device_type, device_member)
}

Using this field, you could rewrite show method as follows:

// Shows value of integer field, stored in device.
static ssize_t bounded_attr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
  // Obtain pointer to the real attribute structure.
  struct device_bounded_attr* bounded_attr = container_of(attr, struct device_bounded_attr, base_attr);
  // Having offset of the field, calculate pointer to it
  int field_ptr* = (int*)(((char*)dev) + bounded_attr->field_offset);
  return sprintf(buf, "%d\n", *field_ptr);
}

So attributes can be declared as follows:

static struct device_bounded_attr repetition_attr =
  BOUNDED_ATTR(repetition, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, repetition);
static struct device_bounded_attr counter_attr =
  BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, counter);
static struct device_bounded_attr value_attr =
  BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, value);