tgt服务端流程分析

整体流程

Figure1 tgt流程图

基本操作

安装tgt包:

yum --enablerepo=epel -y install scsi-target-utils

安装完成后,启动 TGT 服务:

# service tgtd start

如果想在系统启动时自动启动 TGT 服务,可以使用如下命令:

# chkconfig tgtd on

如果想确认 TGT 服务是否启动,可以用 # servicetgtd status 查看服务状态;当然,也可以查询 TGT 服务占用的端口:

# netstat -anlpt | grep 3260

其中,3260 是 TGT 服务的监听端口。结果如下:

创建Target:

tgtadm --lld iscsi --mode target --op new --tid 2--targetname iqn.2012-12.com.example:server.target

查询Target:

tgtadm --lld iscsi--mode target --op show

查询详细信息:

tgtadm --lld iscsi--mode target --op show --tid 2 

创建用作LUN的文件:

ddif=/dev/zero of=/home/lun.bin count=0 obs=1 seek=1G 

添加LUN:(需要关闭SELinux,否则报错invalid

tgtadm --lld iscsi--mode logicalunit --op new --tid 2 --lun 1 --backing-store /home/lun.bin

修改配置文件需要重启机器:

修改/etc/selinux/config 文件

SELINUX=enforcing改为SELINUX=disabled

重启机器即可

设置访问权限all:(服务端权限要设到具体IP,否则客户端discover失败)

 tgtadm --lldiscsi --mode target --op bind --tid 2 -I all

tgtadm --lld iscsi--mode target --op bind --tid 2 -I 192.168.56.103

iscsi initiator 连接(需要关闭服务端防火墙)

systemctl stop firewalld.service

systemctl disable firewalld.service

iscsiadm -m discovery -t sendtargets -p 192.168.56.102

登录:

iscsiadm -m node --login

#登录某一个

#iscsiadm --mode node --targetname iqn.2012-10.net.cpd:san.target01 --portal 172.16.201.200 --login

查看核对登录信息

iscsiadm -m session -o show

登陆后,fdisk -l 能看到iscsi挂载的服务端设备,然后可以当做正常磁盘设备一样使用。

配置tgt编译环境:

去掉doc相关编译,网络连接问题会卡住,

find -name Makefile | xargs grep O2 -R--colour

make DEBUG=yes

make install

然后就可以gdb调试了~

tgt流程分析:

http://www.sysnote.org/2014/08/23/tgt-arch/

/usr/sbin/tgtd -f

tgt锁:

/var/run/tgtd/socket.0.lock

清理锁:

rm -f /var/run/tgtd/*

杀掉tgtd进程:

kill -9 pid

带debug信息调试:

gdb -args /usr/sbin/tgtd -f -d 1

流程详解

创建target

执行:

tgtadm --lld iscsi --mode target --op new --tid 2--targetname iqn.2012-12.com.example:server.target

触发:

mgmt_event_handler

main ->ipc_init -> tgt_event_add (mgmt_event_handler )

通过socket来触发事件:/var/run/tgtd/socket.0

tgtadm 的ipc_mgmt_connect触发了tgtd的事件mgmt_event_handler

main -> ipc_mgmt_req -> ipc_mgmt_connect

             ipc_mgmt_req -> ipc_mgmt_rsp -> ipc_mgmt_connect

用来连接进程。

多次触发:

mtask_recv_send_handler 来进行进程间通信。

mtask_recv_send_handler ->mtask_received->mtask_execute ->target_mgmt ->tgt_target_create->tgt_device_create

backing 为 0,不会启动bs工作线程

用来进行具体操作。

创建后端存储

执行:

tgtadm --lld iscsi --mode logicalunit --op new --tid 2--lun 1 --backing-store /home/lun.bin

触发:

mgmt_event_handler(添加了管理事件处理)

mtask_recv_send_handler ->mtask_received->mtask_execute -> device_mgmt -> tgt_device_create ->bs_rdwr_init ->bs_thread_open->bs_thread_worker_fn

在执行new 后端时,会调用 bs_rdwr_init从而开启bs_thread_worker_fn.

创建设备时,创建了16个bs工作线程。

 

设置客户端访问IP

执行:

tgtadm --lld iscsi --mode target --op bind --tid 2 -I192.168.56.103

触发:

mgmt_event_handler

mtask_recv_send_handler ->mtask_received->mtask_execute -> target_mgmt ( OP_BIND )

客户端登录

执行:

iscsiadm -m node --login

触发:

accept_connection/ iscsi_tcp_event_handler

accept_connection 用于连接客户端,iscsi_tcp_event_handler用于处理各种客户端的请求。

事件相关

list_add(&tev->e_list, &tgt_events_list);

事件具体信息被加入到tgt_events_list中

tgt_event_add中将事件句柄加入ep_fd代表的实体

    err = epoll_ctl(ep_fd, EPOLL_CTL_ADD, fd, &ev);

ep_event_modify 可以修改事件为EPOLLOUT

网络端口初始化

tcp初始化流程:main->lld_init ->lld_init_one ->iscsi_init ->iscsi_tcp_init->iscsi_add_portal ->iscsi_add_portal ->iscsi_tcp_init_portal

 

3260是监听端口

getaddrinfo 来获取socketaddr结构链表

https://baike.baidu.com/item/getaddrinfo/9021771

一个tcp socket对应两个句柄:IPv4 &IPv6

块设备相关初始化

main ->bs_init ->bs_init_signalfd (bs_sig_request_done)

 

优先使用信号量通知,其次使用线程间通知

信号量对应的处理函数是bs_sig_request_done。

 

bs_thread_worker_fn 中 kill(getpid(),SIGUSR2)来发信号触发bs_sig_request_done

sbc初始化

main执行前sbc_init初始化,注册sbc_template到device_type_list

tgt_device_create时将sbc_template加入lu中

 

初始化后scsi_cmd_perform中才能根据操作码op来找到对应的操作cmd_perform。

数据处理流程

iscsiadm -m node --login

客户端登录服务端以后,就产生了一些iscsi_tcp事件需要服务端处理。

主线程会触发事件:iscsi_tcp_event_handler

accept_connection 中定义了事件iscsi_tcp_event_handler

bs_thread_cmd_submit 激活了 bs_thread_worker_fn中的pthread_cond_wait

注:pthread_cond_wait是多线程抢占式的,pthread_cond_signal发信号,谁抢到了锁,谁去用,没抢到的while再次阻塞等待

以数据读写为例

iscsi_tcp_event_handler -> iscsi_rx_handler-> iscsi_task_rx_done -> iscsi_task_queue-> iscsi_task_execute -> iscsi_target_cmd_queue-> target_cmd_perform -> scsi_cmd_perform -> sbc_rw (以读写为例)

#0  bs_thread_cmd_submit(cmd=0x66abe0) at bs.c:464

#1  0x000000000042d938 in sbc_rw (host_no=0, cmd=0x66abe0) at sbc.c:399

#2  0x00000000004253a1 in scsi_cmd_perform(host_no=0, cmd=0x66abe0) at scsi.c:541

#3  0x0000000000420d59 in target_cmd_perform(tid=2, cmd=0x66abe0) at target.c:1168

#4  0x0000000000420c73 in target_cmd_queue(tid=2, cmd=0x66abe0) at target.c:1150

#5  0x000000000040c2f4 iniscsi_target_cmd_queue (task=0x66ab10) at iscsi/iscsid.c:1387

#6  0x000000000040c3c2 iniscsi_scsi_cmd_execute (task=0x66ab10) at iscsi/iscsid.c:1407

#7  0x000000000040c792 in iscsi_task_execute(task=0x66ab10) at iscsi/iscsid.c:1527

#8  0x000000000040cbac in iscsi_task_queue(task=0x66ab10) at iscsi/iscsid.c:1614

#9  0x000000000040d2ca in iscsi_task_rx_done(conn=0x666000) at iscsi/iscsid.c:1744

#10 0x000000000040e675 in iscsi_rx_handler(conn=0x666000) at iscsi/iscsid.c:2222

#11 0x0000000000416739 in iscsi_tcp_event_handler (fd=14,events=1, data=0x666000) at iscsi/iscsi_tcp.c:278

#12 0x000000000041b500 in event_loop () at tgtd.c:432

#13 0x000000000041be05 in main (argc=2,argv=0x7fffffffe628) at tgtd.c:624

bs_thread_cmd_submit将cmd->bs_list加到 info->pending_list上

bs_thread_cmd_submit-> pthread_cond_signal 来激活bs_thread_worker_fn

bs_thread_worker_fn 中的 info 和 bs_thread_cmd_submit 中的 info 是指向同一块内存单元(gdb证实)

bs_thread_worker_fn 中从info->pending_list上取下cmd

bs_rdwr_request从设备句柄fd里读出信息到scsi的buffer中,这个函数应该是底层块设备的具体操作了。

 

数据存在 cmd->in_sdb.buffer

将cmd->bs_list加入到finished_list

bs_thread_worker_fn中kill发信号SIGUSR2触发bs_sig_request_done,sig_fd

随后通过事件触发主线程执行bs_sig_request_done

bs_sig_request_done ->target_cmd_io_done->iscsi_scsi_cmd_done -> iscsi_event_modify (改事件为 EPOLLOUT)-> tgt_event_modify

将task加入task->conn->tx_clist由主线程处理

tgt_event_modify -> epoll_ctl打断主线程的epoll_wait睡眠,让主线程及时处理tx事件。

iscsi_tx_handler ->iscsi_task_tx_start-> iscsi_data_rsp_build 将读取的数据存在conn->rsp.data中

conn->rsp.data

写完rsp数据给客户端,事件变回EPOLLIN

iscsi_tx_handler -> ep_write_end( iscsi_tcp_write_end)

 

以上基本上就是一个完整的服务端读操作,响应给客户端的流程细节。

服务端上数据的读或写是由客户端控制的req->cdb传值给cmd->scb

 

bs_rdwr_request 中根据cmd->scb[0]来对块设备进行操作。

 

猜你喜欢

转载自blog.csdn.net/tdaajames/article/details/80983309
今日推荐