内核区间树的使用Demo

Linux内核区间树(interval tree)是一种用于高效存储和查询区间的数据结构,被广泛用于许多内核子系统中,例如文件系统,内存管理,虚拟化等等,在区间树中,每个节点都表示一个区间,并存储一个键值,该键值通常是区间的左端点

具体来说,在Linux内核中,区间树的键值类型是unsigned long,用于存储区间的左端点,每个区间树节点包含两个unsigned long类型的变量,start和last,分别表示节点所代表的区间的左端点和右端点,区间树还可以存储每个节点相关的数据结构,例如用于存储文件的inode结构体,因此,通过区间树可以高效率地查询满足某个条件的区间,并找到相关的数据结构。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/fs.h>
#include <linux/namei.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>
#include <linux/netdevice.h>
#include <net/net_namespace.h>
#include <linux/sched/debug.h>
#include <linux/topology.h>
#include <linux/version.h>
#include <linux/fs_struct.h>
#include <linux/mount.h>
#include <linux/i2c.h>
#include <linux/backing-dev-defs.h>
#include <linux/dmi.h>
#include <linux/interval_tree_generic.h>
 
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 int kobj_created = 0;
static struct kset *class_zilong;
static struct kobject kobj;
static void kill_processes(int pid_nr);


//========================================================================================================================================

struct va_mapping {
	void *cookie;
	struct rb_node rb;
	uint64_t start;
	uint64_t last;
	uint64_t __subtree_last;
};
#define START(node) ((node)->start)
#define LAST(node) ((node)->last)

//bool zlcao_vm_it_augment_compute_max(struct va_mapping *node, bool exit);
//void zlcao_vm_it_augment_propagate(struct rb_node *rb, struct rb_node *stop);
//void zlcao_vm_it_augment_copy(struct rb_node *rb_old, struct rb_node *rb_new);
//void zlcao_vm_it_augment_rotate(struct rb_node *rb_old, struct rb_node *rb_new);
//void zlcao_vm_it_insert(struct va_mapping *node, struct rb_root_cached *root);
//void zlcao_vm_it_remove(struct va_mapping *node, struct rb_root_cached *root);
//struct va_mapping *zlcao_vm_it_subtree_search(struct va_mapping *node, uint64_t start, uint64_t last);
//struct va_mapping *zlcao_vm_it_iter_first(struct rb_root_cached *root, uint64_t start, uint64_t last);
//struct va_mapping *zlcao_vm_it_iter_next(struct va_mapping *node, uint64_t start, uint64_t last);
//static const struct rb_augment_callbacks zlcao_vm_it_augment = { .propagate = zlcao_vm_it_augment_propagate, .copy = zlcao_vm_it_augment_copy, .rotate = zlcao_vm_it_augment_rotate };
INTERVAL_TREE_DEFINE(struct va_mapping, rb, uint64_t, __subtree_last,START, LAST, static, zlcao_vm_it)

