Advanced Char Driver Operations Linux Kernel Programming CIS 4930/COP 5641.

Slides:



Advertisements
Similar presentations
Device Drivers. Linux Device Drivers Linux supports three types of hardware device: character, block and network –character devices: R/W without buffering.
Advertisements

RT_FIFO, Device driver.
R4 Dynamically loading processes. Overview R4 is closely related to R3, much of what you have written for R3 applies to R4 In R3, we executed procedures.
Ch 7 B.
Module R2 CS450. Next Week R1 is due next Friday ▫Bring manuals in a binder - make sure to have a cover page with group number, module, and date. You.
Ch. 7 Process Synchronization (1/2) I Background F Producer - Consumer process :  Compiler, Assembler, Loader, · · · · · · F Bounded buffer.
Silberschatz, Galvin and Gagne ©2009 Operating System Concepts – 8 th Edition, Chapter 6: Process Synchronization.
I/O Multiplexing The role of the ‘poll()’ method in Linux device-driver operations.
Advanced Char Driver Operations Ted Baker  Andy Wang COP 5641 / CIS 4930.
Daemon Processes Long lived utility processes Often started at system boot and ended when system shuts down Run in the background with no controlling terminal.
Advanced Char Driver Operations
Advanced Character Driver Operations Ted Baker  Andy Wang COP 5641 / CIS 4930.
The ‘system-call’ ID-numbers How can Linux applications written in assembly language create and access files?
04/14/2008CSCI 315 Operating Systems Design1 I/O Systems Notice: The slides for this lecture have been largely based on those accompanying the textbook.
Character Driver Issues Implementing ‘/dev/physmem’
I/O Hardware n Incredible variety of I/O devices n Common concepts: – Port – connection point to the computer – Bus (daisy chain or shared direct access)
Inter Process Communication:  It is an essential aspect of process management. By allowing processes to communicate with each other: 1.We can synchronize.
Concurrency: Mutual Exclusion, Synchronization, Deadlock, and Starvation in Representative Operating Systems.
Home: Phones OFF Please Unix Kernel Parminder Singh Kang Home:
1 Device Management The von Neumann Architecture System Architecture Device Management Polling Interrupts DMA operating systems.
Chapter 13: I/O Systems I/O Hardware Application I/O Interface
Data Structures in the Kernel Sarah Diesburg COP 5641.
I/O Systems ◦ Operating Systems ◦ CS550. Note:  Based on Operating Systems Concepts by Silberschatz, Galvin, and Gagne  Strongly recommended to read.
CS252: Systems Programming Ninghui Li Final Exam Review.
Operating System Program 5 I/O System DMA Device Driver.
CS 6560 Operating System Design Lecture 13 Finish File Systems Block I/O Layer.
Concurrency and Race Conditions Sarah Diesburg COP 5641.
Real-time Systems Lab, Computer Science and Engineering, ASU Linux Input Systems (ESP – Fall 2014) Computer Science & Engineering Department Arizona State.
Nachos Phase 1 Code -Hints and Comments
I/O Systems I/O Hardware Application I/O Interface
Concurrency and Race Conditions Linux Kernel Programming CIS 4930/COP 5641.
Lecture 3 Process Concepts. What is a Process? A process is the dynamic execution context of an executing program. Several processes may run concurrently,
UNIX Files File organization and a few primitives.
OS2014 PROJECT 2 Supplemental Information. Outline Sequence Diagram of Project 2 Kernel Modules Kernel Sockets Work Queues Synchronization.
CS333 Intro to Operating Systems Jonathan Walpole.
Sogang University Advanced Operating Systems (Enhanced Device Driver Operations) Advanced Operating Systems (Enhanced Device Driver Operations) Sang Gue.
Thread Implementations; MUTEX Reference on thread implementation –text: Tanenbaum ch. 2.2 Reference on mutual exclusion (MUTEX) –text: Tanenbaum ch
Interfacing Device Drivers with the Kernel
Processes, Threads, and Process States. Programs and Processes  Program: an executable file (before/after compilation)  Process: an instance of a program.
© Janice Regan, CMPT 300, May CMPT 300 Introduction to Operating Systems Operating Systems Processes and Threads.
4P13 Week 12 Talking Points Device Drivers 1.Auto-configuration and initialization routines 2.Routines for servicing I/O requests (the top half)
COMP 3438 – Part I - Lecture 5 Character Device Drivers
Silberschatz, Galvin and Gagne ©2011 Operating System Concepts Essentials – 8 th Edition Chapter 2: The Linux System Part 2.
Silberschatz, Galvin, and Gagne  Applied Operating System Concepts Module 12: I/O Systems I/O hardwared Application I/O Interface Kernel I/O.
I/O Software CS 537 – Introduction to Operating Systems.
Mutual Exclusion -- Addendum. Mutual Exclusion in Critical Sections.
Real Numbers Device driver process within the operating system that interacts with I/O controller logical record 1 logical record 2 logical record 3.
Andrew Hanushevsky: Basic I/O API’s
Chapter 13: I/O Systems.
Module 12: I/O Systems I/O hardware Application I/O Interface
Chapter 13: I/O Systems Modified by Dr. Neerja Mhaskar for CS 3SH3.
Background on the need for Synchronization
Concurrency and Race Conditions
Linux Kernel Driver.
CSCI 315 Operating Systems Design
Chapter 2: The Linux System Part 2
I/O Systems I/O Hardware Application I/O Interface
Operating Systems Chapter 5: Input/Output Management
Operating System Concepts
13: I/O Systems I/O hardwared Application I/O Interface
CS703 - Advanced Operating Systems
CSE 451: Operating Systems Spring 2012 Module 6 Review of Processes, Kernel Threads, User-Level Threads Ed Lazowska 570 Allen.
CSE 451 Autumn 2003 Section 3 October 16.
Jonathan Walpole Computer Science Portland State University
Chapter 13: I/O Systems I/O Hardware Application I/O Interface
CSE 153 Design of Operating Systems Winter 19
CSE 451: Operating Systems Autumn 2004 Module 4 Processes
Low-Level I/O – the POSIX Layer CSE 333 Winter 2019
Module 12: I/O Systems I/O hardwared Application I/O Interface
Chapter 13: I/O Systems “The two main jobs of a computer are I/O and [CPU] processing. In many cases, the main job is I/O, and the [CPU] processing is.
Presentation transcript:

