iscsi-session.c

来自「iSCSI协议在LINUX下的源码.源代码是IBM公布的.主要是结合其OSD设备」· C语言 代码 · 共 1,649 行 · 第 1/3 页

C
1,649
字号
		test_and_clear_bit(TX_WAKE, &session->control_bits));	for (req = 0; req < TX_WAKE; req++) {		if (signal_pending(current))			return;		/*		 * when a logout is in progress or about to be sent		 * we do not start new requests, but we continue to		 * respond to R2Ts and Nops.		 */		if (test_and_clear_bit(req, &session->control_bits)) {			if (test_bit(SESSION_LOGOUT_REQUESTED,				     &session->control_bits) &&			    req <= TX_SCSI_COMMAND)				continue;			tx_request_fns[req].request_fn(session);		}	}}/** * session_kthread_sleep - put a thread to sleep while waiting for shutdown. * @session: session.  * * Description: *    If for some reason we could not relogin into a session we sleep here *    and and wait for someone to remove the session. Returns -EPERM to *    indicate the thread should exit, or zero to indicate that the thread *    can proceed with its normal action. **/static inline intsession_kthread_sleep(struct iscsi_session *session){ retest:	set_current_state(TASK_INTERRUPTIBLE);	if (kthread_should_stop()) {		__set_current_state(TASK_RUNNING);		return -EPERM;	}	/*	 * We fall into this sleep, when someone has broken us	 * out of the lower loops that process requests or log us in,	 * terminate the session (session drops will not sleep here),	 * but have not (yet) cleaned up the host and called kthread_stop()).	 */	if (test_bit(SESSION_TERMINATING, &session->control_bits)) {		schedule();		if (signal_pending(current))			flush_signals(current);		goto retest;	}	__set_current_state(TASK_RUNNING);	return 0;}/* * move to session timeout handler when that is fixed?? */static voidreplacement_timed_out(unsigned long data){	struct iscsi_session *session = (struct iscsi_session *)data;	iscsi_host_err(session, "replacement session time out after %d "		       "seconds, drop %lu, now %lu, failing all commands\n",		       session->replacement_timeout,		       session->session_drop_time, jiffies);	spin_lock(&session->task_lock);	if (test_bit(SESSION_ESTABLISHED, &session->control_bits) ||	    test_and_set_bit(SESSION_REPLACEMENT_TIMEDOUT,			     &session->control_bits)) {		spin_unlock(&session->task_lock);		return;	}	/*	 * No point in letting the commands timeout, fail them.	 */	iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_BUS_BUSY);	spin_unlock(&session->task_lock);	wake_up_all(&session->login_wait_q);}/* * the writer thread TODO - we should look into * replacing this thread and wait_for_tx_requests * with a work queue */static intiscsi_tx_thread(void *data){	int rc;	unsigned long tmo;	struct iscsi_session *session = (struct iscsi_session *)data;		current->flags |= PF_MEMALLOC;	allow_signal(SIGHUP);	/*	 * tell the rx thread that we're about to block, and that	 * it can safely call iscsi_sendmsg now as part of	 * the Login phase.	 */	up(&session->tx_blocked);	while (!session_kthread_sleep(session)) {		spin_lock(&session->portal_lock);		tmo = session->replacement_timeout * HZ;		if (tmo && session->session_drop_time) {			del_timer_sync(&session->replacement_timer);			session->replacement_timer.expires = jiffies + tmo;			add_timer(&session->replacement_timer);		}		spin_unlock(&session->portal_lock);		rc = iscsi_wait_for_session(session, 1);		spin_lock(&session->portal_lock);		del_timer_sync(&session->replacement_timer);		spin_unlock(&session->portal_lock);		if (!rc)			continue;		/* we're up and running with a new session */		down(&session->tx_blocked);		/* make sure we start sending commands again */		set_bit(TX_PING, &session->control_bits);		set_bit(TX_SCSI_COMMAND, &session->control_bits);		set_bit(TX_WAKE, &session->control_bits);		while (!signal_pending(current))			wait_for_tx_requests(session);		flush_signals(current);		up(&session->tx_blocked);	}	return 0;}static intrx_establish_session(struct iscsi_session *session, unsigned int login_delay){	int rc;	unsigned long login_failures = 0;	while (!test_bit(SESSION_ESTABLISHED, &session->control_bits)) {		if (login_delay) {			iscsi_host_notice(session, "Waiting %u seconds before "					  "next login attempt\n", login_delay);			msleep_interruptible(login_delay * 1000);		}		/*		 * wait for the tx thread to block or exit, ignoring signals.		 * the rx thread needs to know that the tx thread is not		 * running before it can safely close the socket and start		 * a new login phase on a new socket, Also, tasks still in		 * use by the tx thread can't safely be completed on		 * a session drop.		 */		down(&session->tx_blocked);		up(&session->tx_blocked);		if (test_bit(SESSION_TERMINATING, &session->control_bits))			return 0;		iscsi_disconnect(session);		clear_bit(SESSION_LOGOUT_REQUESTED, &session->control_bits);		clear_bit(SESSION_WINDOW_CLOSED, &session->control_bits);		rc = establish_session(session);		if (rc > 0)			/* established or redirected */			login_failures = 0;		else if (rc < 0)			/* failed, retry */			login_failures++;		 else {			/* failed, give up */			iscsi_host_err(session, "Session giving up\n");			set_bit(SESSION_TERMINATING, &session->control_bits);			return 0;		}		/* slowly back off the frequency of login attempts */		if (login_failures == 0)			login_delay = 0;		else if (login_failures < 30)			login_delay = 1;		else if (login_failures < 48)			login_delay = 5;		else if (session->replacement_timeout &&			 time_before_eq(session->session_drop_time +					(HZ * session->replacement_timeout),					jiffies))			login_delay = 10;		 else			login_delay = 60;	}	return 1;}/** * get_time2wait - return iSCSI DefaultTime2Wait * @session: iscsi session * @short_sessions: number of short sessions * * Description: *   Return DefaultTime2Wait. However, if the session dies really *   quicky after we reach FFP, we'll not be interoperable due to bugs *   in the target (or this driver) that send illegal opcodes, *   or disagreements about how to do CRC  calculations. To *   avoid spinning, we track sessions with really short *   lifetimes, and decrease the login frequency if we keep *   getting session failures, like we do for login failures. **/static unsigned intget_time2wait(struct iscsi_session *session, unsigned long *short_sessions){	unsigned int login_delay = 0;	if (session->time2wait >= 0) {		login_delay = session->time2wait;		session->time2wait = -1;	} else		login_delay = session->def_time2wait;	if (time_before_eq(session->session_drop_time,		     	   session->session_established_time + (2 * HZ))) {		(*short_sessions)++;		if (*short_sessions < 30)			login_delay = max_t(unsigned int, login_delay, 1);		else if (*short_sessions < 48)			login_delay = max_t(unsigned int, login_delay, 5);		else if (session->replacement_timeout &&			 time_before_eq(session->session_drop_time +					(HZ * session->replacement_timeout),					jiffies))			login_delay = max_t(unsigned int, login_delay, 10);		 else			login_delay = max_t(unsigned int, login_delay, 60);		iscsi_host_warn(session, "Session has ended quickly %lu times, "				"login delay %u seconds\n", *short_sessions,				login_delay);	} else		/* session lived long enough that the target is probably ok */		*short_sessions = 0;	return login_delay;}static intiscsi_rx_thread(void *data){	struct iscsi_hdr hdr;	unsigned int login_delay = 0;	unsigned long short_sessions = 0;	struct iscsi_session *session = (struct iscsi_session *)data;	current->flags |= PF_MEMALLOC;	allow_signal(SIGHUP);        while (!session_kthread_sleep(session)) {		if (!rx_establish_session(session, login_delay))			continue;		while (!signal_pending(current))			iscsi_recv_pdu(session, &hdr, session->header_digest,				       session->rx_buffer, ISCSI_RXCTRL_SIZE,				       session->data_digest, 0);		flush_signals(current);		login_delay = get_time2wait(session, &short_sessions);		/*		 * we need to wait for the tx thread to block before		 * trying to complete commands, since it may be using a		 * task at the moment, which means we can't complete it yet.		 * even if the session is terminating, we must wait for		 * the tx thread.		 */		down(&session->tx_blocked);		up(&session->tx_blocked);		spin_lock_bh(&session->task_lock);		/*		 * session dropped unexpectedly, often due to		 * network problems		 */		iscsi_host_err(session, "Session dropped\n");		iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_BUS_BUSY);		reinit_session(session);		spin_unlock_bh(&session->task_lock);	}	/*	 * If there are any commands left this will remove them.	 */	spin_lock_bh(&session->task_lock);	iscsi_flush_queues(session, ISCSI_MAX_LUNS, DID_NO_CONNECT);	spin_unlock_bh(&session->task_lock);	return 0;}static intstart_session_threads(struct iscsi_session *session){	session->tx_task = kthread_run(iscsi_tx_thread, session, "iscsi-tx");	if (IS_ERR(session->tx_task)) {		iscsi_host_err(session, "Failed to start tx thread, terminating"			       " session\n");		goto fail;	}	session->rx_task = kthread_run(iscsi_rx_thread, session, "iscsi-rx");	if (IS_ERR(session->rx_task)) {		iscsi_host_err(session, "Failed to start rx thread, terminating"			       " session\n");		goto shutdown_tx_thread;	}	init_timer(&session->session_timer);	session->session_timer.data = (unsigned long)session;	session->session_timer.function = session_timeout;	/*	 * During periods of transition we use some safe value.	 */	session->session_timer.expires = DEF_SESSION_TIMEOUT;	add_timer(&session->session_timer);	return 0; shutdown_tx_thread:	set_bit(SESSION_TERMINATING, &session->control_bits);	kthread_stop(session->tx_task); fail:	return -EAGAIN;}voidiscsi_destroy_session(struct iscsi_session *session){	set_bit(SESSION_TERMINATING, &session->control_bits);	down(&iscsi_session_sem);	list_del(&session->list);	up(&iscsi_session_sem);	clear_bit(SESSION_ESTABLISHED, &session->control_bits);	session->session_drop_time = jiffies ? jiffies : 1;	session->session_alive = 0;	signal_iscsi_threads(session);	kthread_stop(session->tx_task);	kthread_stop(session->rx_task);	if (session->socket)			iscsi_disconnect(session);	set_bit(SESSION_TERMINATED, &session->control_bits);	/*	 * wake up any ioctls sleeping on the session	 * (this will be needed when update_session is fixed)	 */	wake_up(&session->login_wait_q);	del_timer_sync(&session->session_timer);	/*	 * grab the config mutex to make sure update_session is not	 * accessing the session fields we are going to free	 */	down(&session->config_mutex);	free_session(session);	up(&session->config_mutex);}intiscsi_create_session(struct iscsi_session *session,		     struct iscsi_session_ioctl *ioctld){	int rc;	init_session_structure(session, ioctld);	session->preallocated_task = kmem_cache_alloc(iscsi_task_cache,						      GFP_KERNEL);	if (!session->preallocated_task) {		iscsi_host_err(session, "Couldn't preallocate task\n");		rc = -ENOMEM;		goto free_session;	}	session->mgmt_task = kmem_cache_alloc(iscsi_task_cache, GFP_KERNEL);	if (!session->mgmt_task) {		iscsi_host_err(session, "Couldn't preallocate mgmt task\n");		rc = -ENOMEM;		goto free_session;	}	memset(session->mgmt_task, 0, sizeof(*session->mgmt_task));	iscsi_init_task(session->mgmt_task);	rc = copy_iscsi_strings(session, ioctld);	if (rc)		goto free_session;	memcpy(session->isid, ioctld->isid, sizeof(session->isid));	/*	 * FIXME: Do we have to check on both the username_in and	 * password_length_in. Same with iscsi_update_session as well? Smitha	 */	if (ioctld->username_in[0] || ioctld->password_length_in)		session->bidirectional_auth = 1;	else		session->bidirectional_auth = 0;	rc = alloc_auth_buffers(session);	if (rc)		goto free_session;	memcpy(&session->portal, &ioctld->portal, sizeof(ioctld->portal));	iscsi_set_portal(session);	/*	 * preallocate rx/tx_tfm, so that we do not have to possibly	 * call crypto_alloc_tfm (it uses GFP_KERNEL) while IO is queued.	 */	session->rx_tfm = crypto_alloc_tfm("crc32c", 0);	if (!session->rx_tfm) {		rc = -ENOMEM;		goto free_session;	}	session->tx_tfm = crypto_alloc_tfm("crc32c", 0);	if (!session->tx_tfm) {		rc = -ENOMEM;		goto free_session;	}	init_timer(&session->replacement_timer);	session->replacement_timer.data = (unsigned long)session;	session->replacement_timer.function = replacement_timed_out;	rc = start_session_threads(session);	up(&session->config_mutex);	if (rc)		goto free_session;	down(&iscsi_session_sem);	list_add_tail(&session->list, &iscsi_sessions);	up(&iscsi_session_sem);	wait_event_interruptible(session->login_wait_q,		test_bit(SESSION_ESTABLISHED, &session->control_bits));	if (!test_bit(SESSION_ESTABLISHED, &session->control_bits)) {		iscsi_destroy_session(session);		return -ENOTCONN;	}	return 0;	 free_session:	free_session(session);	return rc;}struct iscsi_session *iscsi_find_session(const char *target_name, u8 isid[6], int tpgt){	struct iscsi_session *session;	down(&iscsi_session_sem);	list_for_each_entry(session, &iscsi_sessions, list) {		 if (!strcmp(session->target_name, target_name) &&		     !memcmp(session->isid, isid, sizeof(session->isid)) &&		     session->portal_group_tag == tpgt) {			if (scsi_host_get(session->shost)) {				up(&iscsi_session_sem);				return session;			}			break;		}	}	up(&iscsi_session_sem);	return NULL;}intiscsi_update_address(struct iscsi_session *session, char *address){	struct sockaddr_in *addr;	char *tag;	char *port;	int err = 1;	tag = strrchr(address, ',');	if (tag) {		*tag = '\0';		tag++;	}	port = strrchr(address, ':');	if (port) {		*port = '\0';		port++;	}	/*	 * Still only ipv4 is supported. No access to ipv6	 * to test so feel free to implement it later...	 */	if (address[0] == '[') {		iscsi_host_err(session, "Driver does not support ipv6 "			       "addresses\n");		err = 0;		goto done;	}	addr = (struct sockaddr_in *)&session->addr;	addr->sin_addr.s_addr = in_aton(address);	if (port)		addr->sin_port = htons(simple_strtoul(port, NULL, 0));	else		addr->sin_port = htons(ISCSI_LISTEN_PORT); done:	/* restore the original strings */	if (tag) {		--tag;		*tag = ',';	}	if (port) {		--port;		*port = ':';	}	return err;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?