static struct rb_root_cached root;
void interval_tree_test(struct seq_file *m)
{
	struct va_mapping *va, *vb, *vc, *vtmp;
	struct rb_node *tmp;

	va = kmalloc_array(10, sizeof(*va), GFP_KERNEL);
	if(va == NULL) {
	    printk("%s line %d, failure.\n", __func__, __LINE__);
	    return;
	}

	va[0].start = 3;
	va[0].last = 4;
	zlcao_vm_it_insert(&va[0], &root);

	va[1].start = 5;
	va[1].last = 7;
	zlcao_vm_it_insert(&va[1], &root);

	va[2].start = 9;
	va[2].last = 11;
	zlcao_vm_it_insert(&va[2], &root);

	va[3].start = 100;
	va[3].last = 111;
	zlcao_vm_it_insert(&va[3], &root);

	va[4].start = 180;
	va[4].last = 211;
	zlcao_vm_it_insert(&va[4], &root);

	va[5].start = 280;
	va[5].last = 311;
	zlcao_vm_it_insert(&va[5], &root);

	va[6].start = 380;
	va[6].last = 411;
	zlcao_vm_it_insert(&va[6], &root);

	va[7].start = 580;
	va[7].last = 611;
	zlcao_vm_it_insert(&va[7], &root);

	va[8].start = 9;
	va[8].last = 1000;
	zlcao_vm_it_insert(&va[8], &root);

	va[9].start = 105;
	va[9].last = 190;
	zlcao_vm_it_insert(&va[9], &root);

	seq_printf(m, "interval tree test fine!\n");

	tmp = rb_first(&root.rb_root);
	if(tmp != NULL && tmp != root.rb_root.rb_node) {
		vb = container_of(tmp, struct va_mapping, rb);
		seq_printf(m, "first node start %lld, last %lld!\n", vb->start, vb->last);
	}

	while(tmp) {
		tmp = rb_next(tmp);
		if(tmp != NULL && tmp != root.rb_root.rb_node) {
		    	vb = container_of(tmp, struct va_mapping, rb);
			seq_printf(m, "node start %lld, last %lld!\n", vb->start, vb->last);
		} else if(tmp == root.rb_root.rb_node) {
			vb = container_of(tmp, struct va_mapping, rb);
			seq_printf(m, "finish node start %lld, last %lld!\n", vb->start, vb->last);
		} else {
			seq_printf(m, "tmp is null!\n");
		}
	}

	for (vc = zlcao_vm_it_iter_first(&root, 0, U64_MAX),vtmp = vc ? zlcao_vm_it_iter_next(vc, 0, U64_MAX) : NULL;
		vc;
		vc = vtmp, vtmp = vtmp ? zlcao_vm_it_iter_next(vtmp, 0, U64_MAX) : NULL) {
		seq_printf(m, "vist start %lld, last %lld!\n", vc->start, vc->last);
	}

	vb = container_of(root.rb_root.rb_node, struct va_mapping, rb);
	vc = zlcao_vm_it_subtree_search(vb, 103, 189);
	if(vc == NULL) {
		seq_printf(m, "%s line %d, vc is null.\n", __func__, __LINE__);
	} else {
		seq_printf(m, "find ok %lld, last %lld!\n", vc->start, vc->last);
	}

	vc = zlcao_vm_it_subtree_search(vb, 1, 10);
	if(vc == NULL) {
		seq_printf(m, "%s line %d, vc is null.\n", __func__, __LINE__);
	} else {
		seq_printf(m, "find ok %lld, last %lld!\n", vc->start, vc->last);
	}

	while(vc)
	{
		vc = zlcao_vm_it_iter_next(vc, 1, 10);
		if(vc == NULL) {
			seq_printf(m, "%s line %d, vc is null.\n", __func__, __LINE__);
		} else {
			seq_printf(m, "find ok %lld, last %lld!\n", vc->start, vc->last);
		}
	}

	zlcao_vm_it_remove(&va[0], &root);
	zlcao_vm_it_remove(&va[1], &root);
	zlcao_vm_it_remove(&va[2], &root);
	zlcao_vm_it_remove(&va[3], &root);
	zlcao_vm_it_remove(&va[4], &root);
	zlcao_vm_it_remove(&va[5], &root);
	zlcao_vm_it_remove(&va[6], &root);
	zlcao_vm_it_remove(&va[7], &root);
	zlcao_vm_it_remove(&va[8], &root);
	zlcao_vm_it_remove(&va[9], &root);

	memset(&root, 0x00, sizeof(root));
	kfree(va);
	return;
}

#undef START
#undef LAST

//========================================================================================================================================


// 开始输出任务列表
// 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 void pci_reset_sbr(struct pci_dev *dev)
{
    uint16_t ctrl;

    pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
    ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
    pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);

    msleep(2);

    ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
    pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
    ssleep(1);
}