Advanced Char Driver Operations Linux Kernel Programming CIS 4930/COP 5641

Topics Managing ioctl command numbers Putting a thread to sleep Seeking on a device Access control

ioctl input/output control system call For operations beyond simple data transfers  Eject the media  Report error information  Change hardware settings  Self destruct Alternatives  Embedded commands in the data stream  Driver-specific file systems

ioctl User-level interface (application view) int ioctl(int fd, int request,...); ... Does not indicate variable number of arguments  Would be problematic for the system call interface In this context, it is meant to pass a single optional argument  Traditionally a char *argp  Just a way to bypass the type checking  For more information, look at man page

ioctl Driver-level interface int (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg);  cmd is passed from the user unchanged  arg can be an integer or a pointer  Compiler does not type check ioctl() has changed from the LDD3 era  Modified to remove the big kernel lock (BKL) 

Choosing the ioctl Commands Desire a numbering scheme to avoid mistakes  E.g., issuing a command to the wrong device (changing the baud rate of an audio device)  Unique ioctl command numbers across system  Check ioctl.h files in the source and directory Documentation/ioctl/

Choosing the ioctl Commands A command number uses four bitfields  Defined in include/uapi/asm-generic/ioctl.h (for most architectures)  direction : direction of data transfer  _IOC_NONE  _IOC_READ  _IOC_WRITE  _IOC_READ | WRITE

Choosing the ioctl Commands  type ( ioctl device type)  8-bit ( _IOC_TYPEBITS ) magic number  Associated with the device number  8-bit ( _IOC_NRBITS ) sequential number  Unique within device

Choosing the ioctl Commands  size : size of user data involved  _IOC_SIZEBITS  Usually 14 bits but could be overridden by architecture #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int) /* provoke compile error for invalid uses of size argument */ extern unsigned int __invalid_size_argument_for_IOC; #define _IOC_TYPECHECK(t) \ ((sizeof(t) == sizeof(t[1]) && \ sizeof(t) < (1 << _IOC_SIZEBITS)) ? \ sizeof(t) : __invalid_size_argument_for_IOC) /* See */

Choosing the ioctl Commands Useful macros to create ioctl command numbers  _IO(type, nr)  _IOR(type, nr, datatype)  _IOW(type, nr, datatype)  _IOWR(type, nr, datatype) _IO*_BAD used for backward compatibility  Uses number (of bytes) rather than datatype  arg is unsigned long (integer) arg is a pointer

Choosing the ioctl Commands Useful macros to decode ioctl command numbers  _IOC_DIR(nr)  _IOC_TYPE(nr)  _IOC_NR(nr)  _IOC_SIZE(nr)

Choosing the ioctl Commands The scull example /* Use 'k' as magic number (type) field */ #define SCULL_IOC_MAGIC 'k‘ /* Please use a different 8-bit number in your code */ #define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)

