container_of (inode->i_cdev, struct scull_dev, cdev) explanation - linux-kernel

I was reading on the explanation of container_of macro and I got most of it except one thing. How does inode->I_cdev point to cdev?
To be sure the cdev here is the character device we initialized in our cdev_init call .

one more thing to add
dev in "struct cdev" is DD point of view and dev_t in "struct inode" is Fs( file system) point of view.
also the Struct inode ( part of fs.h) ---> struct cdev(part of cdev.h & Member object of struct Inode ) ---> ( major , minor )
& the Struct Dev ( user defined ) ---> struct cdev---> ( major , minor)
both reaches the same point .

if you look in chrdev_open function under fs/char_dev.c file you will see that before opening any character device, its respective i_cdev structure is populated in inode's structure.
p = inode->i_cdev;
if (!p) {
struct kobject *kobj;
int idx;
spin_unlock(&cdev_lock);
kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx);
if (!kobj)
return -ENXIO;
new = container_of(kobj, struct cdev, kobj);
spin_lock(&cdev_lock);
/* Check i_cdev again in case somebody beat us to it while
we dropped the lock. */
p = inode->i_cdev;
if (!p) {
inode->i_cdev = p = new;
list_add(&inode->i_devices, &p->list);
new = NULL;
} else if (!cdev_get(p))
ret = -ENXIO;
} else if (!cdev_get(p))
ret = -ENXIO;
Well for the inode itself, it must be created when a device node is created.
I dont know much about kobjects, but its some how getting the cdev using kobj, and where we associated that kobject with this perticular device? the answer lies in cdev_add
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
int error;
p->dev = dev;
p->count = count;
error = kobj_map(cdev_map, dev, count, NULL,
exact_match, exact_lock, p);
if (error)
return error;
kobject_get(p->kobj.parent);
return 0;
}

Related

Busy inodes/dentries after umount in self-written virtual fs

I wrote a simple FS that should only statically contain one file named hello. This file should contain the string Hello, world!. I did this for educational purposes. While the fs is mounted it actually behaves like expected. I can read the file just fine.
However after unmounting I always get
VFS: Busy inodes after unmount of dummyfs. Self-destruct in 5 seconds. Have a nice day...
If I called ls on the rootdir while the fs was mounted I get
BUG: Dentry (ptrval){i=2,n=hello} still in use (-1) [unmount of dummyfs dummyfs]
on top of that.
What does this mean in detail and how can I fix it?
The mount and kill_sb routines call mount_nodev and allocate space for a struct holding the 2 inodes this FS uses.
static struct dentry *dummyfs_mount(struct file_system_type* fs_type,
int flags, const char* dev_name, void* data)
{
struct dentry *ret;
ret = mount_nodev(fs_type, flags, data, dummyfs_fill_super);
if (IS_ERR(ret)) {
printk(KERN_ERR "dummyfs_mount failed");
}
return ret;
}
static void dummyfs_kill_sb(struct super_block *sb) {
kfree(sb->s_fs_info);
kill_litter_super(sb);
}
The fill superblock method creates the 2 inodes and saves them in the struct allocated by mount:
static int dummyfs_fill_super(struct super_block *sb, void *data, int flags)
{
struct dummyfs_info *fsi;
sb->s_magic = DUMMYFS_MAGIC;
sb->s_op = &dummyfs_sops;
fsi = kzalloc(sizeof(struct dummyfs_info), GFP_KERNEL);
sb->s_fs_info = fsi;
fsi->root = new_inode(sb);
fsi->root->i_ino = 1;
fsi->root->i_sb = sb;
fsi->root->i_op = &dummyfs_iops;
fsi->root->i_fop = &dummyfs_dops;
fsi->root->i_atime = fsi->root->i_mtime = fsi->root->i_ctime = current_time(fsi->root);
inode_init_owner(fsi->root, NULL, S_IFDIR);
fsi->file = new_inode(sb);
fsi->file->i_ino = 2;
fsi->file->i_sb = sb;
fsi->file->i_op = &dummyfs_iops;
fsi->file->i_fop = &dummyfs_fops;
fsi->file->i_atime = fsi->file->i_mtime = fsi->file->i_ctime = current_time(fsi->file);
inode_init_owner(fsi->file, fsi->root, S_IFREG);
sb->s_root = d_make_root(fsi->root);
return 0;
}
The lookup method just adds the fsi->file_inode to the dentry if the parent is the root dir:
if (parent_inode->i_ino == fsi->root->i_ino) {
d_add(child_dentry, fsi->file);
}
And the iterate method just emits the dot files and the hello file when called:
if (ctx->pos == 0) {
dir_emit_dots(file, ctx);
ret = 0;
}
if (ctx->pos == 2) {
dir_emit(ctx, "hello", 5, file->f_inode->i_ino, DT_UNKNOWN);
++ctx->pos;
ret = 0;
}
The read method just writes a static string using copy_to_user. The offsets are calculated correctly and on EOF the method just returns 0. However since the problems occur even when the read method was not called I think it is out-of-scope for this already too long question.
For actually running this I use user-mode linux from the git master (4.15+x commit d48fcbd864a008802a90c58a9ceddd9436d11a49). The userland is compiled from scratch and the init process is a derivative of Rich Felker's minimal init to which i added mount calls for /proc, /sys and / (remount).
My command line is ./linux ubda=../uml/image root=/dev/ubda
Any pointers to more thorough documentation are also appreciated.
Using gdb watching the dentry->d_lockref.count I realized that the kill_litter_super call in umount was actually responsible for the dentry issues. Replacing it with kill_anon_super solved that problem.
The busy inode problem vanished too mostly except when i unmounted after immediately after mounting. Allocating the second inode lazily solved that problem too.