static struct pci_dev *g_pci_dev = NULL;
static int lookup_pci_devices_reset(struct device *dev, void *data)
{
    struct seq_file *m = (struct seq_file *)data;
    struct pci_dev *pdev = to_pci_dev(dev);

    seq_printf(m, "%s line %d vendor id 0x%x, device id 0x%x, devname %s.\n", __func__, __LINE__, pdev->vendor, pdev->device, dev_name(&pdev->dev));

    // find the EHCI device.8086:a12f
    //if ((pdev->vendor == 0x1ee0) && (pdev->device == 0xf)) {
    if ((pdev->vendor == 0x168c) && (pdev->device == 0x36)) {
    //if ((pdev->vendor == 0x8086) && (pdev->device == 0xa12f)) {
        seq_printf(m, "%s line %d, do the reset of wireless device, domain 0x%04x, device PCI:%d:%d:%d\n.", \
            __func__, __LINE__, pci_domain_nr(pdev->bus), pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
        g_pci_dev = pdev;
    }

    return 0;
}

static unsigned int pci_rescan_bus_bridge_resize_priv(struct pci_dev *bridge)
{
    unsigned int max;
    struct pci_bus *bus = bridge->subordinate;

    max = pci_scan_child_bus(bus);

    pci_assign_unassigned_bridge_resources(bridge);

    pci_bus_add_devices(bus);

    return max;
}

static struct pci_bus *find_pci_root_bus(struct pci_bus *bus)
{
     while (bus->parent) bus = bus->parent;

     return bus;
}
 
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);
    struct pci_bus *rootbus = NULL;
 
    rootbus = find_pci_root_bus(pdev->bus);

    seq_printf(m, "vendor id 0x%x, device id 0x%x, devname %s.domain: 0x%04x, BDF: %x:%x:%x. sub 0x%p. rootbus number %d, rootbusname %s, bridgename %s. bridge %s.\n", \
            pdev->vendor, pdev->device, dev_name(&pdev->dev),pci_domain_nr(pdev->bus), pdev->bus->number, \
	    PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->subordinate, rootbus->number, rootbus->name, dev_name(pdev->bus->bridge), dev_name(pdev->bus->bridge));

    if(pdev->bus->self) {
	seq_printf(m,"bridge pdev->bus->self->dev = 0x%px, %s.\n", &pdev->bus->self->dev, dev_name(&pdev->bus->self->dev));
    }

    seq_printf(m, "sr-iov is %d %d.\n", pci_num_vf(pdev), pdev->is_physfn);
    if(dev_is_pf(&pdev->dev)) {
	seq_printf(m, "is pf.\n");
    } else {
	seq_printf(m, "is vf.\n");
    }

    if(pdev->dev.bus->iommu_ops) {
	    seq_printf(m, "%s line %d iommu ops is 0x%lx.\n", __func__, __LINE__, (unsigned long)pdev->dev.bus->iommu_ops);
    } else {
	    seq_printf(m, "no iommuops.\n");
    }
    if(pdev->dev.dma_ops) {
	    seq_printf(m, "%s line %d dma ops is 0x%lx.\n", __func__, __LINE__, (unsigned long)pdev->dev.dma_ops);
    } else {
	    seq_printf(m, "no dma ops.\n");
    }

    if(pdev->dev.iommu_group) {
	    seq_printf(m, "%s line %d iommu group id is 0x%px.\n", __func__, __LINE__, pdev->dev.iommu_group);
    } else {
	    seq_printf(m, "%s line %d no iommu group exist.\n", __func__, __LINE__);
    }

    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_find_bridge(struct pci_dev *pdev, void *data)
{
    struct seq_file *m = (struct seq_file *)data;
    struct pci_bus *rootbus = NULL;
    rootbus = pci_find_bus(0, 0);

    if(pci_is_bridge(pdev)){
        seq_printf(m, "bridge find.vendor id 0x%x, device id 0x%x, devname %s. domain:0x%04x, BDF: %x:%x:%x, subordinate 0x%p, busid %d, rootbus %d rootself 0x%p.\n", \
            pdev->vendor, pdev->device, dev_name(&pdev->dev), pci_domain_nr(pdev->bus), pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->subordinate, 
	    pdev->subordinate->number, rootbus->number, rootbus->self);
    }

    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;
}

struct zilong_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct kobject *kobj, struct zilong_attribute *attr, char *buf);
    ssize_t (*store)(struct kobject *dev, struct zilong_attribute *attr,const char *buf, size_t count);
};

#define to_zilong_attr(_attr) container_of(_attr, struct zilong_attribute, attr)
static ssize_t zilong_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
    struct zilong_attribute *zlattr = to_zilong_attr(attr);
    zlattr->show(kobj, zlattr, buf);
    
    return 0;
}

static ssize_t zilong_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
    ssize_t size;

    struct zilong_attribute *zlattr = to_zilong_attr(attr);
    size = zlattr->store(kobj, zlattr, buf, count);

    return size;
}
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,
};

ssize_t height_show(struct kobject *kobj, struct zilong_attribute *attr, char *buf)
{
    char *p = buf;

    p += sprintf(p, "I am 180 cm tall.\n");

    printk("%s line %d output %ld.\n", __func__, __LINE__, p - buf);
    return p - buf;
}

ssize_t height_store(struct kobject *dev, struct zilong_attribute *attr,const char *buf, size_t count)
{
    unsigned long height = 0;

    sscanf(buf, "%lx", &height);

    if (printk_ratelimit())
	printk("%s line %d, height %ld.\n", __func__, __LINE__, height);

    return count;
}

