- 浏览: 75958 次
- 性别:
- 来自: 苏州
最新评论
接口就是ioctl 没什么好说的
定义了一个上下文的 static DECLARE_MUTEX(ioctl_sem); 用来限制一次只能调用一个ioctl
主要分为
Session / Conn/ volume 增加 减少
Param的配置修改
先看临时一下 iet_socket_bind
struct iscsi_conn { struct list_head list; /* list entry in session list */ struct iscsi_session *session; /* owning session */ u16 cid; unsigned long state; u32 stat_sn; u32 exp_stat_sn; int hdigest_type; int ddigest_type; struct list_head poll_list; struct file *file; /*通用 BSD socket 关键是socket->sk<L2 L3>*/ struct socket *sock; //... };
static void iet_socket_bind(struct iscsi_conn *conn) { int opt = 1; mm_segment_t oldfs; struct iscsi_session *session = conn->session; struct iscsi_target *target = session->target; dprintk(D_GENERIC, "%llu\n", (unsigned long long) session->sid); /*userspace传递的是struct file得到对应的sock */ conn->sock = SOCKET_I(conn->file->f_dentry->d_inode); conn->sock->sk->sk_user_data = conn; /*下面iscsi用了自己socket线程处理唤醒 方法,和kernel独立开*/ write_lock_bh(&conn->sock->sk->sk_callback_lock); /*sk_state_change: callback to indicate change in the state of the sock*/ target->nthread_info.old_state_change = conn->sock->sk->sk_state_change; conn->sock->sk->sk_state_change = iet_state_change; /*sk_data_ready: callback to indicate there is data to be processed*/ target->nthread_info.old_data_ready = conn->sock->sk->sk_data_ready; conn->sock->sk->sk_data_ready = iet_data_ready; target->nthread_info.old_write_space = conn->sock->sk->sk_write_space; conn->sock->sk->sk_write_space = iet_write_space; write_unlock_bh(&conn->sock->sk->sk_callback_lock); /*没什么好说的 其实应该用int kernel_setsockopt(struct socket *sock, int level, int optname, char *optval, unsigned int optlen)*/ oldfs = get_fs(); set_fs(get_ds()); conn->sock->ops->setsockopt(conn->sock, SOL_TCP, TCP_NODELAY, (void *)&opt, sizeof(opt)); set_fs(oldfs); }
非常著名的函数: 优化的源泉
void sock_init_data(struct socket *sock, struct sock *sk)
初始化 sock 结构中和IP相关的可扩展接口部分
void sock_init_data(struct socket *sock, struct sock *sk) { //... sk->sk_state_change = sock_def_wakeup; /*主要执行 wake_up_interruptible_sync_poll 唤醒socket_wq 的线程 固定CPU*/ sk->sk_data_ready = sock_def_readable; sk->sk_write_space = sock_def_write_space; sk->sk_error_report = sock_def_error_report; sk->sk_destruct = sock_def_destruct; //... } struct socket_wq { wait_queue_head_t wait; struct fasync_struct *fasync_list; struct rcu_head rcu; } ____cacheline_aligned_in_smp; static void sock_def_wakeup(struct sock *sk) { struct socket_wq *wq; rcu_read_lock(); wq = rcu_dereference(sk->sk_wq); /*如果有等待数据的进程就返回ture*/ if (wq_has_sleeper(wq)) wake_up_interruptible_all(&wq->wait); rcu_read_unlock(); }
static void iet_data_ready(struct sock *sk, int len)
{
struct iscsi_conn *conn = sk->sk_user_data;
struct iscsi_target *target = conn->session->target;
/*就是调用 wake_up_process(info->task); struct task_struct *task; 通知他们起来处理*/
nthread_wakeup(target);
target->nthread_info.old_data_ready(sk, len);
}
static int is_data_available(struct iscsi_conn *conn)
{
int avail, res;
mm_segment_t oldfs;
struct socket *sock = conn->sock;
oldfs = get_fs();
set_fs(get_ds());
/*这里作者很懒啊 ,直接通过FIONREAD 来得到数据情况*/
res = sock->ops->ioctl(sock, SIOCINQ, (unsigned long) &avail);
set_fs(oldfs);
return (res >= 0) ? avail : res;
}
下面看看 add_conn
static int add_conn(struct iscsi_target *target, unsigned long ptr)
{
//...
err = copy_from_user(&info, (void *) ptr, sizeof(info));
if (err)
return -EFAULT;
/*找到对应会话*/
session = session_lookup(target, info.sid);
if (!session)
return -ENOENT;
return conn_add(session, &info);
}
int conn_add(struct iscsi_session *session, struct conn_info *info)
{
//...
/*如果有了 就删掉 重新生成*/
conn = conn_lookup(session, info->cid);
if (conn)
conn_close(conn);
err = iet_conn_alloc(session, info);
if (!err && conn)
err = -EEXIST;
return err;
}
iet_conn_alloc 初始化iscsi_conn 结构体 ,所有的核心操作都和其有关
static int iet_conn_alloc(struct iscsi_session *session, struct conn_info *info)
{
struct iscsi_conn *conn;
dprintk(D_SETUP, "%#Lx:%u\n", (unsigned long long) session->sid, info->cid);
conn = kzalloc(sizeof(*conn), GFP_KERNEL);
if (!conn)
return -ENOMEM;
//...
spin_lock_init(&conn->list_lock);
atomic_set(&conn->nr_cmnds, 0);
atomic_set(&conn->nr_busy_cmnds, 0);
INIT_LIST_HEAD(&conn->pdu_list);
INIT_LIST_HEAD(&conn->write_list);
INIT_LIST_HEAD(&conn->poll_list);
init_timer(&conn->nop_timer); /*连接超时器*/
list_add(&conn->list, &session->conn_list);
set_bit(CONN_ACTIVE, &conn->state);
/*用户空间建立的连接传下来处理 */
conn->file = fget(info->fd);
iet_socket_bind(conn);
/*把自己加到该session 的激活链接中*/
list_add(&conn->poll_list, &session->target->nthread_info.active_conns);
nthread_wakeup(conn->session->target);
return 0;
}
add_session()->session_add()-> iet_session_alloc
和上面类似 不过建立了一个 命令的hash 数组链表
{
//...
for (i = 0; i < ARRAY_SIZE(session->cmnd_hash); i++)
INIT_LIST_HEAD(&session->cmnd_hash[i]);
spin_lock_init(&session->ua_hash_lock);
for (i = 0; i < ARRAY_SIZE(session->ua_hash); i++)
INIT_LIST_HEAD(&session->ua_hash[i]);
list_for_each_entry(vol, &target->volumes, list)
/* power-on, reset, or bus device reset occurred */
ua_establish_for_session(session, vol->lun, 0x29, 0x0);
//...
}
然后来看看干活的进程
static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
//...
if (cmd == ADD_TARGET) {
err = add_target(arg);
goto done;
}
//...
}
add_target()->通过用户入参target_info 初始化
target_add()->必要的检验
iscsi_target_create()->开始target线程 完成结构体初始化
target_thread_start()->
int nthread_start(struct iscsi_target *target)
{
//...
task = kthread_run(istd, target, "istd%d", target->tid);
//...
}
这是最主要关键的后台进程
static int istd(void *arg) { struct iscsi_target *target = arg; struct network_thread_info *info = &target->nthread_info; struct iscsi_conn *conn, *tmp; /*进入调度*/ __set_current_state(TASK_RUNNING); do { spin_lock_bh(&info->nthread_lock); __set_current_state(TASK_INTERRUPTIBLE); /*数据没有好就让出调度 (这里要注意设置顺序,防止丢失事件)*/ if (!test_bit(D_DATA_READY, &info->flags)) { spin_unlock_bh(&info->nthread_lock); schedule(); spin_lock_bh(&info->nthread_lock); } __set_current_state(TASK_RUNNING); clear_bit(D_DATA_READY, &info->flags); spin_unlock_bh(&info->nthread_lock); target_lock(target, 0); list_for_each_entry_safe(conn, tmp, &info->active_conns, poll_list) { if (test_bit(CONN_ACTIVE, &conn->state)) process_io(conn);/*处理IO事物*/ else close_conn(conn); } target_unlock(target); } while (!kthread_should_stop()); return 0; }
先回顾一下 socket-> ops 的一个函数指针结构
struct proto_ops { //... ssize_t (*sendpage) (struct socket *sock, struct page *page, int offset, size_t size, int flags); ssize_t (*splice_read)(struct socket *sock, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags); //
...
}
在 tcp4中主要是
struct proto tcp_prot = {
.sendpage = tcp_sendpage,
}
Tcp_sendpage 也就是do_tcp_sendpages 先把页面拆分后
放到sk_write_queue 中然后按需通过 __tcp_push_pending_frames (多页) tcp_push_one(单页 头部) 发送出去。
static void process_io(struct iscsi_conn *conn) { struct iscsi_target *target = conn->session->target; int res, wakeup = 0; /*从网络上接受过来的数据请求*/ res = recv(conn); //... /*将带数据相应包发送出去*/ res = send(conn); /*如果还有数据就等待下次处理*/ if (!list_empty(&conn->write_list) || conn->write_cmnd) { conn_reset_nop_timer(conn); wakeup = 1; } //... return; }
函数很简单没什么好说的 看看recv
static int recv(struct iscsi_conn *conn) --> case RX_DATA: do_recv()
这里作者还是很懒
直接 res = sock_recvmsg(conn->sock, &msg, len, MSG_DONTWAIT | MSG_NOSIGNAL);
然后就是 send
static int send(struct iscsi_conn *conn) -> 先是 case TX_INIT:
cmnd_tx_start(cmnd); case ISCSI_OP_SCSI_RSP:
static void cmnd_send_pdu(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd)
将 iscsi_conn-> write_tcmnd->tio 该tio 通过随后的 do_send()--->write_data()-->sendpage
发送出去
发表评论
-
置顶说明
2018-08-19 12:41 416根据公司要求技术blog 涉及公司业务 只能在内网发布。从 ... -
手机QQ公众号亿级消息实时群发架构
2017-01-21 17:42 777http://chuansong.me/n/2071796 -
<linux net>tcp optimize
2011-07-04 09:29 2047tcp_sack :tcp_sack - BOOLEAN E ... -
IIC
2011-01-18 22:57 0异步串行通讯原理: 波特率:每秒发送码元的位数 常 ... -
<Learn From Kernel> printk_ratelimit()
2010-12-25 21:03 4148今天在看ldd网卡驱动的时候,发现一个有趣的函数 printk ... -
<linux Assembly> atom_inc
2010-12-17 11:10 1363当然首先还是说 一下,好像在RISC 思想中, 使用 原子交换 ... -
<linux Assembly> 常用工具
2010-12-16 15:51 12621.1 gdb 1) info registers ...
相关推荐
iscsi-initiator-utils-6.2.0.874-19.el7.aarch64.rpm python-kmod-0.9-4.el7.aarch64.rpm iscsi-initiator-utils-iscsiuio-6.2.0.874-19.el7.aarch64.rpm python-rtslib-2.1.72-1.el7.noarch.rpm pyparsing-1.5.6-9...
2018\09\10 周一 17:27 <DIR> iSCSI WMI Client 2018\09\10 周一 17:27 <DIR> Kernel Counter Sample (Kcs) 2018\09\10 周一 17:27 <DIR> Kernel mode display-only miniport driver (KMDOD) sample 2018\09\10 周一...
iscsi-initiator-utils-6.2.0.871-0.10.el5.x86_64.rpm
官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装
官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装
你可以通过`yum`仓库安装,命令为`# yum -y install iscsi-initiator-utils`,或者从本地RPM包安装,命令为`# rpm -uvh iscsi-initiator-utils-<ver>.rpm`。 2. 可选地,你可以配置发起者的别名,例如`echo ...
- 安装iSCSI Initiator软件包,执行命令:`rpm -ivh iscsi-initiator-utils-<version>.rpm`。 2. **配置iSCSI Initiator**: - 安装完成后,进入`/etc/iscsi`目录,编辑`initiatorname.iscsi`文件,这个文件包含...
官方离线安装包,亲测可用
在某些centos 6 32位系统下使用 yum -y install scsi-target-utils安装提示下载相关软件包失败时,可以下载这个rpm离线安装包解决问题。
离线安装包,亲测可用
标题中的“ceph-iscsi-tools-2.1-3.el7cp.noarch”是指Ceph iSCSI工具的特定版本,适用于Red Hat Enterprise Linux 7的兼容版本(el7cp)。Ceph是一个开源的分布式存储系统,而iSCSI(Internet Small Computer ...
官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装
**open-iscsi-2.0.873.zip** 是一个包含开源iSCSI启动器(open-iscsi)源代码的压缩包,版本号为2.0.873。iSCSI(Internet Small Computer System Interface)是一种网络存储协议,它允许通过IP网络进行块级数据传输,...
linux下的iscsi启动器软件,希望对你有用!
iscsi-initiator-utils-6.2.0.873-2.el6.x86_64.rpm
iscsi-initiator_linux驱动
- **iSCSI驱动:** 实现了iSCSI协议的软件或硬件模块,允许主机通过IP网络访问存储资源。 - **网络:** 连接Initiator和Target之间的物理网络,通常为以太网。 #### 二、Linux下iSCSI配置详解 在Linux环境下配置...
Windonws下 Microsoft iSCSI Software Initiator Version 2.08 Initiator-2.08-build3825-x64fre.exe Initiator-2.08-build3825-x86fre.exe Linux下 open-iscsi-2.0-873.tar
在CentOS或Fedora系统中,使用的是`iscsi-initiator-utils`: ```bash sudo yum install iscsi-initiator-utils ``` ### 2. 配置iSCSI发起器 安装完成后,需要编辑配置文件 `/etc/iscsi/iscsid.conf`。确保以下几...
这个“iSCSI-2.08-x86(网吧实测稳定版)”是一个专为x86架构设计的iSCSI软件版本,特别强调在网吧环境中的稳定性和可靠性。在网吧这样的多用户、高并发访问存储的环境中,一个稳定的iSCSI解决方案至关重要,因为它...