Chapter 9-11: Advanced Driver Types & Debugging
This file contains the remaining topics: Block Drivers, Network Drivers, and Debugging.
Chapter 9: Block Device Drivers
Overview
Block devices handle data in fixed-size blocks (typically 512, 4096 bytes). Examples: Hard drives, SSDs, RAM disks.
Basic Block Driver Structure
#include <linux/blkdev.h>
#include <linux/blk-mq.h>
struct my_block_dev {
struct gendisk *disk;
struct request_queue *queue;
struct blk_mq_tag_set tag_set;
u8 *data; /* RAM disk data */
size_t size;
};
/* Request handling */
static blk_status_t my_block_request(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
struct request *req = bd->rq;
struct my_block_dev *dev = req->q->queuedata;
struct bio_vec bvec;
struct req_iterator iter;
loff_t pos = blk_rq_pos(req) << 9; /* Convert sector to bytes */
blk_mq_start_request(req);
if (blk_rq_is_passthrough(req)) {
blk_mq_end_request(req, BLK_STS_IOERR);
return BLK_STS_OK;
}
rq_for_each_segment(bvec, req, iter) {
void *buf = kmap_atomic(bvec.bv_page);
if (rq_data_dir(req) == WRITE)
memcpy(dev->data + pos, buf + bvec.bv_offset, bvec.bv_len);
else
memcpy(buf + bvec.bv_offset, dev->data + pos, bvec.bv_len);
kunmap_atomic(buf);
pos += bvec.bv_len;
}
blk_mq_end_request(req, BLK_STS_OK);
return BLK_STS_OK;
}
static const struct blk_mq_ops my_block_mq_ops = {
.queue_rq = my_block_request,
};
/* Initialization */
static int my_block_init(void)
{
struct my_block_dev *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->size = 16 * 1024 * 1024; /* 16 MB */
dev->data = vmalloc(dev->size);
if (!dev->data) {
ret = -ENOMEM;
goto out_free;
}
/* Setup tag set */
dev->tag_set.ops = &my_block_mq_ops;
dev->tag_set.nr_hw_queues = 1;
dev->tag_set.queue_depth = 128;
dev->tag_set.numa_node = NUMA_NO_NODE;
dev->tag_set.flags = BLK_MQ_F_SHOULD_MERGE;
ret = blk_mq_alloc_tag_set(&dev->tag_set);
if (ret)
goto out_vfree;
/* Create disk */
dev->disk = blk_mq_alloc_disk(&dev->tag_set, dev);
if (IS_ERR(dev->disk)) {
ret = PTR_ERR(dev->disk);
goto out_tag_set;
}
dev->queue = dev->disk->queue;
dev->queue->queuedata = dev;
dev->disk->major = 0; /* Dynamic */
dev->disk->first_minor = 0;
dev->disk->minors = 1;
dev->disk->fops = &my_block_fops;
dev->disk->private_data = dev;
strcpy(dev->disk->disk_name, "myblock0");
set_capacity(dev->disk, dev->size >> 9); /* Size in sectors */
ret = add_disk(dev->disk);
if (ret)
goto out_cleanup_disk;
pr_info("Block device registered: %s\n", dev->disk->disk_name);
return 0;
out_cleanup_disk:
put_disk(dev->disk);
out_tag_set:
blk_mq_free_tag_set(&dev->tag_set);
out_vfree:
vfree(dev->data);
out_free:
kfree(dev);
return ret;
}
Chapter 10: Network Device Drivers
Overview
Network drivers handle packet transmission and reception. Use NAPI (New API) for efficient interrupt handling.
Basic Network Driver Structure
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
struct my_net_priv {
struct net_device *ndev;
struct napi_struct napi;
void __iomem *regs;
struct sk_buff *tx_skb;
spinlock_t lock;
};
/* Transmit packet */
static netdev_tx_t my_net_start_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
struct my_net_priv *priv = netdev_priv(ndev);
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
/* Check if device is ready */
if (priv->tx_skb) {
netif_stop_queue(ndev);
spin_unlock_irqrestore(&priv->lock, flags);
return NETDEV_TX_BUSY;
}
priv->tx_skb = skb;
/* Write packet to hardware */
write_packet_to_hw(priv, skb->data, skb->len);
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += skb->len;
spin_unlock_irqrestore(&priv->lock, flags);
return NETDEV_TX_OK;
}
/* NAPI poll function */
static int my_net_poll(struct napi_struct *napi, int budget)
{
struct my_net_priv *priv = container_of(napi, struct my_net_priv, napi);
struct net_device *ndev = priv->ndev;
int work_done = 0;
while (work_done < budget) {
struct sk_buff *skb;
int len;
/* Check if packet available */
if (!packet_available(priv))
break;
/* Allocate skb */
skb = netdev_alloc_skb(ndev, MAX_PACKET_SIZE);
if (!skb)
break;
/* Read packet from hardware */
len = read_packet_from_hw(priv, skb->data, MAX_PACKET_SIZE);
skb_put(skb, len);
/* Set protocol */
skb->protocol = eth_type_trans(skb, ndev);
/* Pass to network stack */
netif_receive_skb(skb);
ndev->stats.rx_packets++;
ndev->stats.rx_bytes += len;
work_done++;
}
if (work_done < budget) {
napi_complete_done(napi, work_done);
/* Re-enable interrupts */
enable_rx_interrupt(priv);
}
return work_done;
}
/* Interrupt handler */
static irqreturn_t my_net_interrupt(int irq, void *dev_id)
{
struct net_device *ndev = dev_id;
struct my_net_priv *priv = netdev_priv(ndev);
u32 status;
status = readl(priv->regs + REG_INT_STATUS);
if (status & INT_RX) {
/* Disable RX interrupt */
disable_rx_interrupt(priv);
/* Schedule NAPI */
napi_schedule(&priv->napi);
}
if (status & INT_TX) {
/* TX complete */
if (priv->tx_skb) {
dev_kfree_skb_irq(priv->tx_skb);
priv->tx_skb = NULL;
netif_wake_queue(ndev);
}
}
return IRQ_HANDLED;
}
static const struct net_device_ops my_net_netdev_ops = {
.ndo_open = my_net_open,
.ndo_stop = my_net_stop,
.ndo_start_xmit = my_net_start_xmit,
.ndo_set_mac_address = eth_mac_addr,
.ndo_validate_addr = eth_validate_addr,
};
/* Driver initialization */
static int my_net_probe(struct platform_device *pdev)
{
struct net_device *ndev;
struct my_net_priv *priv;
int ret;
/* Allocate net device */
ndev = alloc_etherdev(sizeof(struct my_net_priv));
if (!ndev)
return -ENOMEM;
priv = netdev_priv(ndev);
priv->ndev = ndev;
SET_NETDEV_DEV(ndev, &pdev->dev);
platform_set_drvdata(pdev, ndev);
/* Setup device */
ndev->netdev_ops = &my_net_netdev_ops;
ndev->ethtool_ops = &my_net_ethtool_ops;
/* Initialize NAPI */
netif_napi_add(ndev, &priv->napi, my_net_poll, 64);
/* Get resources */
priv->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->regs)) {
ret = PTR_ERR(priv->regs);
goto err_free;
}
/* Request IRQ */
ret = devm_request_irq(&pdev->dev, platform_get_irq(pdev, 0),
my_net_interrupt, 0, dev_name(&pdev->dev), ndev);
if (ret)
goto err_free;
/* Register network device */
ret = register_netdev(ndev);
if (ret)
goto err_free;
dev_info(&pdev->dev, "Network device registered\n");
return 0;
err_free:
free_netdev(ndev);
return ret;
}
Chapter 11: Debugging Techniques
printk and Dynamic Debug
/* Logging levels */
pr_emerg("System unusable\n");
pr_alert("Action required\n");
pr_crit("Critical condition\n");
pr_err("Error condition\n");
pr_warn("Warning\n");
pr_notice("Normal but significant\n");
pr_info("Informational\n");
pr_debug("Debug message\n"); /* Requires DEBUG or dynamic debug */
/* Device-specific logging */
dev_err(&pdev->dev, "Device error\n");
dev_info(&pdev->dev, "Device info\n");
dev_dbg(&pdev->dev, "Device debug\n"); /* Dynamic debug */
/* Dynamic debug control */
// Enable: echo 'file my_driver.c +p' > /sys/kernel/debug/dynamic_debug/control
// Disable: echo 'file my_driver.c -p' > /sys/kernel/debug/dynamic_debug/control
Debugging Tools
1. /proc and /sys Debugging
/* Create debugfs entries */
#include <linux/debugfs.h>
static struct dentry *debug_dir;
static u32 debug_value;
static int create_debug_entries(void)
{
debug_dir = debugfs_create_dir("my_driver", NULL);
if (!debug_dir)
return -ENOMEM;
debugfs_create_u32("value", 0644, debug_dir, &debug_value);
debugfs_create_file("stats", 0444, debug_dir, NULL, &stats_fops);
return 0;
}
static void remove_debug_entries(void)
{
debugfs_remove_recursive(debug_dir);
}
2. ftrace
# Enable function tracer
echo function > /sys/kernel/debug/tracing/current_tracer
# Trace specific function
echo my_driver_function > /sys/kernel/debug/tracing/set_ftrace_filter
# View trace
cat /sys/kernel/debug/tracing/trace
# Trace events
echo 1 > /sys/kernel/debug/tracing/events/my_driver/enable
3. Memory Debugging
/* KASAN (Kernel Address Sanitizer) - Enable in config */
CONFIG_KASAN=y
/* KMEMLEAK - Detect memory leaks */
CONFIG_DEBUG_KMEMLEAK=y
# Check for leaks
echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak
/* SLUB debugging */
CONFIG_SLUB_DEBUG=y
# Enable SLUB debugging
slub_debug=FZP /* Kernel parameter */
4. KGDB - Kernel Debugger
# Setup (requires two machines or VM)
# On target kernel command line:
kgdbwait kgdboc=ttyS0,115200
# On host with GDB:
gdb vmlinux
(gdb) target remote /dev/ttyS0
(gdb) break my_driver_probe
(gdb) continue
5. Crash Dumps
# Enable crash dumps
CONFIG_CRASH_DUMP=y
# After crash, analyze with crash utility:
crash vmlinux vmcore
6. Lockdep - Lock Validator
/* Enable in kernel config */
CONFIG_PROVE_LOCKING=y
CONFIG_DEBUG_LOCK_ALLOC=y
/* Lockdep will detect:
* - Circular lock dependencies (potential deadlocks)
* - Lock inversion
* - Inconsistent lock state
*/
7. Common Debugging Patterns
/* Tracepoints */
#define CREATE_TRACE_POINTS
#include "my_driver_trace.h"
TRACE_EVENT(my_driver_event,
TP_PROTO(int value),
TP_ARGS(value),
TP_STRUCT__entry(__field(int, value)),
TP_fast_assign(__entry->value = value;),
TP_printk("value=%d", __entry->value)
);
/* Use in code */
trace_my_driver_event(42);
/* BUG_ON and WARN_ON */
BUG_ON(ptr == NULL); /* Panic kernel (use sparingly!) */
WARN_ON(condition); /* Warning (preferred) */
WARN_ON_ONCE(condition); /* Warn only once */
/* Assertions */
#ifdef DEBUG
#define assert(expr) \
if (!(expr)) { \
pr_err("Assertion failed: %s\n", #expr); \
dump_stack(); \
}
#else
#define assert(expr)
#endif
/* Stack trace */
#include <linux/stacktrace.h>
dump_stack(); /* Print stack trace */
Summary
This completes the advanced topics:
✅ Block Drivers: Multi-queue block layer
✅ Network Drivers: NAPI, packet handling
✅ Debugging: printk, ftrace, KASAN, KGDB, lockdep
Essential Debugging Commands
# View kernel log
dmesg
journalctl -k
# Module info
lsmod
modinfo module_name
# Hardware info
lspci
lsusb
# Trace function calls
trace-cmd record -p function_graph -g my_function
trace-cmd report
# Performance profiling
perf record -g
perf report
Final Notes
Best Practices:
- Always test in VMs or test hardware
- Use managed resources (devm_*) when possible
- Follow kernel coding style
- Handle errors properly
- Document your code
- Test thoroughly before upstream submission
Resources:
- Linux kernel documentation
- LWN.net articles
- Kernel mailing lists
- Bootlin training materials
End of advanced chapters!