struct zilong_attribute height = {
    .attr = {
        .name = "height",
        .mode = 0777,
    },
    .show = height_show,
    .store = height_store,
};

static void f(struct super_block *sb, void *data)
{
	struct seq_file *m = (struct seq_file*)data;

	// for the sb->s_root dentry, d_parent is itself.
	seq_dentry(m, sb->s_root, " \t\n\\");
	seq_printf(m, "device %s.\n", sb->s_bdi->dev_name);
}

struct vfsmount *get_path_mount(char *path)
{
	struct path fspath;
	struct vfsmount *mnt;

	int err = kern_path(path, LOOKUP_FOLLOW, &fspath);
	if (err){
		printk(KERN_ERR "failed to get path.\n");
		return NULL;
	}

	mnt = fspath.mnt;

	return mnt;
}

#if 1
struct dentry *get_mount_entry(struct vfsmount *mnt, void **n, struct vfsmount **parent)
{
	void *p = NULL;
	p = mnt;
	mnt = p;
	return NULL;
}
#endif

struct dentry *get_mount_entry(struct vfsmount *mnt, void**, struct vfsmount **parent);
static void hack_mount(struct seq_file *m, struct vfsmount *mnt)
{
	struct path path;
	char buf[64];
	char *name;
	void *ptr;
	struct vfsmount *mt;

	struct dentry *dent = (struct dentry*) (*(unsigned long*)((unsigned long)mnt - sizeof(void*)));
	seq_printf(m, "mnt parent dentry name %s, %s.\n", dent->d_parent->d_name.name, dent->d_name.name);
	dent = get_mount_entry(mnt, &ptr, &mt);
	seq_printf(m, "mnt parent dentry name %s, %s, offset %ld.\n", dent->d_parent->d_name.name, dent->d_name.name, (unsigned long)ptr - (unsigned long)mnt);

	path.mnt = mnt;
	path.dentry=mnt->mnt_root;

	name = d_path(&path, buf, sizeof(buf));
	seq_printf(m, "mount dir is %s.\n", name);
	if(mt){
		struct vfsmount *cd = NULL;
		seq_printf(m, "parent fs is %s,mt=0x%px.\n", mt->mnt_sb->s_type->name,mt);
		get_mount_entry(mt, &ptr, &cd);
		if(cd) {
			seq_printf(m, "parent parent fs is %s cd=0x%px.\n", cd->mnt_sb->s_type->name, cd);
		} else {
			seq_printf(m, "parent parent fs is null.\n");
		}
	}
}

static void check_fs_mount(struct seq_file *m, char *path)
{
	struct vfsmount *mnt, *root;
    	struct dentry* dentry = NULL;

	root = current->fs->root.mnt;

	mnt = get_path_mount(path);
	if(mnt){
		seq_printf(m, "fstype %s, info %s, mntroot %s, mntroot 0x%px, superroot %px, root %px.\n", \
				mnt->mnt_sb->s_type->name, mnt->mnt_sb->s_id, mnt->mnt_root->d_name.name, mnt->mnt_root, mnt->mnt_sb->s_root, root);
		seq_printf(m, "fstype %s, info %s, mntroot %s, mntroot 0x%px, superroot %px, root %px,mnt %px.\n", \
				root->mnt_sb->s_type->name, root->mnt_sb->s_id, root->mnt_root->d_name.name, root->mnt_root, root->mnt_sb->s_root, root, mnt);
		if(mnt->mnt_sb->s_root == mnt->mnt_root){
			seq_printf(m, "root identical.\n");
		} else {
			seq_printf(m, "root non-identical.\n");
		}

		dentry = mnt->mnt_root;
    		while (dentry->d_parent != dentry) {
        		dentry = dentry->d_parent;
			seq_printf(m, "dentry name %s.\n", dentry->d_name.name);
    		}
		hack_mount(m, mnt);
	}
}

void list_all_mount_points(struct seq_file *m)
{
	//struct task_struct *task = get_current();

	check_fs_mount(m, "/");
	//check_fs_mount(m, "/home/zlcao/Workspace/linux/ramfs/mountdir");
	check_fs_mount(m, "/dev");
	check_fs_mount(m, "/tmp");
	check_fs_mount(m, "/var");
	check_fs_mount(m, "/dev/pts");
	check_fs_mount(m, "/home/zlcao/Workspace/zlramfs");

	return;
}

