iscsi kernel 모듈 - 네트워크 부분

9472 단어
자세히 보기
인터페이스가 ioctl인데 뭐라고 정의할 게 없어요. 상하문에 static DECLAREMUTEX(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*/
 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
", (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); }

 
매우 유명한 함수: 최적화의 원천voidsockinit_데이터(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 infoldsk realdy reale}
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;}
다음은 addconnstatic 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 conaddursension, info &
int conn_dd(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 초기화 iscsiconn 구조체, 모든 핵심 조작과 관련static int ietconn_alloc(struct iscsi_session *session, struct conn_info *info){ struct iscsi_conn *conn;
 dprintk(D_SETUP, "%#Lx:%u", (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의 활성화 링크에 자신을 추가합니다 */listadd(&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 초기화 targetadd () -> 필요한 검사 iscsitarget_create()->target 라인 시작 구조체 초기화 targetthread_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 tcpprot = { .sendpage  = tcp_sendpage,
}
Tcp_sendpage 즉 Dotcp_sendpages 먼저 페이지를 분리한 후 skwrite_queue에서 필요에 따라 통과tcp_push_pending_frames tcppush_원 (한 페이지 머리) 발송.
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 = sockrecvmsg(conn->sock, &msg, len, MSG_DONTWAIT | MSG_NOSIGNAL);
그 다음은 send static int send(struct iscsi conn *conn)-> 먼저case TXINIT:cmnd_tx_start(cmnd);  case ISCSI_OP_SCSI_RSP:
static void cmnd_send_pdu(struct iscsi conn *conn, struct iscsi cmnd *cmnd)는 iscsiconn-> write_tcmnd->tio 이tio는 다음do 를 통과합니다send()--->write_데이터 () -->sendpage 보내기

좋은 웹페이지 즐겨찾기