demo codes:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/seq_file.h>
#include <linux/sched/signal.h>
#include <linux/proc_fs.h>
#include <linux/pid.h>
#include <linux/pci.h>
#include <linux/usb.h>
#include <linux/kobject.h>
#include <linux/sched/mm.h>
#include <linux/platform_device.h>
MODULE_AUTHOR("zlcao");
MODULE_LICENSE("GPL");
int seqfile_debug_mode = 0;
EXPORT_SYMBOL(seqfile_debug_mode);
module_param(seqfile_debug_mode, int, 0664);
int pid_number = -1;
EXPORT_SYMBOL(pid_number);
module_param(pid_number, int, 0664);
static void kill_processes(int pid_nr);
// 开始输出任务列表
// my_seq_ops_start()的返回值,会传递给my_seq_ops_next()的v参数
static void *my_seq_ops_start(struct seq_file *m, loff_t *pos)
{
loff_t index = *pos;
struct task_struct *task;
printk("%s line %d, index %lld.count %ld, size %ld here.\n", __func__, __LINE__, index, m->count, m->size);
if(seqfile_debug_mode == 0) {
// 如果缓冲区不足, seq_file可能会重新调用start()函数,
// 并且传入的pos是之前已经遍历到的位置,
// 这里需要根据pos重新计算开始的位置
for_each_process(task) {
if (index-- == 0) {
return task;
}
}
} else {
return NULL + (*pos == 0);
}
return NULL;
}
// 继续遍历, 直到my_seq_ops_next()放回NULL或者错误
static void *my_seq_ops_next(struct seq_file *m, void *v, loff_t *pos)
{
struct task_struct *task = NULL;
if(seqfile_debug_mode == 0) {
task = next_task((struct task_struct *)v);
// 这里加不加好像都没有作用
++ *pos;
// 返回NULL, 遍历结束
if(task == &init_task) {
return NULL;
}
} else {
++ *pos;
}
return task;
}
// 遍历完成/出错时seq_file会调用stop()函数
static void my_seq_ops_stop(struct seq_file *m, void *v)
{
}
static int lookup_pci_devices(struct device *dev, void *data)
{
struct seq_file *m = (struct seq_file *)data;
struct pci_dev *pdev = to_pci_dev(dev);
seq_printf(m, "vendor id 0x%x, device id 0x%x, devname %s.\n", pdev->vendor, pdev->device, dev_name(&pdev->dev));
return 0;
}
static int lookup_pci_drivers(struct device_driver *drv, void *data)
{
struct seq_file *m = (struct seq_file *)data;
seq_printf(m, "driver name %s.\n", drv->name);
return 0;
}
static int lookup_platform_devices(struct device *dev, void *data)
{
struct seq_file *m = (struct seq_file *)data;
struct platform_device *platdev = to_platform_device(dev);
seq_printf(m, "devpath %s.\n", platdev->name);
return 0;
}
static int lookup_platform_drivers(struct device_driver *drv, void *data)
{
struct seq_file *m = (struct seq_file *)data;
seq_printf(m, "driver name %s.\n", drv->name);
return 0;
}
static int list_device_belongs_todriver_pci(struct device *dev, void *p)
{
struct seq_file *m = (struct seq_file *)p;
struct pci_dev *pdev = to_pci_dev(dev);
seq_printf(m, "vendor id 0x%x, device id 0x%x, devname %s.\n", pdev->vendor, pdev->device, dev_name(&pdev->dev));
return 0;
}
static int list_device_belongs_todriver_platform(struct device *dev, void *p)
{
struct seq_file *m = (struct seq_file *)p;
struct platform_device *platdev = to_platform_device(dev);
seq_printf(m, "platdevname %s.\n", platdev->name);
return 0;
}
static int pcie_device_info(struct pci_dev *pdev, void *data)
{
struct seq_file *m = (struct seq_file *)data;
seq_printf(m, "vendor id 0x%04x, device id 0x%04x, devname %s, belongs to bus %16s, parent bus name %6s subordinate 0x%p.\n", \
pdev->vendor, pdev->device, dev_name(&pdev->dev), pdev->bus->name, pdev->bus->parent? pdev->bus->parent->name : "null", pdev->subordinate);
if(pdev->subordinate) {
seq_printf(m, " subordinate have bus name %s.\n", pdev->subordinate->name);
if(pdev->subordinate->self) {
seq_printf(m, " subordinate have dev name %s.\n", dev_name(&pdev->subordinate->self->dev));
if(pdev->subordinate->self != pdev) {
seq_printf(m, " cant happend!\n");
} else {
seq_printf(m, " surely!\n");
}
}
} else {
seq_printf(m, " subordinate not have.\n");
}
if(pdev->bus->self) {
seq_printf(m, " device belongs to child pci bus %s.\n", dev_name(&pdev->bus->self->dev));
} else {
seq_printf(m, " device belongs to top lvl pci bus.\n");
}
seq_printf(m, "\n");
return 0;
}
static ssize_t zilong_attr_show(struct kobject *kobj, struct attribute *attr,char *buf)
{
return 0;
}
static ssize_t zilong_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
return 0;
}
static const struct sysfs_ops zilong_sysfs_ops = {
.show = zilong_attr_show,
.store = zilong_attr_store,
};
static struct kobj_type zilong_ktype = {
.release = NULL,
.sysfs_ops = &zilong_sysfs_ops,
.namespace = NULL,
.get_ownership = NULL,
};
// 此函数将数据写入`seq_file`内部的缓冲区
// `seq_file`会在合适的时候把缓冲区的数据拷贝到应用层
// 参数@V是start/next函数的返回值
static int my_seq_ops_show(struct seq_file *m, void *v)
{
struct task_struct *task = NULL;
struct task_struct *tsk = NULL;
struct task_struct *p = NULL;
struct file *file = m->private;
struct pid *session = NULL;
if(seqfile_debug_mode == 0) {
seq_puts(m, " file=");
seq_file_path(m, file, "\n");
seq_putc(m, ' ');
task = (struct task_struct *)v;
session = task_session(task);
tsk = pid_task(session, PIDTYPE_PID);
if(task->flags & PF_KTHREAD) {
seq_printf(m, "Kernel thread: PID=%u, task: %s, index=%lld, read_pos=%lld, %s.\n", task->tgid, task->comm, m->index, m->read_pos, tsk? "has session" : "no session");
} else {
seq_printf(m, "User thread: PID=%u, task: %s, index=%lld, read_pos=%lld %s.\n", task->tgid, task->comm, m->index, m->read_pos, tsk? "has session" : "no session");
}
} else if(seqfile_debug_mode == 1) {
struct task_struct *g, *p;
static int oldcount = 0;
static int entercount = 0;
char *str;
printk("%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);
seq_printf(m, "%s line %d here enter %d times.\n", __func__, __LINE__, ++ entercount);
rcu_read_lock();
for_each_process_thread(g, p) {
struct task_struct *session = pid_task(task_session(g), PIDTYPE_PID);
struct task_struct *thread = pid_task(task_session(p), PIDTYPE_PID);
struct task_struct *ggroup = pid_task(task_pgrp(g), PIDTYPE_PID);
struct task_struct *pgroup = pid_task(task_pgrp(p), PIDTYPE_PID);
struct pid * pid = task_session(g);
if(list_empty(&p->tasks)) {
str = "empty";
} else {
str = "not empty";
}
seq_printf(m, "process %s(pid %d tgid %d,cpu%d) thread %s(pid %d tgid %d,cpu%d),threadnum %d, %d. tasks->prev = %p, tasks->next = %p, p->tasks=%p, %s, process parent %s(pid %d tgid %d), thread parent%s(pid %d, tgid %d, files %p\n)",
g->comm, task_pid_nr(g), task_tgid_nr(g), task_cpu(g), \
p->comm, task_pid_nr(p), task_tgid_nr(p), task_cpu(p), \
get_nr_threads(g), get_nr_threads(p), p->tasks.prev, p->tasks.next, &p->tasks, str, g->real_parent->comm, \
task_pid_nr(g->real_parent),task_tgid_nr(g->real_parent), p->real_parent->comm, task_pid_nr(p->real_parent), task_tgid_nr(p->real_parent), p->files);
if(ggroup) {
seq_printf(m, "ggroup(pid %d tgid %d).", task_pid_nr(ggroup),task_tgid_nr(ggroup));
}
if(pgroup) {
seq_printf(m, "pgroup(pid %d tgid %d).", task_pid_nr(pgroup),task_tgid_nr(pgroup));
}
seq_printf(m, "current smp processor id %d.", smp_processor_id());
if(thread) {
seq_printf(m, "thread session %s(%d).", thread->comm, task_pid_nr(thread));
}
if(session) {
seq_printf(m, "process session %s(%d).", session->comm, task_pid_nr(session));
}
if(oldcount == 0 || oldcount != m->size) {
printk("%s line %d, m->count %ld, m->size %ld.", __func__, __LINE__, m->count, m->size);
oldcount = m->size;
}
if(pid){
seq_printf(m, "pid task %p,pgid task %p, psid_task %p", pid_task(pid, PIDTYPE_PID), pid_task(pid, PIDTYPE_PGID), pid_task(pid, PIDTYPE_SID));
seq_printf(m, "pid task %s,pgid task %s, psid_task %s", pid_task(pid, PIDTYPE_PID)->comm, pid_task(pid, PIDTYPE_PGID)->comm, pid_task(pid, PIDTYPE_SID)->comm);
}
seq_printf(m, "\n");
}
rcu_read_unlock();
} else if(seqfile_debug_mode == 2) {
for_each_process(task) {
struct pid *pgrp = task_pgrp(task);
seq_printf(m, "Group Header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(pgrp, PIDTYPE_PGID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d.\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p));
} while_each_pid_task(pgrp, PIDTYPE_PGID, p);
}
} else if (seqfile_debug_mode == 3) {
for_each_process(task) {
struct pid *session = task_session(task);
struct task_struct *tsk = pid_task(session, PIDTYPE_PID);
if(tsk) {
seq_printf(m, "session task %s(%d,cpu%d):", tsk->comm, task_pid_nr(tsk), task_cpu(tsk));
} else {
seq_printf(m, "process %s(%d,cpu%d) has no session task.", task->comm, task_pid_nr(task), task_cpu(task));
}
seq_printf(m, "session header %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
do_each_pid_task(session, PIDTYPE_SID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d, spidtask %s(%d,%d).\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p), pid_task(session, PIDTYPE_SID)->comm, pid_task(session, PIDTYPE_SID)->tgid, pid_task(session, PIDTYPE_SID)->pid);
if(pid_task(session, PIDTYPE_PID)) {
seq_printf(m, "pidtask %s(%d,%d).\n", pid_task(session, PIDTYPE_PID)->comm, pid_task(session, PIDTYPE_PID)->tgid, pid_task(session, PIDTYPE_PID)->pid);
}
} while_each_pid_task(pgrp, PIDTYPE_SID, p);
}
} else if(seqfile_debug_mode == 4) {
struct task_struct *thread, *child;
for_each_process(task) {
seq_printf(m, "process %s(%d,cpu%d):\n", task->comm, task_pid_nr(task), task_cpu(task));
for_each_thread(task, thread) {
list_for_each_entry(child, &thread->children, sibling) {
seq_printf(m, " thread %s(%d,cpu%d) child %s(%d,cpu%d),threadnum %d, %d.\n",
thread->comm, task_pid_nr(thread), task_cpu(thread), \
child->comm, task_pid_nr(child), task_cpu(child), \
get_nr_threads(thread), get_nr_threads(child));
}
}
}
} else if(seqfile_debug_mode == 5) {
struct task_struct *g, *t;
do_each_thread (g, t) {
seq_printf(m, "Process %s(%d cpu%d), thread %s(%d cpu%d), threadnum %d.\n", g->comm, task_pid_nr(g), task_cpu(g), t->comm, task_pid_nr(t), task_cpu(t), get_nr_threads(g));
} while_each_thread (g, t);
} else if(seqfile_debug_mode == 6) {
for_each_process(task) {
struct pid *pid = task_pid(task);
seq_printf(m, "Process %s(%d,cpu%d) pid %d, tgid %d:\n", task->comm, task_pid_nr(task), task_cpu(task), task_pid_vnr(task), task_tgid_vnr(task));
do_each_pid_task(pid, PIDTYPE_TGID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. pid %d, tgid %d\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p), task_pid_vnr(p), task_tgid_vnr(p));
} while_each_pid_task(pid, PIDTYPE_TGID, p);
}
} else if(seqfile_debug_mode == 7) {
for_each_process(task) {
struct pid *pid = task_pid(task);
seq_printf(m, "Process %s(%d,cpu%d) pid %d, tgid %d:\n", task->comm, task_pid_nr(task), task_cpu(task), task_pid_vnr(task), task_tgid_vnr(task));
do_each_pid_task(pid, PIDTYPE_PID, p) {
seq_printf(m, " process %s(%d,cpu%d) thread %s(%d,cpu%d),threadnum %d, %d. pid %d, tgid %d\n",
task->comm, task_pid_nr(task), task_cpu(task), \
p->comm, task_pid_nr(p), task_cpu(p), \
get_nr_threads(task), get_nr_threads(p), task_pid_vnr(p), task_tgid_vnr(p));
} while_each_pid_task(pid, PIDTYPE_PID, p);
}
} else if(seqfile_debug_mode == 8) {
bus_for_each_dev(&pci_bus_type, NULL, (void*)m, lookup_pci_devices);
bus_for_each_drv(&pci_bus_type, NULL, (void*)m, lookup_pci_drivers);
} else if(seqfile_debug_mode == 9) {
struct device_driver *drv;
drv = driver_find("pcieport", &pci_bus_type);
driver_for_each_device(drv, NULL, (void*)m, list_device_belongs_todriver_pci);
} else if(seqfile_debug_mode == 10) {
for_each_process(task) {
seq_printf(m, "Process %s(%d),state 0x%08lx, exit_state 0x%08x, refcount %d, usage %d rcucount %d.", \
task->comm, task->tgid, task->state, task->exit_state, refcount_read(&task->stack_refcount), refcount_read(&task->usage), refcount_read(&task->rcu_users));
if(task->parent) {
seq_printf(m, "parent name %s pid %d.\n", task->parent->comm, task->parent->tgid);
} else {
seq_printf(m, "no parent.\n");
}
}
} else if(seqfile_debug_mode == 11) {
struct pci_bus *bus;
list_for_each_entry(bus, &pci_root_buses, node) {
seq_printf(m, "pcibus name %s.\n", bus->name);
pci_walk_bus(bus, pcie_device_info, (void*)m);
}
} else if(seqfile_debug_mode == 12) {
struct device_driver *drv;
// EXPORT_SYMBOL(usb_bus_type);
// bus_for_each_dev(&usb_bus_type, NULL, (void*)m, lookup_usb_devices);
// bus_for_each_drv(&usb_bus_type, NULL, (void*)m, lookup_usb_drivers);
bus_for_each_dev(&platform_bus_type, NULL, (void*)m, lookup_platform_devices);
bus_for_each_drv(&platform_bus_type, NULL, (void*)m, lookup_platform_drivers);
drv = driver_find("demo_platform", &platform_bus_type);
driver_for_each_device(drv, NULL, (void*)m, list_device_belongs_todriver_platform);
} else if(seqfile_debug_mode == 13) {
static struct kset *class_zilong;
static struct kobject kobj;
int ret;
class_zilong = kset_create_and_add("zilong_class", NULL, NULL);
if (!class_zilong) {
printk("%s line %d, fatal error, create class failure.\n", __func__, __LINE__);
return -ENOMEM;
}
ret = kobject_init_and_add(&kobj, &zilong_ktype, &class_zilong->kobj, "%s-%d", "zilong", 1);
if(ret < 0) {
printk("%s line %d, fatal error, create class failure.\n", __func__, __LINE__);
return -ENOMEM;
}
} else if(seqfile_debug_mode == 14) {
// cad pid is process 1 pid.
int ret = kill_cad_pid(SIGINT, 1);
printk("%s lne %d ret %d.\n", __func__, __LINE__, ret);
} else if(seqfile_debug_mode == 15) {
kill_processes(pid_number);
} else {
printk("%s line %d,cant be here, seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);
}
return 0;
}
static struct task_struct *find_lock_task_mm(struct task_struct *p)
{
struct task_struct *t;
rcu_read_lock();
for_each_thread(p, t) {
task_lock(t);
if (likely(t->mm))
goto found;
task_unlock(t);
}
t = NULL;
found:
rcu_read_unlock();
return t;
}
static bool process_shares_task_mm(struct task_struct *p, struct mm_struct *mm)
{
struct task_struct *t;
for_each_thread(p, t) {
struct mm_struct *t_mm = READ_ONCE(t->mm);
if (t_mm)
return t_mm == mm;
}
return false;
}
static void kill_processes(int pid_nr)
{
struct task_struct *victim;
struct task_struct *p;
struct mm_struct *mm;
victim = get_pid_task(find_vpid(pid_nr), PIDTYPE_PID);
if(victim == NULL) {
printk("%s line %d,return.\n", __func__, __LINE__);
return;
}
printk("%s line %d, task has live %d threads total.\n", __func__, __LINE__, atomic_read(&victim->signal->live));
p = find_lock_task_mm(victim);
if (!p) {
put_task_struct(victim);
return;
} else {
get_task_struct(p);
put_task_struct(victim);
victim = p;
}
mm = victim->mm;
mmgrab(mm);
kill_pid(find_vpid(pid_nr), SIGKILL, 1);
task_unlock(victim);
rcu_read_lock();
for_each_process(p) {
if (!process_shares_task_mm(p, mm))
continue;
if (same_thread_group(p, victim))
continue;
if (unlikely(p->flags & PF_KTHREAD))
continue;
kill_pid(get_pid(task_pid(p)), SIGKILL, 1);
}
rcu_read_unlock();
mmdrop(mm);
while(atomic_read(&victim->signal->live)) {
printk("%s line %d, live %d.\n", __func__, __LINE__, atomic_read(&victim->signal->live));
}
put_task_struct(victim);
}
static const struct seq_operations my_seq_ops = {
.start = my_seq_ops_start,
.next = my_seq_ops_next,
.stop = my_seq_ops_stop,
.show = my_seq_ops_show,
};
static int proc_seq_open(struct inode *inode, struct file *file)
{
int ret;
struct seq_file *m;
ret = seq_open(file, &my_seq_ops);
if(!ret) {
m = file->private_data;
m->private = file;
}
return ret;
}
static ssize_t proc_seq_write(struct file *file, const char __user *buffer, size_t count, loff_t *pos)
{
char debug_string[16];
int debug_no;
memset(debug_string, 0x00, sizeof(debug_string));
if (count >= sizeof(debug_string)) {
printk("%s line %d, fata error, write count exceed max buffer size.\n", __func__, __LINE__);
return -EINVAL;
}
if (copy_from_user(debug_string, buffer, count)) {
printk("%s line %d, fata error, copy from user failure.\n", __func__, __LINE__);
return -EFAULT;
}
if (sscanf(debug_string, "%d", &debug_no) <= 0) {
printk("%s line %d, fata error, read debugno failure.\n", __func__, __LINE__);
return -EFAULT;
}
seqfile_debug_mode = debug_no;
//printk("%s line %d, debug_no %d.\n", __func__, __LINE__, debug_no);
return count;
}
static ssize_t proc_seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
ssize_t ret;
printk("%s line %d enter, fuck size %lld size %ld.\n"