static int __process_all_devices(struct device *dev, void *data)
{
	struct seq_file *m = (struct seq_file*)data;
	if(dev->type == &i2c_adapter_type){
		struct i2c_adapter *adap = to_i2c_adapter(dev);
		seq_printf(m, "this is adaptor adaptor name %s dev name %s.\n", adap->name, dev_name(&adap->dev));
	}
	else if(dev->type == &i2c_client_type){
		struct i2c_client *clie = to_i2c_client(dev);
		seq_printf(m, "this is device %s, dev %s.\n", clie->name, dev_name(&clie->dev));
	}
	else
		seq_printf(m, "cant hanppend.\n");

	return 0;
}

static void probe_i2c_adaptor(struct seq_file *m, int id)
{
	struct i2c_adapter *adap;
	adap = i2c_get_adapter(id);
	if(!adap){
		seq_printf(m, "cant find i2c adaptor 6.\n");
		return;
	}
	//i2c_transfer(adap, msg, ...);
	seq_printf(m, "i2c adaptor name %s, i2c dev name %s.\n", adap->name, dev_name(&adap->dev));
	i2c_put_adapter(adap);

	i2c_for_each_dev(m, __process_all_devices);
}

void list_all_i2c_devices(struct seq_file *m)
{
	probe_i2c_adaptor(m, 0);
	probe_i2c_adaptor(m, 1);
	probe_i2c_adaptor(m, 2);
	probe_i2c_adaptor(m, 3);
	probe_i2c_adaptor(m, 4);
	probe_i2c_adaptor(m, 5);
	probe_i2c_adaptor(m, 6);
	probe_i2c_adaptor(m, 7);
	return;
}

typedef struct dmi_info_entry
{
	int id;
	char *name;
}dmi_info_entry_t;

#define DMI_ENTRY(idx) {.id = idx, .name = #idx}

dmi_info_entry_t dmi_tbl[] = {
	DMI_ENTRY(DMI_NONE),
	DMI_ENTRY(DMI_BIOS_VENDOR),
	DMI_ENTRY(DMI_BIOS_VERSION),
	DMI_ENTRY(DMI_BIOS_DATE),
	//DMI_ENTRY(DMI_BIOS_RELEASE),
	//DMI_ENTRY(DMI_EC_FIRMWARE_RELEASE),
	DMI_ENTRY(DMI_SYS_VENDOR),
	DMI_ENTRY(DMI_PRODUCT_NAME),
	DMI_ENTRY(DMI_PRODUCT_VERSION),
	DMI_ENTRY(DMI_PRODUCT_SERIAL),
	DMI_ENTRY(DMI_PRODUCT_UUID),
	DMI_ENTRY(DMI_PRODUCT_SKU),
	DMI_ENTRY(DMI_PRODUCT_FAMILY),
	DMI_ENTRY(DMI_BOARD_VENDOR),
	DMI_ENTRY(DMI_BOARD_NAME),
	DMI_ENTRY(DMI_BOARD_VERSION),
	DMI_ENTRY(DMI_BOARD_SERIAL),
	DMI_ENTRY(DMI_BOARD_ASSET_TAG),
	DMI_ENTRY(DMI_CHASSIS_VENDOR),
	DMI_ENTRY(DMI_CHASSIS_TYPE),
	DMI_ENTRY(DMI_CHASSIS_VERSION),
	DMI_ENTRY(DMI_CHASSIS_SERIAL),
	DMI_ENTRY(DMI_CHASSIS_ASSET_TAG),
	//DMI_ENTRY(DMI_STRING_MAX),
	DMI_ENTRY(DMI_OEM_STRING),
};

void dmi_info_verbose(struct seq_file *m)
{
	int i;
	
	for (i = 0; i < sizeof(dmi_tbl)/sizeof(dmi_info_entry_t); i ++){
		const char *s = dmi_get_system_info(dmi_tbl[i].id);
		if(s)
			seq_printf(m, "DMI %s INFO: %s.\n", dmi_tbl[i].name, s);
		else
			seq_printf(m, "DMI %s INFO is null.\n", dmi_tbl[i].name);
	}

	return;
}

unsigned long get_mm_rss_counter(struct mm_struct *mm, int idx)
{
	unsigned long val = atomic_long_read(&mm->rss_stat.count[idx]);

	return val;
}