Choosing the ioctl Commands The scull example /* * S means "Set" through a ptr, * T means "Tell" directly with the argument value * G means "Get": reply by setting through a pointer * Q means "Query": response is on the return value * X means "eXchange": switch G and S atomically * H means "sHift": switch T and Q atomically */ #define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int) #define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int) #define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3) #define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4) #define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int) Set new value and return the old value

Choosing the ioctl Commands The scull example #define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int) #define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7) #define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8) #define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int) #define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int) #define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11) #define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12)... #define SCULL_IOC_MAXNR 14

The Return Value When the command number is not supported  –ENOTTY (according to the POSIX standard)  Some drivers may (in conflict with the POSIX standard) return –EINVAL

The Predefined Commands Handled by the kernel first  Will not be passed down to device drivers Three groups  For any file (regular, device, FIFO, socket) Magic number: “T.”  For regular files only  Specific to the file system type E.g., see ext2_ioctl()

Using the ioctl Argument If it is an integer, just use it directly If it is a pointer  Need to check for valid user address int access_ok(int type, const void *addr, unsigned long size); type : either VERIFY_READ or VERIFY_WRITE Returns 1 for success, 0 for failure  Driver then results –EFAULT to the caller Defined in Mostly called by memory-access routines

Using the ioctl Argument The scull example int scull_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0, tmp; int retval = 0; /* check the magic number and whether the command is defined */ if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) { return -ENOTTY; } if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) { return -ENOTTY; } …

Using the ioctl Argument The scull example … /* the concept of "read" and "write" is reversed here */ if (_IOC_DIR(cmd) & _IOC_READ) { err = !access_ok(VERIFY_WRITE, (void __user *) arg, _IOC_SIZE(cmd)); } else if (_IOC_DIR(cmd) & _IOC_WRITE) { err = !access_ok(VERIFY_READ, (void __user *) arg, _IOC_SIZE(cmd)); } if (err) return -EFAULT; …

Capabilities and Restricted Operations Limit certain ioctl operations to privileged users See for the full set of capabilities To check a certain capability call int capable(int capability); In the scull example if (!capable(CAP_SYS_ADMIN)) { return –EPERM; } A catch-all capability for many system administration operations

The Implementation of the ioctl Commands A giant switch statement … switch(cmd) { case SCULL_IOCRESET: scull_quantum = SCULL_QUANTUM; scull_qset = SCULL_QSET; break; case SCULL_IOCSQUANTUM: /* Set: arg points to the value */ if (!capable(CAP_SYS_ADMIN)) { return -EPERM; } retval = __get_user(scull_quantum, (int __user *)arg); break; …

The Implementation of the ioctl Commands Six ways to pass and receive arguments from the user space  Need to know command number int quantum; ioctl(fd,SCULL_IOCSQUANTUM, &quantum); /* Set by pointer */ ioctl(fd,SCULL_IOCTQUANTUM, quantum); /* Set by value */ ioctl(fd,SCULL_IOCGQUANTUM, &quantum); /* Get by pointer */ quantum = ioctl(fd,SCULL_IOCQQUANTUM); /* Get by return value */ ioctl(fd,SCULL_IOCXQUANTUM, &quantum); /* Exchange by pointer */ quantum = ioctl(fd,SCULL_IOCHQUANTUM, quantum); /* Exchange by value */

Pros/Cons of ioctl Cons  Unregulated means to add new system call API  Not reviewed  Different for each device  32/64-bit compatibility  No way to enumerate Pros  read and write with one call Ref 

Device Control Without ioctl Writing control sequences into the data stream itself  Example: console escape sequences  Advantages: No need to implement ioctl methods  Disadvantages: Need to make sure that escape sequences do not appear in the normal data stream (e.g., cat a binary file) Need to parse the data stream

Device Control Without ioctl sysfs  Can be used to enumerate all exported components  Use standard unix shell commands Netlink  Getting/setting socket options debugfs  Probably not a good choice since its purpose is for debugging relay interface 

SLEEPING

Sleeping Suspend thread waiting for some condition Example usage: Blocking I/O  Data is not immediately available for reads  When the device is not ready to accept data Output buffer is full

Introduction to Sleeping A process is removed from the scheduler’s run queue Certain rules  Generally never sleep when running in an atomic context Multiple steps must be performed without concurrent accesses Not while holding a spinlock, seqlock, or RCU lock Not while disabling interrupts

Introduction to Sleeping After waking up  Make no assumptions about the state of the system  The resource one is waiting for might be gone again  Must check the wait condition again

Introduction to Sleeping Wait queue: contains a list of processes waiting for a specific event  #include  To initialize statically, call DECLARE_WAIT_QUEUE_HEAD(my_queue);  To initialize dynamically, call wait_queue_head_t my_queue; init_waitqueue_head(&my_queue);

Simple Sleeping Call variants of wait_event macros  wait_event(queue, condition) queue = wait queue head  Passed by value Waits until the boolean condition becomes true Puts into an uninterruptible sleep  Usually is not what you want  wait_event_interruptible(queue, condition) Can be interrupted by signals Returns nonzero if sleep was interrupted  Your driver should return -ERESTARTSYS

Simple Sleeping  wait_event_timeout(queue, condition, timeout) Wait for a limited time (in jiffies) Returns 0 regardless of condition evaluations  wait_event_interruptible_timeout(queue, condition, timeout)

Simple Sleeping To wake up, call variants of wake_up functions void wake_up(wait_queue_head_t *queue); Wakes up all processes waiting on the queue void wake_up_interruptible(wait_queue_head_t *queue); Wakes up processes that perform an interruptible sleep

Simple Sleeping Example module: sleepy static DECLARE_WAIT_QUEUE_HEAD(wq); static int flag = 0; ssize_t sleepy_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) { printk(KERN_DEBUG "process %i (%s) going to sleep\n", current->pid, current->comm); wait_event_interruptible(wq, flag != 0); flag = 0; printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm); return 0; /* EOF */ } Multiple threads can wake up at this point

