Confused on creation of binary sysfs entry - c

On kernel 4.0, when stepping through the kernel source for sysfs_create_bin_file, I notice it passes to sysfs_add_file(kobj->sd, &attr->attr, true); The &attr->attr being the struct attribute struct within the bin_attribute struct.
This makes sense until I visit sysfs_add_file_mode_ns, which is directly called from sysfs_add_file, and on line #277 sets temp variable stuct bin_attribute *battr = (void*)attr;
Isn't this pointing to a struct attribute at this point, how is it resolving this to the proper struct (due to the call to sysfs_add_file using &attr->attr on line #483)?
Code
int sysfs_create_bin_file(struct kobject *kobj,
const struct bin_attribute *attr)
{
BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file(kobj->sd, &attr->attr, true);
}
int sysfs_add_file(struct kernfs_node *parent, const struct attribute *attr,
bool is_bin)
{
return sysfs_add_file_mode_ns(parent, attr, is_bin, attr->mode, NULL);
}
int sysfs_add_file_mode_ns(struct kernfs_node *parent,
const struct attribute *attr, bool is_bin,
umode_t mode, const void *ns)
{
struct lock_class_key *key = NULL;
const struct kernfs_ops *ops;
struct kernfs_node *kn;
loff_t size;
if (!is_bin) {
...
} else {
struct bin_attribute *battr = (void *)attr;
...
}

Line
stuct bin_attribute *battr = (void*)attr;
correctly obtains pointer to the bin_attribute structure from the pointer to its first field attr of type struct attribute.
Normally, Linux kernel developers tend to use container_of macro for obtaining pointer to the structure type knowing pointer to its field. More "canonical" way for the transformation above would be:
stuct bin_attribute *battr = container_of(attr, struct bin_attribute, attr);
(In this call the first attr argument refers to the pointer, and the third attr argument refers to the field's name).

Related

What is the intended usage of DEVICE_INT_ATTR?

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.
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);

What is this technique in linux kernel called?

Hi i have a watchdog linux driver as below:
struct omap_wdt_dev {
struct watchdog_device wdev;
struct device *dev;
void __iomem *base_addr;
uint32_t ttgr_pattern;
};
static int omap_wdt_start(struct watchdog_device *wdev)
{
struct omap_wdt_dev *omap_wdev = watchdog_get_drvdata(wdev);
/*...*/
omap_wdt_reload(omap_wdev);
return 0;
}
static int omap_wdt_plf_probe(struct platform_device *pdev)
{
struct omap_wdt_dev *omap_wdev;
omap_wdev = kzalloc(sizeof(*omap_wdev), GFP_KERNEL);
watchdog_set_drvdata(wdev, (void *)omap_wdev);
/*...*/
return 0;
}
In this code, they have created a struct omap_wdt_dev which have a watchdog_deviceinside it. Then in probe function omap_wdt_plf_probe, they use a void pointer inside watchdog_device to point to "wdev" by the function:watchdog_set_drvdata(wdev, (void *)omap_wdev). Then in function omap_wdt_start which have input parameter is a watchdog_device, they get the omap_wdt_dev from watchdog_device to use omap_wdt_dev.
I see this technical, use a void pointer to contain the data of a struct, is used frequently in linux kernel, but i do not know the name of this technique.
Can you explain me more about it and show me some another technique which is used in linux kernel ?

Static allocation of struct members within another static struct?

I am trying to implement a low-level thread lock without the use of dynamic memory allocation; this code will basically be used on a completely bare-bones kernel.
However, I am running into the problem of receiving a seg fault when I am trying to dereference a member inside this global static struct. My code is as such
My wrapper struct
/** LOCKING STRUCT & FUNCTIONS **/
struct lock {
int free;
struct thread_list* wait_list;
struct thread* current_holder;
};
The nested struct(intended as a linked list sort of deal)
struct thread_list {
struct thread *head;
};
And the member inside this list
struct thread {
void *top; // top of the stack for this thread
void *sp; // current stack pointer for this thread (context)
void (*start_func)(void *);
void *arg;
int state;
int exit_value;
struct thread *join_thread;
struct thread *next_thread;
int id;
};
The method I'm trying to implement is as such
void lock_init (struct lock *lk) {
lk->free = 1; //Set lock as free
struct thread_list waiting = lk->wait_list; //Get waitlist, works fine
waiting->head = NULL; //Set waitlist's head to null, SEGFAULTS HERE
}
I am not super proficient at C, but I can't seem to figure out the correct methodology/syntax to make my code work like this.
struct thread_list waiting = lk->wait_list; //Get waitlist, works fine
waiting->head = NULL; //Set waitlist's head to null, SEGFAULTS HERE
waiting is not a struct pointer but a struct variable . To access member using it you need to use . operator -
waiting.head = NULL;
Or to use -> operator declare it as a struct pointer .

undefined symbol of a struct pointer

I'm working on xinu, and I need to change some *.c files.
I have this struct in file ready.c:
struct newtimer{
struct newtimer* tnext;
struct newtimer* tprev;
int tkey;
int tprio;
int tcount;
};
and then I declared:
struct newtimer *timer = NULL;
I did some use with the timer variable in this file and I need to use it in another file as well (clkint.c). So in clkint I did this:
extern struct newtimer *timer;
(which compiles alright)
but when i try to access timer's fields I get these errors:
What am I doing wrong?
Thank you
edit:
As requested, here is some of the clkint.c:
struct newtimer *t;
extern struct newtimer *timer;
...
t = timer;
while(t!= NULL)
{
++(t->tcount);
if(t->tcount >= 18){
t->tcount = 0;
newprior = proctab[t->tkey]->pprio + 10;
t->tcount = newprior;
chprio(t->tkey, newprior);
}
t = t->tnext;
resched();
}
edit:
Replacing all the ts with timers does not solve the problem.
Your struct newtimer type is not defined. You probaly forgot to include the header file that defines struct newtimer.
When you use an unknown struct name in struct something, C compiler treats that as a forward declaration of a completely new struct type. The type is, of course, incomplete, which is why you are not allowed to access any innards of that type. The compiler simply knows nothing about those innards.
Where is your struct newtimer defined? If it is defined in a header file, you have to include it into your clkint.c.

passing argument from incompatible pointer type

static struct dll_wifi_state **dll_states;
enum dll_type {
DLL_UNSUPPORTED,
DLL_ETHERNET,
DLL_WIFI
};
struct dll_state {
enum dll_type type;
union {
struct dll_eth_state *ethernet;
struct dll_wifi_state *wifi;
} data;
};
static struct dll_state *dll_states = NULL;
struct dll_wifi_state {
int link;
// A pointer to the function that is called to pass data up to the next layer.
up_from_dll_fn_ty nl_callback;
bool is_ds;
};
This is the method whose pointer is being passed in the dll_wifi_state struct.
static void up_from_dll(int link, const char *data, size_t length)
{
//some code here
}
In other file, I am calling this method
void reboot_accesspoint()
{
// We require each node to have a different stream of random numbers.
CNET_srand(nodeinfo.time_of_day.sec + nodeinfo.nodenumber);
// Provide the required event handlers.
CHECK(CNET_set_handler(EV_PHYSICALREADY, physical_ready, 0));
// Prepare to talk via our wireless connection.
CHECK(CNET_set_wlan_model(my_WLAN_model));
// Setup our data link layer instances.
dll_states = calloc(nodeinfo.nlinks + 1, sizeof(struct dll_state));
for (int link = 0; link <= nodeinfo.nlinks; ++link) {
switch (linkinfo[link].linktype) {
case LT_LOOPBACK:
dll_states[link].type = DLL_UNSUPPORTED;
break;
case LT_WAN:
dll_states[link].type = DLL_UNSUPPORTED;
break;
case LT_LAN:
dll_states[link].type = DLL_ETHERNET;
dll_states[link].data.ethernet = dll_eth_new_state(link, up_from_dll);
break;
case LT_WLAN:
dll_states[link].type = DLL_WIFI;
dll_states[link].data.wifi = dll_wifi_new_state(link,
up_from_dll,
true /* is_ds */);
break;
}
}
// printf("reboot_accesspoint() complete.\n");
}
It works fine like this, but I want to add another argument i.e. up_from_dll((int link, const char *data, size_t length, int seq). And as soon as I add this argument, following error starts coming up
ap.c:153: warning: passing argument 2 of ‘dll_wifi_new_state’ from incompatible pointer type
Is there a way of adding another argument to that method without getting error ??? I am really bad with pointers :(
Any help would be much appreciated.
Line 153 :
dll_states[link].data.wifi = dll_wifi_new_state(link,
up_from_dll,
true /* is_ds */);
And method
struct dll_wifi_state *dll_wifi_new_state(int link,
up_from_dll_fn_ty callback,
bool is_ds)
{
// Ensure that the given link exists and is a WLAN link.
if (link > nodeinfo.nlinks || linkinfo[link].linktype != LT_WLAN)
return NULL;
// Allocate memory for the state.
struct dll_wifi_state *state = calloc(1, sizeof(struct dll_wifi_state));
// Check whether or not the allocation was successful.
if (state == NULL)
return NULL;
// Initialize the members of the structure.
state->link = link;
state->nl_callback = callback;
state->is_ds = is_ds;
return state;
}
I haven't changed anything else apart from adding the new parameter to up_from_dll.
The second parameter to dll_wifi_new_state is up_from_dll_fn_ty callback.
It's not in your code listing right now, but up_from_dll_fn_ty is a typedef saying that the up_from_dll_fn_ty is a function pointer with specific parameters (which don't include int seq)
When you updated up_from_dll with different parameters, it no longer matches the type specified by up_from_dll_fn_ty and expected as the second parameter for dll_wifi_new_state. You'll need to add the parameter to up_from_dll_fn_ty and you should be good.
If you post the definition of up_from_dll_fn_ty, it would make the question have all the information and allow me to help you more if you still need it.
You're looking for something like:
typedef void (*up_from_dll_fn_ty)(int link, const char *data, size_t length);
and change it to
typedef void (*up_from_dll_fn_ty)(int link, const char *data, size_t length, int seq);
Here's a link to a question that has good information about creating typedefs for function pointers:
Understanding typedefs for function pointers in C

Resources