void list_bus_iommu(struct seq_file *m)
{
	if(pci_bus_type.iommu_ops)
		seq_printf(m, "pci_bus type iommu %px.\n", pci_bus_type.iommu_ops);
	else 
		seq_printf(m, "pci bus iommuops is null.\n");
	if(i2c_bus_type.iommu_ops)
		seq_printf(m, "i2c_bus type iommu %px.\n", i2c_bus_type.iommu_ops);
	else 
		seq_printf(m, "i2c bus iommuops is null.\n");
	if(platform_bus_type.iommu_ops)
		seq_printf(m, "platform_bus type iommu %px.\n", platform_bus_type.iommu_ops);
	else 
		seq_printf(m, "platform bus iommuops is null.\n");
}
void list_all_process_vma(struct seq_file *m)
{
	int i;
	struct mm_struct *mm;
	struct rb_node *nd;
	struct rb_root *root;
	struct vm_area_struct *vma;
	char buf[256];

	mm = current->mm;
	root = &mm->mm_rb;

	seq_printf(m, "mm->mm_count %d, mm->mm_users %d mm->total_vm(total pages mapped) %ld, mm->pinned_vm 0x%llx.\n", \
			atomic_read(&mm->mm_count), atomic_read(&mm->mm_users), mm->total_vm, atomic64_read(&mm->pinned_vm));

	seq_printf(m, "pagetable bytes 0x%lx.\n", mm_pgtables_bytes(mm));

	for(i = 0; i < NR_MM_COUNTERS; i ++) {
		seq_printf(m, "rss idx %d: 0x%lx.\n", i, get_mm_rss_counter(mm, i));
	}

	i = 0;
	seq_printf(m, "iter vam with rbtree in mm_struct.\n");
	for (nd = rb_first(root); nd; nd = rb_next(nd)) {
		char *nm = "?";
		vma = rb_entry(nd, struct vm_area_struct, vm_rb);
		memset(buf, 0x00, 256);
		if(vma->vm_file) {
			nm = file_path(vma->vm_file, buf, 255);
		}
		seq_printf(m, "%d: %s line %d, vma->start: 0x%lx, vma->end: 0x%lx, file %s.\n", \
				i++, __func__, __LINE__, vma->vm_start, vma->vm_end, nm);
	}

	i = 0;
	seq_printf(m, "iter vam with vmlist in mm_struct.\n");
	vma = mm->mmap;
	while(vma != NULL) {
		seq_printf(m, "%d: %s line %d, vma->start: 0x%lx, vma->end: 0x%lx.\n", \
				i++, __func__, __LINE__, vma->vm_start, vma->vm_end);
		vma = vma->vm_next;
	}

	return;
}

