本文共 3561 字,大约阅读时间需要 11 分钟。
在基于iSCSI构建的IP SAN系统中,为了便于targetd端对initiator进行访问权限管理和存储资源分配,有的客户通常需要尽可能早地得到initiator的IQN名称乃至其IP信息。常用的target管理工具targetadm虽然可以记录initiator登陆后的IQN名称和IP信息,但是它不能早在discovery session就发现initiator的IQN名称。虽然iSNS可以得到局域网范围内所有的iSCSI设备的名称,但是它配置比较复杂,为此可以考虑在iSCSI驱动中discovery session的时候就尽快拿到initiator的IQN。为此,我们需要找到合适的代码位置,然后用合适的接口把从内核驱动中得到的IQN信息告诉给用户。
阅读iSCSI驱动中的代码,不难找到discovery session的时候执行的代码,在iscsi_target_nego.c中的login nego函数中。类Unix系统设计中体现了一种良好的抽象哲学,就是几乎所有的数据实体都被抽象成一个统一的接口--文件,这样一些简单的基本工具就能完成大量复杂的操作。在Linux中也继承了这类特殊的伪文件系统,用于使用与文件接口统一的操作来完成各种功能,例如sysfs、configfs和procfs。这些接口可以考虑用来实现我们的功能。
Sysfs
sysfs把设备驱动和总线根据其拓扑信息映射到文件系统,,它是Linux 2.6所提供的一种虚拟文件系统。这个文件系统不仅可以把装置(devices)和驱动程式(drivers)的资讯从kernel space输出到user space,也可以用来对装置和驱动程式做设定。 sysfs的目的是把一些原本在procfs中的,关于装置的部份独立出来,以[装置阶层架构}(device tree)的形式呈现。它把实际连接到系统上的设备和总线组织成一个分级的文件,用户空间的程序同样可以利用这些信息以实现和内核的交互。由于我的需求只是简单输出一个IQN信息,不影响也不涉及device tree,因此不采用这种接口。
Configfs
configfs是一种基于ram的伪文件系统,支持在用户空间通过目录文件访问接口配置内核对象。例如修改或者显示内核设置,更改一些开关等配置信息,适用于内核对象有众多复杂的配置。比如内核需要很多参数需要配置时,或者需要动态创建内核对象并且内核对象需要修改配置;此时用户可以写shell脚本就可以直接配置configfs。一个简单的configfs的结构实现步骤如下:
1.加入一个item;
2.定义show和 store操作;(show是向user space更新消息,用来显示内核或驱动的设置;store是从用户态王内核态输入并保存信息,用来更改内核或驱动设置)
3.相关的重要的数据结构:
顶层结构是struct configfs_subsystem,为configfs子系统结构,接着是struct config_group,是configfs目录和属性的容器,struct config_item是configfs目录,代表可配置的内核对象,struct configfs_attribute是目录下面的属性。
可见configfs还是比较复杂,特别是考虑到我的需求只是从驱动中读会一个信息,不涉及修改任何内核变量或者配置,因此也不考虑用这个接口。
Procfs
procfs是最早的伪文件系统,作为Linux内核信息的抽象文件接口,常常用来动态显示或者控制内核或驱动的信息。内核中的信息以及可调参数都被作为常规文件映射到一个目录树中,这样通过echo或cat之类的文件操作命令对系统信息进行查取和调整了。同时procfs也提供了一个接口,使得我们自己的内核模块或用户态程序可以通过procfs进行参数的传递。考虑到我们从内核驱动中得到IQN的需求,以及procfs的简单易用性,我们刚好可以用proc接口来查取IQN信息了。
Procfs依赖的头文件
同用户态编程一样,为了利用procfs接口,我们不需要从头到尾重新设计和实现procfs的所有细节,应该尽可能地用内核procfs模块业已提供的函数接口。在3.10.0的内核中,这些接口函数定义在linux source code目录下:include/linux/proc_fs.h
Procfs API
查看proc_fs.h,可以看到它提供了丰富的接口来快速实现procfs:
IQN Procfs的实现
在了解procfs的主要接口后,我们可以参考其他驱动中的procfs的实现,比如drivers/scsi/scsi_devinfo.c中procfs的实现,或者driver/char/rtc.c中的下面代码来实现自己的iqn procfs。
static const struct file_operations rtc_proc_fops = {
.owner = THIS_MODULE,
.open = rtc_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
实现读取IQN的procfs主要的步骤如下:
1.在模块初始化的时候调用proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent,const struct file_operations *proc_fops)来创建/proc下的端点;
2.定义并实现读取initiator IQN必需的file_operations:
static const struct file_operations iscsi_iqn_get_fops = {
.owner = THIS_MODULE,
.open = proc_iscsi_iqninfo_open,
.read = proc_iscsi_iqninfo_read,
.write = NULL,
.llseek = NULL,
.release = NULL,
};
3.在模块卸载的时候实现exit操作,释放资源,当设备退出或者驱动removed的时候调用下面的函数:remove_proc_entry("scsi/device_info", NULL);
4.在新的discovery session 认证过程中,记录当前initiator的IQN,为了便于管理所有的IQN,可以把它们组织成list。
5.代码开发完功能验证完成后,可以参考下面的步骤进行部署:
编译和手动加载:
cd /root/linux-3.10.0-229.el7/drivers/target/iscsi;
make -C /lib/modules/3.10.0-229.el7.x86_64/build M=`pwd` modules
cp iscsi_target_mod.ko /lib/modules/3.10.0-229.el7.x86_64/kernel/drivers/target/iscsi
modprobe iscsi_target_mod -f
设置开机自动加载更新:
update /etc/sysconfig/modules/ls.modules
添加iscsi_target_mod的安装:
[root@localhost modules]# tail ls.modules
/sbin/modinfo -F filename iscsi_target_mod > /dev/null 2>&1 if [ $? -eq 0 ]; then /sbin/modprobe iscsi_target_mod -f fi参考链接:
1.http://blog.csdn.net/liumangxiong/article/details/12154865
2.http://www.linuxidc.com/Linux/2014-01/95688.htm
3.http://blog.csdn.net/liumangxiong/article/details/12154865
4.http://blog.csdn.net/cjsycyl/article/details/13091951
本文转自存储之厨51CTO博客,原文链接:http://blog.51cto.com/xiamachao/1856761 ,如需转载请自行联系原作者