Simple Sleeping Example module: sleepy ssize_t sleepy_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos) { printk(KERN_DEBUG "process %i (%s) awakening the readers...\n", current->pid, current->comm); flag = 1; wake_up_interruptible(&wq); return count; /* succeed, to avoid retrial */ }

Blocking and Nonblocking Operations By default, operations block  If no data is available for reads  If no space is available for writes Non-blocking I/O is indicated by the O_NONBLOCK flag in filp->f_flags  Defined in  Only open, read, and write calls are affected  Returns –EAGAIN immediately instead of block  Applications need to distinguish non-blocking returns vs. EOF s

A Blocking I/O Example scullpipe  A read process Blocks when no data is available Wakes a blocking write when buffer space becomes available  A write process Blocks when no buffer space is available Wakes a blocking read process when data arrives

A Blocking I/O Example scullpipe data structure struct scull_pipe { wait_queue_head_t inq, outq; /* read and write queues */ char *buffer, *end; /* begin of buf, end of buf */ int buffersize; /* used in pointer arithmetic */ char *rp, *wp; /* where to read, where to write */ int nreaders, nwriters; /* number of openings for r/w */ struct fasync_struct *async_queue; /* asynchronous readers */ struct mutex mutex; /* mutual exclusion */ struct cdev cdev; /* Char device structure */ };

A Blocking I/O Example static ssize_t scull_p_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { struct scull_pipe *dev = filp->private_data; if (mutex_lock_interruptible(&dev->mutex)) return -ERESTARTSYS; while (dev->rp == dev->wp) { /* nothing to read */ mutex_unlock(&dev->mutex); /* release the lock */ if (filp->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp))) return -ERESTARTSYS; if (mutex_lock_interruptible(&dev->mutex)) return -ERESTARTSYS; }

A Blocking I/O Example if (dev->wp > dev->rp) count = min(count, (size_t)(dev->wp - dev->rp)); else /* the write pointer has wrapped */ count = min(count, (size_t)(dev->end - dev->rp)); if (copy_to_user(buf, dev->rp, count)) { mutex_lock(&dev->mutex); return -EFAULT; } dev->rp += count; if (dev->rp == dev->end) dev->rp = dev->buffer; /* wrapped */ mutex_unlock(&dev->mutex); /* finally, awake any writers and return */ wake_up_interruptible(&dev->outq); return count; }

LLSEEK()

The llseek Implementation Implements lseek and llseek system calls  Modifies filp->f_pos loff_t scull_llseek(struct file *filp, loff_t off, int whence) { struct scull_dev *dev = filp->private_data; loff_t newpos; switch(whence) { case 0: /* SEEK_SET */ newpos = off; break; case 1: /* SEEK_CUR, relative to the current position */ newpos = filp->f_pos + off; break;

The llseek Implementation case 2: /* SEEK_END, relative to the end of the file */ newpos = dev->size + off; break; default: /* can't happen */ return -EINVAL; } if (newpos < 0) return -EINVAL; filp->f_pos = newpos; return newpos; }

The llseek Implementation May not make sense for serial ports and keyboard inputs  Need to inform the kernel via calling nonseekable_open in the open method int nonseekable_open(struct inode *inode, struct file *filp);  Replace llseek method with no_llseek (defined in in your file_operations structure