void list_all_mount_device(struct seq_file *m)
{
#if 0
	struct nsproxy *nsp;
	struct task_struct *task = current;
	struct mnt_namespace *ns = NULL;
	struct list_head *p;
	struct mount *mnt, *ret = NULL;

	task_lock(task);
	nsp = task->nsproxy;
	if (!nsp || !nsp->mnt_ns) {
		printk("%s line %d. return error.\n", __func__, __LINE__);
		task_unlock(task);
		put_task_struct(task);
		return;
	}
	ns = nsp->mnt_ns;
	if (!task->fs) {
		printk("%s line %d. return error.\n", __func__, __LINE__);
		task_unlock(task);
		put_task_struct(task);
		return;
	}
	task_unlock(task);
	put_task_struct(task);

	list_for_each_continue(p, &ns->list) {
		mnt = list_entry(p, typeof(*mnt), mnt_list);
	}
	return;
#else
	struct file_system_type *fs_type;
	fs_type = get_fs_type("ext4");
	if(fs_type == NULL){
		printk("%s line %d. return error.\n", __func__, __LINE__);
		return;
	}

	iterate_supers_type(fs_type, f, m);
#endif
}
// 此函数将数据写入`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 output: PID=%u, task: %s, index=%lld, read_pos=%lld, %s.\n", task_tgid_nr(task),/* 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_nr(task), /* task->tgid,*/
			    task->comm, m->index, m->read_pos, tsk? "has session" : "no session");
        }

	seq_printf(m, "==================\n");
	sched_show_task(task);
    } 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);
        // class_find_device.
        // class_find_device_by_name.
        // class_for_each_device.
    } else if(seqfile_debug_mode == 9) {
        struct device_driver *drv; 
	int ret;

        drv = driver_find("pcieport", &pci_bus_type);
        ret = driver_for_each_device(drv, NULL, (void*)m, list_device_belongs_todriver_pci);
    } else if(seqfile_debug_mode == 10) {
        for_each_process(task) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
            seq_printf(m, "Process %s(%d),state 0x%08x, 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));
#else
            seq_printf(m, "Process %s(%d),state 0x%lx, 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));
#endif
            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; 
	int ret;
        // 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);
        ret = driver_for_each_device(drv, NULL, (void*)m, list_device_belongs_todriver_platform);
    } else if(seqfile_debug_mode == 13) {
        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 kobject failure.\n", __func__, __LINE__);
            return -ENOMEM;
        }

        kobj_created = 1;
        ret = sysfs_create_file(&kobj, &height.attr);
        if(ret != 0){
            printk("%s line %d, fatal error, create sysfs attribute failure.\n", __func__, __LINE__);
            return -ENOMEM;
        }
        kobj_created = 1;
    } 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 if(seqfile_debug_mode == 16) {
        struct pci_dev *pdev    = NULL;
        struct pci_dev *pparent = NULL;
        struct pci_bus *bus     = NULL;
        struct pci_bus *rootbus = NULL;
        struct pci_bus *findbus = NULL;
    
        findbus = pci_find_bus(0, 0);

        list_for_each_entry(rootbus, &pci_root_buses, node) {
            seq_printf(m, "pcibus name %s bus %p, findbus %p.\n", rootbus->name, rootbus, findbus);
            break;
        }

        bus_for_each_dev(&pci_bus_type, NULL, (void*)m, lookup_pci_devices_reset);

        pdev = g_pci_dev;
        if(pdev == NULL) {
            printk("%s line %d, return null.\n", __func__, __LINE__);
            return -1;
        }

        pci_reset_sbr(pdev);

        pci_lock_rescan_remove();
        pci_stop_and_remove_bus_device(pdev);
        pci_unlock_rescan_remove();

        bus = pdev->bus;
        //if (!pci_is_root_bus(bus) && list_empty(&bus->devices))
        if (0)
        {
            printk("%s line %d, lightweight rescan.\n", __func__,  __LINE__);
            pci_lock_rescan_remove();
            pci_rescan_bus_bridge_resize_priv(bus->self);
            pci_unlock_rescan_remove();
        } else {
            printk("%s line %d, rescan.\n", __func__,  __LINE__);
            if(bus->self) {
                bus = bus->self->bus;
                pparent = bus->self;
            }

            seq_printf(m, "%s line %d, do the reset of wireless device, device PCI BUS No.:%d. busname %s, bus %p.\n.", \
                    __func__, __LINE__, bus->number, bus->name, bus);

            if(pparent && pparent->bus) {
                seq_printf(m, "%s line %d, do the reset of wireless device, device PCI:0x%04x:%d:%d.%d\n.", \
                    __func__, __LINE__, pci_domain_nr(pparent->bus), pparent->bus->number, PCI_SLOT(pparent->devfn), PCI_FUNC(pparent->devfn));
            }

            if(bus == rootbus) {
                seq_printf(m, "warnings: reset root bus.\n");
            }

            pci_lock_rescan_remove();
            pci_rescan_bus(bus);
            pci_unlock_rescan_remove();
        }
    } else if(seqfile_debug_mode == 17) {
        struct pci_bus *pbus = NULL;

        while ((pbus = pci_find_next_bus(pbus)) != NULL) {
            seq_printf(m, "find bus %s.\n", pbus->name);  
        }

        list_for_each_entry(pbus, &pci_root_buses, node) {
            seq_printf(m, "pcibus name %s.\n", pbus->name);
            pci_walk_bus(pbus, pcie_device_info_find_bridge, (void*)m);
        }

        bus_for_each_dev(&pci_bus_type, NULL, (void*)m, lookup_pci_devices);
    } else if(seqfile_debug_mode == 18) {
        struct net *net;
        struct net_device *ndev;

        for_each_net(net)
            for_each_netdev(net, ndev) {
                seq_printf(m, "%s line %d, ndev->name %s. net 0x%p.\n", __func__, __LINE__, ndev->name, net);
            }
    } else if(seqfile_debug_mode == 19) {
	unsigned long *p;
	const struct cpumask *mask = cpu_cpu_mask(0);
	p = (unsigned long*)mask;
	seq_printf(m, "mask 0x%lx.\n", p[0]);
    } else if(seqfile_debug_mode == 20) {
	struct task_struct *process, *thread;
    	rcu_read_lock();
    	for_each_process_thread(process, thread) { 
        	if (unlikely(thread->flags & PF_VCPU)) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
			seq_printf(m, "%s line %d, comm %s, pid %d, is %s thread state 0x%x, proces %s(%d).\n", 
					__func__, __LINE__, thread->comm, thread->pid, thread->mm? "user" : "kernel",thread->__state, process->comm, process->pid);
#else
			seq_printf(m, "%s line %d, comm %s, pid %d, is %s thread state 0x%lx, proces %s(%d).\n", 
					__func__, __LINE__, thread->comm, thread->pid, thread->mm? "user" : "kernel",thread->state, process->comm, process->pid);
#endif
		}
   	}
   	rcu_read_unlock(); 
    } else if(seqfile_debug_mode == 21) {
        list_all_mount_device(m);
    } else if(seqfile_debug_mode == 22) {
        list_all_mount_points(m);
    } else if(seqfile_debug_mode == 23) {
        list_all_i2c_devices(m);
    } else if(seqfile_debug_mode == 24) {
 	dmi_info_verbose(m);
    } else if(seqfile_debug_mode == 25) {
	list_all_process_vma(m);
    } else if(seqfile_debug_mode == 26) {
	list_bus_iommu(m);
    } else if(seqfile_debug_mode == 27) {
	struct pci_dev *tmp = NULL;
    	for_each_pci_dev(tmp) {
		lookup_pci_devices(&tmp->dev, m);
	}
    } else if(seqfile_debug_mode == 28) {
	interval_tree_test(m);
    } 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;
    int old_cnt,new_cnt;

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

   old_cnt = atomic_read(&victim->signal->live);
   while((new_cnt=atomic_read(&victim->signal->live))) {
	if(new_cnt != old_cnt) {
        	printk("%s line %d, live %d.\n", __func__, __LINE__, atomic_read(&victim->signal->live));
		old_cnt = new_cnt;
	}
   }

   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", __func__, __LINE__, *ppos, size);
    ret = seq_read(file, buf, size, ppos);
    printk("%s line %d exit, fuck size %lld size %ld,ret = %ld.\n", __func__, __LINE__, *ppos, size, ret);
 
    return ret;
}
 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
