mattmunee
mattmunee

Reputation: 29

Getting correct kobject for sysfs_notify()

I'm working on a PMBus device driver kernel module. (This is my first kernel module, so please be nice.) I've added a GPIO interrupt handler which just keeps track of the number of edges received on the GPIO pin, and I've added a sysfs entry to display the number of events that have occurred. This functionality works perfectly.

However, within the interrupt handler, I would like to add a sysfs_notify() call so that user-space code can poll() the sysfs file that's keeping track of edges. Unfortunately, the poll is not working, and I believe it's because I am feeding the wrong kobject to sysfs_notify().

struct tps25990_data {
    struct device *dev;
    struct pmbus_driver_info info;
    ...
    unsigned int num_gpio_faults;
};
...
static ssize_t num_gpio_faults_show(struct device *dev,
              struct device_attribute *attr, char *buf)
{

    struct i2c_client *client = to_i2c_client(dev->parent);
    const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
    struct tps25990_data *data = to_tps25990_data(info);

    return sysfs_emit(buf, "%d\n", data->num_gpio_faults);
}

static DEVICE_ATTR_RO(num_gpio_faults);

static struct attribute *attributes[] = {
    &dev_attr_num_gpio_faults.attr,
    NULL,
};

static const struct attribute_group attr_group = {
    .attrs  = attributes,
};

static const struct attribute_group *tps25990_attribute_groups[] = {
    &attr_group,
    NULL,
};

...
static irqreturn_t efuse_fault_gpio_irq_handler(int irq, void *dev_id){
    struct tps25990_data *data = dev_id;
    printk(KERN_INFO "Efuse fault detected on gpio%d\n", desc_to_gpio(data->fault_gpio));
    data->num_gpio_faults++;
    const char *path;
    path = kobject_get_path(&data->dev->kobj, GFP_KERNEL);
    dev_info(data->dev, "efuse_fault_gpio_irq_handler Path = %s\n", path);
    kfree(path);
    sysfs_notify(&data->dev->kobj, NULL, "num_gpio_faults");
    return IRQ_HANDLED;
}

static int tps25990_probe(struct i2c_client *client)
{
    ...
    struct tps25990_data *data;
    struct pmbus_driver_info *info;
    ...
    data = devm_kzalloc(&client->dev, sizeof(struct tps25990_data), GFP_KERNEL);
    data->dev = &client->dev;
    info = &data->info;
    info->groups = tps25990_attribute_groups;
    ...
    data->fault_gpio  = devm_gpiod_get_optional(&client->dev, "fault", GPIOD_IN);
    if (data->fault_gpio) {
        gpiod_set_consumer_name(data->fault_gpio, "EFUSE FAULT");
        data->fault_gpio_irq = gpiod_to_irq(data->fault_gpio);
        if (data->fault_gpio_irq < 0) {
            dev_err(&client->dev, "No corresponding irq for fault-gpio\n");
            return data->fault_gpio_irq;
        }
        ret = irq_set_irq_type(data->fault_gpio_irq, IRQ_TYPE_EDGE_FALLING);
        ret = devm_request_threaded_irq(&client->dev,
                data->fault_gpio_irq,
                NULL,
                efuse_fault_gpio_irq_handler,
                0,
                "efuse fault",
                info);
        if (ret) {
            dev_info(&client->dev, "Failed to request fault-gpio irq (code: %d)\n", ret);
            irq_dispose_mapping(data->fault_gpio_irq);
            return ret;
        }
    } else {
        dev_info(&client->dev, "No fault-gpio found in device tree\n");
    }

    data->num_gpio_faults = 0;

    ret = pmbus_do_probe(client, info);
    ...
}

Once this module is running, I can call cat num_gpio_faults in the appropriate sysfs directory. The correct number of event edges is displayed, and dmesg shows:

[  233.630694] hwmon hwmon3: num_gpio_faults_show Path = /devices/platform/soc/fe804000.i2c/i2c-1/i2c-22/22-0046/hwmon/hwmon3

However, when I generate an edge to fire the interrupt, dmesg shows:

[  245.874307] Efuse fault detected on gpio579
[  245.874342] tps25990 22-0046: efuse_fault_gpio_irq_handler Path = /devices/platform/soc/fe804000.i2c/i2c-1/i2c-22/22-0046

So, it appears that whatever struct device is being fed to the interrupt handler (which is the struct device contained in the i2c_client) is different from the struct device that is fed to my num_gpio_faults_show() function. The struct device that is fed to my num_gpio_faults_show() function appears to have the full path in the kobject. However, I haven't been able to determine how to get that correct struct device fed to my interrupt handler in order to have the correct kobject to send with sysfs_notify().

I believe that the correct struct device is the hwmon_dev item that is within struct pmbus_data. However, that struct is not exported and not available in my module.

Any suggestions?

I've tried using the i2c_client device but it has the incorrect kobject.

Upvotes: 0

Views: 217

Answers (1)

mattmunee
mattmunee

Reputation: 29

Well I've found a solution, but I'm sure it's not an advisable way to do it. I've added this snippet to my probe function. Here I've used sysfs_get_dirent() to traverse the subdirectories associated with my struct device. I've added the correct kobject as part of my driver data (data->hwmon_kobj), and now in my interrupt handler I can call sysfs_notify(data->hwmon_kobj, NULL, "num_gpio_faults"). This works as intended. I hate this solution because it assumes a priori knowledge of the subdirectory names (here I know that the subdirectory is named ".../hwmon/hwmon3") and it uses the priv member of the kernfs_node (which I'm sure is a no-no). Anyone with a better idea? Is there a more acceptable way to recursively search through subdirectories for a kobject?

struct kernfs_node* kn = sysfs_get_dirent(data->dev->kobj.sd, "hwmon");

if (!kn) {
    dev_err(data->dev, "Failed to get hwmon kn\n");
} else {
    struct kobject *kobj = (struct kobject *)(kn->priv);

    if (!kobj) {
        dev_err(data->dev, "Failed to get hwmon kobj\n");
    } else {
        char hwmon_subdir[8];
        struct kernfs_node* subdir_kn;

        for (int i = 0; i < 100; i++) {
            sprintf(hwmon_subdir, "hwmon%d", i);
            subdir_kn = sysfs_get_dirent(kobj->sd, hwmon_subdir);
            if (subdir_kn)
                break;
        }

        if (!subdir_kn) {
            dev_err(data->dev, "Failed to get hmwon subdirectory kn\n");
        } else {
            data->hwmon_kobj = (struct kobject *)(subdir_kn->priv);
            if (!data->hwmon_kobj) {
                dev_err(data->dev, "Failed to get hmwon subdirectory kobj\n");
            } else {
                const char *path = kobject_get_path(data->hwmon_kobj, GFP_KERNEL);

                dev_info(data->dev, "hwmon subdirectory path: %s\n", path);
                kfree(path);
            }
        }
    }
}

Upvotes: 0

Related Questions