How to Initialize the Attribute Group Correctly for a Platform Driver

How do I initialize the attribute group correctly for a platform driver?

The problem arose from assigning the attr_groups structure here:

static struct platform_driver platform = {
.remove = my_remove,
.probe = my_probe,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.groups = attr_groups, /* WRONGO: should not be assigned here. */
.of_match_table = of_match,
},
};

By doing it in struct class instead, everything works as expected:

static int __init cz_tdm_init(void)
{
int ret;
pr_info("MODNAME=%s", KBUILD_MODNAME);
/* Dynamically allocate a major number and a
* range of DEV_MINOR_NUMBER_COUNT (1) minor numbers. */
if ((ret = alloc_chrdev_region(&first, 0, DEV_MINOR_NUMBER_COUNT, DRIVER_NAME)) < 0) {
ret = -ENODEV;
goto exit;
}
/* Add the char device to the system. */
cdev_init(&cdev, &fops);
if ((ret = cdev_add(&cdev, first, DEV_MINOR_NUMBER_COUNT)) < 0) {
ret = -ENODEV;
goto exit;
}
resources.cdev = 1;
/* Create a class entry in sysfs */
if ((class = class_create(THIS_MODULE, "coveloz")) == NULL) {
pr_err("Couldn't create 'struct class' structure.");
ret = -ENODEV;
goto exit;
}
class->dev_groups = attr_groups; /* RIGHTO */

/* Create the /dev/ file system entry */
/* return value ignored: there's a 'struct class' to 'struct device' mapping */
if (device_create(class, NULL, first, NULL, DRIVER_NAME) == NULL) {
pr_err("Couldn't create entry in '/dev/' file system.");
ret = -ENODEV;
goto exit;
}

How to attach file operations to sysfs attribute in platform driver?

It boils down to next:

  • reuse existing kobject from struct device (from your struct platform_device) for sysfs_create_group() (instead of creating your own kobject)
  • use DEVICE_ATTR() to declare struct device_attribute instead of regular __ATTR(), which creates struct kobj_attribute.

Here is how I created sysfs attributes for my platform driver.

  1. Create structure you'll be using as private data in show() / store() operations for your sysfs attribute (file). For example:

    struct mydrv {
    struct device *dev;
    long myparam;
    };
  2. Allocate this structure in your driver's probe():

    static int mydrv_probe(struct platform_device *pdev)
    {
    struct mydrv *mydrv;

    mydrv = devm_kzalloc(&pdev->dev, sizeof(*mydrv), GFP_KERNEL);
    mydrv->dev = &pdev->dev;
    platform_set_drvdata(pdev, mydrv);

    ...
    }
  3. Create show() / store() functions:

    static ssize_t mydrv_myparam_show(struct device *dev,
    struct device_attribute *attr, char *buf)
    {
    struct mydrv *mydrv = dev_get_drvdata(dev);
    int len;

    len = sprintf(buf, "%d\n", mydrv->myparam);
    if (len <= 0)
    dev_err(dev, "mydrv: Invalid sprintf len: %d\n", len);

    return len;
    }

    static ssize_t mydrv_myparam_store(struct device *dev,
    struct device_attribute *attr, const char *buf, size_t count)
    {
    struct mydrv *mydrv = dev_get_drvdata(dev);

    kstrtol(buf, 10, &mydrv->myparam);
    return count;
    }
  4. Create device attribute for those functions (right after those functions):

    static DEVICE_ATTR(myparam, S_IRUGO | S_IWUSR, mydrv_myparam_show,
    mydrv_myparam_store);
  5. Declare attributes table (listing in fact sysfs files for you driver):

    static struct attribute *mydrv_attrs[] = {
    &dev_attr_myparam.attr,
    NULL
    };
  6. Declare attribute group (specifying in fact sysfs directory for your driver):

    static struct attribute_group mydrv_group = {
    .name = "mydrv",
    .attrs = mydrv_attrs,
    };

    static struct attribute_group *mydrv_groups[] = {
    &mydrv_group,
    NULL
    }

    which can be actually replaced with one line:

    ATTRIBUTE_GROUPS(mydrv);
  7. Create sysfs directory and files in your driver's probe() function:

    static int mydrv_probe(struct platform_device *pdev)
    {
    int ret;

    ...

    ret = sysfs_create_group(&pdev->dev.kobj, &mydrv_group);
    if (ret) {
    dev_err(&pdev->dev, "sysfs creation failed\n");
    return ret;
    }

    ...
    }
  8. Remove your sysfs files in your driver's remove() function:

    static int mydrv_remove(struct platform_device *pdev)
    {
    sysfs_remove_group(&pdev->dev.kobj, &mydrv_group);
    ...
    }

Race condition note

As @FranzForstmayr correctly pointed out, there may be race condition when adding sysfs files with sysfs_create_group() in mydrv_probe(). That's because user-space can be already notified that those files exist before mydrv_probe() called (where those files are actually being created by sysfs_create_group() function). This issue covered in details in "How to Create a sysfs File Correctly" article by Greg Kroah-Hartman.

So in our case of platform_device, instead of calling sysfs_create_group() (and its counterpart sysfs_remove_group()), you can use default attribute group. To do so, you need to assign corresponding .groups field of your struct device to your attribute groups variable:

static int mydrv_probe(struct platform_device *pdev)
{
...

pdev->dev.groups = mydrv_groups;

...
}

DISCLAIMER: I didn't test this code, though it should work, because of this code.

See [1,2,3] links for more insights on mentioned race condition.

For more examples, run next command in kernel source directory:

$ git grep -l --all-match -e platform_device -e attribute -e '\.groups =' -- drivers/

Also you can search by "default attribute" in commit messages:

$ git log --no-merges --oneline --grep="default attribute" -- drivers/

Some commits I found this way: [4,5,6,7].

References

[1] My attributes are way too racy, what should I do?

[2] PATCH: sysfs: add devm_sysfs_create_group() and friends

[3] [GIT PATCH] Driver core patches for 3.11-rc2

[4] commit 1

[5] commit 2

[6] commit 3

[7] commit 4

How do I properly initialize a late variable

If you got a LateInitializationError before, it's not that "[result()] doesn't recognize the value I initialized in calcBMI()", it's that you called result() before you called calcBMI().

Giving _bmi an initial value avoids the LateInitializationError, but you still have the same fundamental problem: you're reading _bmi before you call calcBMI() to assign it the value you actually want.

In particular, you have:

return ResultsPage(result: calc.result(), bMI: calc.calcBMI(), interpretation: calc.interpretation());

Dart evaluates function arguments in left-to-right order, so you'll call calc.result() first, then calc.calcBMI(), and then calc.interpretation(). Changing the order should fix your problem:

return ResultsPage(
bMI: calc.calcBMI(),
result: calc.result(),
interpretation: calc.interpretation(),
);

However, relying on argument evaluation order would be poor style. It would not be obvious to readers (including your future self) that argument order matters. It would be much better to explicitly order operations that are order-dependent:

var bmi = calc.calcBMI();
return ResultsPage(
result: calc.result(),
bMI: bmi,
interpretation: calc.interpretation(),
);

Note that this has nothing to do with _bmi being late. Declaring _bmi as late provides no purpose in your code as currently written, and you could just remove it. But you also should consider rewriting your code to make CalculatorBrain less dependent on consumers calling its methods in a particular order. Some possibilities:

Compute _bmi dynamically

You could make _bmi a getter that computes the correct value for weight/height on every access:

class CalculatorBrain {
CalculatorBrain({required this.weight, required this.height});

int weight;
int height;

double get _bmi => weight / pow(height / 100, 2);

String calcBMI() => _bmi.toStringAsFixed(1);

String result() {
final _bmi = this._bmi;
if(_bmi >= 25) {
return 'overweight';
} else if(_bmi > 18.5) {
return 'normal';
} else {
return 'underweight';
}
}

...

Compute _bmi exactly once

If you make weight/height final, then you can compute _bmi once and be done:

class CalculatorBrain {
CalculatorBrain({required this.weight, required this.height});

final int weight;
final int height;

// `late` computes `_bmi` lazily, which is necessary because it depends on
// `weight` and `height`.
late final double _bmi = weight / pow(height / 100, 2);

...

Update _bmi automatically if weight or height changes

If you weight/height must be mutable, then you could create setters so that _bmi is always updated automatically:

class CalculatorBrain {
CalculatorBrain({required int weight, required int height})
: _weight = weight,
_height = height {
_updateBmi();
}

late double _bmi;

int _weight;
int get weight => _weight;
set weight(int value) {
_weight = value;
_updateBmi();
}

int _height;
int get height => _height;
set height(int value) {
_height = value;
_updateBmi();
}

void _updateBmi() {
_bmi => weight / pow(height / 100, 2);
}

...

Is this call to `device_show_int()` a Linux kernel bug?

You're confusing device_attribute and driver_attribute. The function drv_attr_show() works on a struct driver_attribute, which is defined as:

struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *driver, char *buf);
ssize_t (*store)(struct device_driver *driver, const char *buf,
size_t count);
};

So no bug here.

Lateinit variable is not initialized in TestNG's @BeforeSuite

TestNG will create a new instance of BaseTest class for you per each test.
If you want to share your driver - make it static. Example:

abstract class BaseTest {
companion object {
lateinit var driver: AppiumDriver<MobileElement>
}
}

Spring boot doesn't load data to initialize database using data.sql

Try adding this line in application.properties:

spring.sql.init.mode=always

Or for Spring Boot before 2.5:

spring.datasource.initialization-mode=always

MySQL JDBC Driver 5.1.33 - Time Zone Issue

Apparently, to get version 5.1.33 of MySQL JDBC driver to work with UTC time zone, one has to specify the serverTimezone explicitly in the connection string.

jdbc:mysql://localhost/db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC

Property '...' has no initializer and is not definitely assigned in the constructor

I think you are using the latest version of TypeScript. Please see the section "Strict Class Initialization" in the link.

There are two ways to fix this:

A. If you are using VSCode you need to change the TS version that the editor use.

B. Just initialize the array when you declare it

makes: any[] = [];

or inside the constructor:

constructor(private makeService: MakeService) { 
// Initialization inside the constructor
this.makes = [];
}

Correct approach to validate attributes of an instance of class

You can use Python properties to cleanly apply rules to each field separately, and enforce them even when client code tries to change the field:

class Spam(object):
def __init__(self, description, value):
self.description = description
self.value = value

@property
def description(self):
return self._description

@description.setter
def description(self, d):
if not d: raise Exception("description cannot be empty")
self._description = d

@property
def value(self):
return self._value

@value.setter
def value(self, v):
if not (v > 0): raise Exception("value must be greater than zero")
self._value = v

An exception will be thrown on any attempt to violate the rules, even in the __init__ function, in which case object construction will fail.

UPDATE: Sometime between 2010 and now, I learned about operator.attrgetter:

import operator

class Spam(object):
def __init__(self, description, value):
self.description = description
self.value = value

description = property(operator.attrgetter('_description'))

@description.setter
def description(self, d):
if not d: raise Exception("description cannot be empty")
self._description = d

value = property(operator.attrgetter('_value'))

@value.setter
def value(self, v):
if not (v > 0): raise Exception("value must be greater than zero")
self._value = v


Related Topics



Leave a reply



Submit