static const struct proc_ops seq_proc_ops_new = {
    .proc_open      = proc_seq_open,
    .proc_read      = proc_seq_read,
    .proc_lseek     = seq_lseek,
    .proc_release   = seq_release,
    .proc_write     = proc_seq_write,
};
#else
static struct file_operations seq_proc_ops = {
    .owner      = THIS_MODULE,
    .open       = proc_seq_open,
    .release    = seq_release,
    .read       = proc_seq_read,
    .write      = proc_seq_write,
    .llseek     = seq_lseek,
    .unlocked_ioctl = NULL,
};
#endif
 
static struct proc_dir_entry * entry;
static int proc_hook_init(void)
{
    printk("%s line %d, init. seqfile_debug_mode = %d.\n", __func__, __LINE__, seqfile_debug_mode);

#if 1
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
    entry = proc_create("dumptask", 0644, NULL, &seq_proc_ops_new);
#else
    entry = proc_create("dumptask", 0644, NULL, &seq_proc_ops);
#endif
#else
    entry = proc_create_seq("dumptask", 0644, NULL, &my_seq_ops);
#endif
    return 0;
}
 
static void proc_hook_exit(void)
{
    if(kobj_created) {
        kobject_del(&kobj);
        kset_unregister(class_zilong);
    }

    proc_remove(entry);
 
    printk("%s line %d, exit.\n", __func__, __LINE__);
 
    return;
}
 
module_init(proc_hook_init);
module_exit(proc_hook_exit);
MODULE_AUTHOR("zlcao");
MODULE_LICENSE("GPL");

区间树使用了宏模板方式定义,用户只需要在源码中调用INTERVAL_TREE_DEFINE宏,传入必要的参数,编译器便会自动展开,生成对应的区间树接口函数:

利用编译器预处理功能将宏模板展开,并用astyle进行格式化。

make -C /lib/modules/5.4.0-148-generic/build M=/home/zlcao/Workspace/linux/dumpstack seqfile.i

rb_first返回红黑数的左子节点,可以看到其起始为3,是所有节点中最小的,印证了区间数是以START作为键值。另外,也可以从__subtree_last域反推出这一点,__subtree_last记录的是子树中最大的last,如果使用右边端点做键值,则直接按照红黑树结构,寻找最右侧的节点即可找到这个值,为什么还多此一举使用一个单独的变量__subtree_last去记录呢?通常,LINUX使用比较经济的策略管理内存,不会浪费空间去保存冗余变量。


结束

猜你喜欢

转载自blog.csdn.net/tugouxp/article/details/130537221