Pointer not printing char[] array

I'm writing some code to take in a string, turn it into a char array and then print back to the user (before passing to another function).
Currently the code works up to dat.toCharArray(DatTim,datsize); however, the pointer does not seem to be working as the wile loop never fires
String input = "Test String for Foo";
InputParse(input);
void InputParse (String dat)
//Write Data
datsize = dat.length()+1;
const char DatTim[datsize];
dat.toCharArray(DatTim,datsize);
//Debug print back
for(int i=0;i<datsize;i++)
{
Serial.write(DatTim[i]);
}
Serial.println();
//Debug pointer print back
const char *b;
b=*DatTim;
while (*b)
{
Serial.print(*b);
b++;
}
Foo(*DatTim);
I can't figure out the difference between what I have above vs the template code provided by Majenko
void PrintString(const char *str)
{
const char *p;
p = str;
while (*p)
{
Serial.print(*p);
p++;
}
}
The expression *DatTim is the same as DatTim[0], i.e. it gets the first character in the array and then assigns it to the pointer b (something the compiler should have warned you about).
Arrays naturally decays to pointers to their first element, that is DatTim is equal to &DatTim[0].
The simple solution is to simply do
const char *b = DatTim;

inserting into priority queue. MIT c programming opencourseware

Iam currently trying an exercise from the "practical programming in C" from MIT opencourseware. the exercise is on huffman coding. it is lab2 part 2 where Iam having an Issue. Primarily the pq_insert() method. Iam getting pretty confused as to how the insertion of a node should be performed? I will post the whole .c file below. I think i need the sudo code for an insert operation.
my node is basically a struct (shown below)
struct tnode
{
struct tnode* left; /*used when in tree*/
struct tnode*right; /*used when in tree*/
struct tnode*parent;/*used when in tree*/
struct tnode* next; /*used when in list*/
float freq;
int isleaf;
char symbol;
};
Iam presuming the pointers "left" and "right" are not used in my PQ construction? I just use "parent" and "next" pointers when creating PQ and if the current "freq" value is less than the next checked nodes "freq" value, I add this to the queue before next ?? I may be wrong here but this is one of the areas in which Iam confused??
below is the full file.
#include <stdio.h>
#include <stdlib.h>
#define MAX_SYMBOLS 255
#define MAX_LEN 7
struct tnode
{
struct tnode* left; /*used when in tree*/
struct tnode*right; /*used when in tree*/
struct tnode*parent;/*used when in tree*/
struct tnode* next; /*used when in list*/
float freq;
int isleaf;
char symbol;
};
/*global variables*/
char code[MAX_SYMBOLS][MAX_LEN];
struct tnode* root=NULL; /*tree of symbols*/
struct tnode* qhead=NULL; /*list of current symbols*/
struct cnode* chead=NULL;/*list of code*/
/*
#function talloc
#desc allocates new node
*/
struct tnode* talloc(int symbol,float freq)
{
struct tnode* p=(struct tnode*)malloc(sizeof(struct tnode));
if(p!=NULL)
{
p->left=p->right=p->parent=p->next=NULL;
p->symbol=symbol;
p->freq=freq;
p->isleaf=1;
}
return p;
}
/*
#function display_tnode_list
#desc displays the list of tnodes during code construction
*/
void pq_display(struct tnode* head)
{
struct tnode* p=NULL;
printf("list:");
for(p=head;p!=NULL;p=p->next)
{
printf("(%c,%f) ",p->symbol,p->freq);
}
printf("\n");
}
/*
#function pq_insert
#desc inserts an element into the priority queue
NOTE: makes use of global variable qhead
*/
void pq_insert(struct tnode* p)
{
struct tnode* curr=NULL;
struct tnode* prev=NULL;
printf("inserting:%c,%f\n",p->symbol,p->freq);
if(qhead==NULL) /*qhead is null*/
{
/*TODO: write code to insert when queue is empty*/
//qhead = null means we nead to set something as the heeed!
//possibly p???????
qhead = p;
return;
//not too sure bout this.
}
/*TODO: write code to find correct position to insert*/
//potentially check if 'symbol' less
//than ??
if(curr==qhead)//curr is always set to null when method called????
{
/*TODO: write code to insert before the current start*/
curr->parent = p;
curr = p;
}
else /*insert between prev and next*/
{
/*TODO: write code to insert in between*/
}
}
/*
#function pq_pop
#desc removes the first element
NOTE: makes use of global variable qhead
*/
struct tnode* pq_pop()
{
struct tnode* p=NULL;
p = qhead;
if(qhead->next != NULL)
{
qhead = qhead->next;
}
/*TODO: write code to remove front of the queue*/
printf("popped:%c,%f\n",p->symbol,p->freq);
return p;
}
/*
#function build_code
#desc generates the string codes given the tree
NOTE: makes use of the global variable root
*/
void generate_code(struct tnode* root,int depth)
{
int symbol;
int len; /*length of code*/
if(root->isleaf)
{
symbol=root->symbol;
len =depth;
/*start backwards*/
code[symbol][len]=0;
/*
TODO: follow parent pointer to the top
to generate the code string
*/
printf("built code:%c,%s\n",symbol,code[symbol]);
}
else
{
generate_code(root->left,depth+1);
generate_code(root->right,depth+1);
}
}
/*
#func dump_code
#desc output code file
*/
void dump_code(FILE* fp)
{
int i=0;
for(i=0;i<MAX_SYMBOLS;i++)
{
if(code[i][0]) /*non empty*/
fprintf(fp,"%c %s\n",i,code[i]);
}
}
/*
#func encode
#desc outputs compressed stream
*/
void encode(char* str,FILE* fout)
{
while(*str)
{
fprintf(fout,"%s",code[*str]);
str++;
}
}
/*
#function main
*/
int main()
{
/*test pq*/
struct tnode* p=NULL;
struct tnode* lc,*rc;
float freq[]={0.01,0.04,0.05,0.11,0.19,0.20,0.4};
int NCHAR=7; /*number of characters*/
int i=0;
const char *CODE_FILE="code.txt";
const char *OUT_FILE="encoded.txt";
FILE* fout=NULL;
/*zero out code*/
memset(code,0,sizeof(code));
/*testing queue*/
pq_insert(talloc('a',0.1));
pq_insert(talloc('b',0.2));
pq_insert(talloc('c',0.15));
/*making sure it pops in the right order*/
puts("making sure it pops in the right order");
while((p=pq_pop()))
{
free(p);
}
qhead=NULL;
/*initialize with freq*/
for(i=0;i<NCHAR;i++)
{
pq_insert(talloc('a'+i,freq[i]));
}
/*build tree*/
for(i=0;i<NCHAR-1;i++)
{
lc=pq_pop();
rc=pq_pop();
/*create parent*/
p=talloc(0,lc->freq+rc->freq);
/*set parent link*/
lc->parent=rc->parent=p;
/*set child link*/
p->right=rc; p->left=lc;
/*make it non-leaf*/
p->isleaf=0;
/*add the new node to the queue*/
pq_insert(p);
}
/*get root*/
root=pq_pop();
/*build code*/
generate_code(root,0);
/*output code*/
puts("code:");
fout=fopen(CODE_FILE,"w");
dump_code(stdout);
dump_code(fout);
fclose(fout);
/*encode a sample string*/
puts("orginal:abba cafe bad");
fout=fopen(OUT_FILE,"w");
encode("abba cafe bad",stdout);
encode("abba cafe bad",fout);
fclose(fout);
getchar();
/*TODO: clear resources*/
return 0;
}
i think i am over thinking this really and the left and right pointers are just used in tree construction after the priority queue is created. another thing that confused me was that "curr" is set to null whenever pq_insert() is called? Iam thinking maybe curr is set to the qhead. asuming its "freq" value is less than the "qhead" freq value? Also iam not doing this as homework or anything. anyway, any input is appreciated.
also not really sure how to use "curr" and "prev" pointers in the pq_insert method. if anyone cold point me in the direction of some pseudo code that would also be pretty helpful.
One of the common ways to implement a priority queue is as a heap, and a heap is commonly represented as a tree structure. However, it appears that the OpenCourseware Lab 2 Part B specifically refers to a linked-list implementation for this priority queue.
It is therefore likely that you are correct in assuming that the "left" and "right" pointers won't be used. Additionally, the "parent" pointer likely won't be used because the comments refer to it as being for the tree-based implementation. A "next" pointer is sufficient for a simple linked list, as each node points to the next node starting from the "head" node or start of the list.
What you're looking to do is called "insert in sorted order" and the general process is described here: inserting element into a sorted list
In general, "curr" will take the value of each node of the linked list as you iterate through it, and "prev" will take the value of the previous node (set this before you advance curr.) Setting curr to null at the start of pq_insert is fine because you'll want to start at the first element of the list when you go to insert a new element. You'll want to know what the previous node was in the case where you find that the current node belongs after the node you're trying to insert.

