在基于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的安装:
[[email protected]
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