I/O to device from kernel module fails with EFAULT

I have created block device in kernel module. When some I/O happens I read/write all data from/to another existing device (let's say /dev/sdb).
It opens OK, but read/write operations return 14 error(EFAULT,Bad Address). After some research I found that I need map address to user space(probably buffer or filp variables), but copy_to_user function does not help. Also I looked to mmap() and remap_pfn_range() functions, but I can not get how to use them in my code, especially where to get correct vm_area_struct structure. All examples that I found, used char devices and file_operations structure, not block device.
Any hints? Thanks for help.
Here is my code for reading:
mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
filp = filp_open("/dev/sdb", O_RDONLY | O_DIRECT | O_SYNC, 00644);
if(IS_ERR(filp))
{
set_fs(old_fs);
int err = PTR_ERR(filp);
printk(KERN_ALERT"Can not open file - %d", err);
return;
}
else
{
bytesRead = vfs_read(filp, buffer, nbytes, &offset); //It gives 14 error
filp_close(filp, NULL);
}
set_fs(old_fs);
I found a better way for I/O to block device from kernel module. I have used bio structure for that. Hope this information save somebody from headache.
1) So, if you want to redirect I/O from your block device to existing block device, you have to use own make_request function. For that you should use blk_alloc_queue function to create queue for your block device like this:
device->queue = blk_alloc_queue(GFP_KERNEL);
blk_queue_make_request(device->queue, own_make_request);
Than into own_make_request function change bi_bdev member into bio structure to device in which you redirecting I/O and call generic_make_request function:
bio->bi_bdev = device_in_which_redirect;
generic_make_request(bio);
More information here at 16 chapter. If link is broken by some cause, here is name of the book - "Linux Device Drivers, Third Edition"
2) If you want read or write your own data to existing block device from kernel module you should use submit_bio function.
Code for writing into specific sector(you need to implement writeComplete function also):
void writePage(struct block_device *device,
sector_t sector, int size, struct page *page)
{
struct bio *bio = bio_alloc(GFP_NOIO, 1);
bio->bi_bdev = vnode->blkDevice;
bio->bi_sector = sector;
bio_add_page(bio, page, size, 0);
bio->bi_end_io = writeComplete;
submit_bio(WRITE_FLUSH_FUA, bio);
}
Code for reading from specific sector(you need to implement readComplete function also):
int readPage(struct block_device *device, sector_t sector, int size,
struct page *page)
{
int ret;
struct completion event;
struct bio *bio = bio_alloc(GFP_NOIO, 1);
bio->bi_bdev = device;
bio->bi_sector = sector;
bio_add_page(bio, page, size, 0);
init_completion(&event);
bio->bi_private = &event;
bio->bi_end_io = readComplete;
submit_bio(READ | REQ_SYNC, bio);
wait_for_completion(&event);
ret = test_bit(BIO_UPTODATE, &bio->bi_flags);
bio_put(bio);
return ret;
}
page can be allocated with alloc_page(GFP_KERNEL). Also for changing data in page use page_address(page). It returns void* so you can interpret that pointer as whatever you want.

Task switching using a queue

i'm developing my own hobby os, and now i'm stuck with a problem on the scheduler/task switching.
I planned to use a FIFO queue as structure to hold processes. I implemented it using linked list.
I also decided to use the iret method to switch from a task to another (so when the os was serving an interrupt request just before the iret i change the ESP register in order to move to the new task).
But i have a problem.
When the os start it launch two tasks:
idle
shell
And with these two i have no problem.
But if i try to launch two other tasks (with a simply printf inside), the task queue was corrupted.
If after that i try to print the queue it print only two tasks that are the 2 just created and with idle and shell disappeared, but the os continues to work (i think that in a specific moment the esp field of the new tasks was replaced with the esp content of the shell).
The task data structure is:
typedef struct task_t{
pid_t pid;
char name[NAME_LENGTH];
void (*start_function)();
task_state status;
task_register_t *registers;
unsigned int cur_quants;
unsigned int eip;
long int esp;
unsigned int pdir;
unsigned int ptable;
struct task_t *next;
}task_t;
and the tss is:
typedef struct {
unsigned int edi; //+0
unsigned int esi; //+1
unsigned int ebp; //+2
unsigned int esp; //+3 (can be null)
unsigned int ebx; //+4
unsigned int edx; //+5
unsigned int ecx; //+6
unsigned int eax; //+7
unsigned int eip; //+8
unsigned int cs; //+9
unsigned int eflags; //+10
unsigned int end;
} task_register_t;
The scheduler function is the following:
void schedule(unsigned int *stack){
asm("cli");
if(active == TRUE){
task_t* cur_task = dequeue_task();
if(cur_task != NULL){
cur_pid = cur_task->pid;
dbg_bochs_print("#######");
dbg_bochs_print(cur_task->name);
if(cur_task->status!=NEW){
cur_task->esp=*stack;
} else {
cur_task->status=READY;
((task_register_t *)(cur_task->esp))->eip = cur_task->eip;
}
enqueue_task(cur_task->pid, cur_task);
cur_task=get_task();
if(cur_task->status==NEW){
cur_task->status=READY;
}
dbg_bochs_print(" -- ");
dbg_bochs_print(cur_task->name);
dbg_bochs_print("\n");
//load_pdbr(cur_taskp->pdir);
*stack = cur_task->esp;
} else {
enqueue_task(cur_task->pid, cur_task);
}
}
active = FALSE;
return;
asm("sti");
}
The tss is initalized with the following values:
void new_tss(task_register_t* tss, void (*func)()){
tss->eax=0;
tss->ebx=0;
tss->ecx=0;
tss->edx=0;
tss->edi =0;
tss->esi =0;
tss->cs = 8;
tss->eip = (unsigned)func;
tss->eflags = 0x202;
tss->end = (unsigned) suicide;
//tss->fine = (unsigned)end; //per metterci il suicide
return;
}
And the function that creates a new task is the following:
pid_t new_task(char *task_name, void (*start_function)()){
asm("cli");
task_t *new_task;
table_address_t local_table;
unsigned int new_pid = request_pid();
new_task = (task_t*)kmalloc(sizeof(task_t));
strcpy(new_task->name, task_name);
new_task->next = NULL;
new_task->start_function = start_function;
new_task->cur_quants=0;
new_task->pid = new_pid;
new_task->eip = (unsigned int)start_function;
new_task->esp = (unsigned int)kmalloc(STACK_SIZE) + STACK_SIZE-100;
new_task->status = NEW;
new_task->registers = (task_register_t*)new_task->esp;
new_tss(new_task->registers, start_function);
local_table = map_kernel();
new_task->pdir = local_table.page_dir;
new_task->ptable = local_table.page_table;
//new_task->pdir = 0;
//new_task->ptable = 0;
enqueue_task(new_task->pid, new_task);
//(task_list.current)->cur_quants = MAX_TICKS;
asm("sti");
return new_pid;
}
I'm sure that i just forgot something, or i miss some consideration. But i cannot figure what i'm missing.
Actually i'm working only in kernel mode, and inside the same address space (pagiing is enabled, but actually i use the same pagedir for all tasks).
The ISR macros are defined here:
https://github.com/inuyasha82/DreamOs/blob/master/include/processore/handlers.h
I declared four kinds of function in order to handle ISR:
EXCEPTION
EXCEPTION_EC (an exception with an error code)
IRQ
SYSCALL
Obviously the scheduler is called by an IRQ routine, so the macro looks like:
__asm__("INT_"#n":"\
"pushad;" \
"movl %esp, %eax;"\
"pushl %eax;"\
"call _irqinterrupt;"\
"popl %eax;"\
"movl %eax, %esp;"\
"popad;"\
"iret;")
the irq handler function is:
void _irqinterrupt(unsigned int esp){
asm("cli;");
int irqn;
irqn = get_current_irq();
IRQ_s* tmpHandler;
if(irqn>=0) {
tmpHandler = shareHandler[irqn];
if(tmpHandler!=0) {
tmpHandler->IRQ_func();
#ifdef DEBUG
printf("2 - IRQ_func: %d, %d\n", tmpHandler->IRQ_func, tmpHandler);
#endif
while(tmpHandler->next!=NULL) {
tmpHandler = tmpHandler->next;
#ifdef DEBUG
printf("1 - IRQ_func (_prova): %d, %d\n", tmpHandler->IRQ_func, tmpHandler);
#endif
if(tmpHandler!=0) tmpHandler->IRQ_func();
}
} else printf("irqn: %d\n", irqn);
}
else printf("IRQ N: %d E' arrivato qualcosa che non so gestire ", irqn);
if(irqn<=8 && irqn!=2) outportb(MASTER_PORT, EOI);
else if(irqn<=16 || irqn==2){
outportb(SLAVE_PORT, EOI);
outportb(MASTER_PORT, EOI);
}
schedule(&esp);
asm("sti;");
return;
}
And these are the enqueue_task and dequeue_task functions:
void enqueue_task(pid_t pid, task_t* n_task){
n_task->next=NULL;
if(task_list.tail == NULL){
task_list.head = n_task;
task_list.tail = task_list.head;
} else {
task_list.head->next=n_task;
task_list.head = n_task;
}
}
task_t* dequeue_task(){
if(task_list.head==NULL){
return NULL;
} else {
task_t* _task;
_task = task_list.tail;
task_list.tail=_task->next;
return _task;
}
return;
}
Thanks in advance,
and let me know if you need more details!
It is hard to tell. How does your assembly part of the isr look like? What makes me think is the problem (since you can save and restore two tasks but not more) is that you don't push and pop all registers properly. You do use pusha and popa for the isr right?
I also want to add that having cli and sti like you have done there can be dangerous. In your isrs set cli as the first opcode. Then you wont need to use sti at all because iret will automatically flip this on for you (it is actually a bit in the eflags register).
Good luck!

Resources