Exemplo n.º 1
0
static void drop_data(struct nlmsghdr *nlh)
{
	int ev_size;

	ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr));
	nlpayload_read(ctrl_fd, NULL, ev_size, 0);
}
Exemplo n.º 2
0
static int
__ksession_call(int ctrl_fd, void *iov_base, int iov_len)
{
	int rc;
	struct iovec iov;
	struct iscsi_uevent *ev = iov_base;
	iscsi_uevent_e type = ev->type;

	iov.iov_base = iov_base;
	iov.iov_len = iov_len;

	if ((rc = ctldev_writev(ctrl_fd, type, &iov, 1)) < 0) {
		return rc;
	}

	do {
		if ((rc = nlpayload_read(ctrl_fd, (void*)ev,
					 sizeof(*ev), MSG_PEEK)) < 0) {
			return rc;
		}
		if (ev->type != type) {
			/*
			 * receive and queue async. event which as of
			 * today could be:
			 *	- CNX_ERROR
			 *	- RECV_PDU
			 */
			ctldev_handle(ctrl_fd);
		} else {
			if ((rc = nlpayload_read(ctrl_fd, (void*)ev,
						 sizeof(*ev), 0)) < 0) {
				return rc;
			}
			break;
		}
	} while (ev->type != type);

	return rc;
}
Exemplo n.º 3
0
static int
kget_stats(uint64_t transport_handle, uint32_t sid, uint32_t cid,
	   char *statsbuf, int statsbuf_max)
{
	int rc;
	int ev_size;
	struct iscsi_uevent ev;
	char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))];
	struct nlmsghdr *nlh;
	struct iovec iov[2];

	log_debug(7, "in %s", __FUNCTION__);

	memset(&ev, 0, sizeof(struct iscsi_uevent));

	ev.type = ISCSI_UEVENT_GET_STATS;
	ev.transport_handle = transport_handle;
	ev.u.get_stats.sid = sid;
	ev.u.get_stats.cid = cid;

	iov[1].iov_base = &ev;
	iov[1].iov_len = sizeof(ev);
	rc = __kipc_call(iov, 2);
	if (rc < 0)
		return rc;

	if ((rc = nl_read(ctrl_fd, nlm_ev,
		NLMSG_SPACE(sizeof(struct iscsi_uevent)), MSG_PEEK)) < 0) {
		log_error("can not read nlm_ev, error %d", rc);
		return rc;
	}
	nlh = (struct nlmsghdr *)nlm_ev;
	ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr));

	log_debug(6, "message real length is %d bytes", nlh->nlmsg_len);

	if (ev_size > statsbuf_max) {
		log_error("destanation buffer for statistics is "
			"not big enough to fit %d bytes", statsbuf_max);
		ev_size = statsbuf_max;
	}

	if ((rc = nlpayload_read(ctrl_fd, (void*)statsbuf, ev_size, 0)) < 0) {
		log_error("can not read from NL socket, error %d", rc);
		return rc;
	}

	return 0;
}
Exemplo n.º 4
0
static int kget_chap(uint64_t transport_handle, uint32_t host_no,
		     uint16_t chap_tbl_idx, uint32_t num_entries,
		     char *chap_buf, uint32_t *valid_chap_entries)
{
	int rc = 0;
	int ev_size;
	struct iscsi_uevent ev;
	struct iovec iov[2];
	char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))];
	struct nlmsghdr *nlh;

	memset(&ev, 0, sizeof(struct iscsi_uevent));

	ev.type = ISCSI_UEVENT_GET_CHAP;
	ev.transport_handle = transport_handle;
	ev.u.get_chap.host_no = host_no;
	ev.u.get_chap.chap_tbl_idx = chap_tbl_idx;
	ev.u.get_chap.num_entries = num_entries;

	iov[1].iov_base = &ev;
	iov[1].iov_len = sizeof(ev);
	rc = __kipc_call(iov, 2);
	if (rc < 0)
		return rc;

	if ((rc = nl_read(ctrl_fd, nlm_ev,
			  NLMSG_SPACE(sizeof(struct iscsi_uevent)),
			  MSG_PEEK)) < 0) {
		log_error("can not read nlm_ev, error %d", rc);
		return rc;
	}

	nlh = (struct nlmsghdr *)nlm_ev;
	ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr));

	if ((rc = nlpayload_read(ctrl_fd, (void *)chap_buf, ev_size, 0)) < 0) {
		log_error("can not read from NL socket, error %d", rc);
		return rc;
	}

	*valid_chap_entries = ev.u.get_chap.num_entries;

	return rc;
}
Exemplo n.º 5
0
static int kget_host_stats(uint64_t transport_handle, uint32_t host_no,
		     char *host_stats)
{
	int rc = 0;
	int ev_size;
	struct iscsi_uevent ev;
	struct iovec iov[2];
	char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))];
	struct nlmsghdr *nlh;

	memset(&ev, 0, sizeof(struct iscsi_uevent));

	ev.type = ISCSI_UEVENT_GET_HOST_STATS;
	ev.transport_handle = transport_handle;
	ev.u.get_host_stats.host_no = host_no;

	iov[1].iov_base = &ev;
	iov[1].iov_len = sizeof(ev);
	rc = __kipc_call(iov, 2);
	if (rc < 0)
		return rc;

	if ((rc = nl_read(ctrl_fd, nlm_ev,
			  NLMSG_SPACE(sizeof(struct iscsi_uevent)),
			  MSG_PEEK)) < 0) {
		log_error("can not read nlm_ev, error %d", rc);
		return rc;
	}

	nlh = (struct nlmsghdr *)nlm_ev;
	ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr));

	if ((rc = nlpayload_read(ctrl_fd, (void *)host_stats,
				 ev_size, 0)) < 0) {
		log_error("can not read from NL socket, error %d", rc);
		return rc;
	}

	return rc;
}
Exemplo n.º 6
0
/*
 * __kipc_call() should never block. Therefore
 * Netlink's xmit logic is serialized. This means we do not allocate on
 * xmit path. Instead we reuse nlm_sendbuf buffer.
 *
 * Transport must assure non-blocking operations for:
 *
 *	- session_create()
 *	- conn_create()
 *	- conn_bind()
 *	_ set_param()
 *	- conn_start()
 *	- conn_stop()
 *
 * Its OK to block for cleanup for short period of time in operatations for:
 *
 *	- conn_destroy()
 *	- session_destroy()
 *
 * FIXME: interface needs to be extended to allow longer blocking on
 *        cleanup. (Dima)
 */
static int
__kipc_call(struct iovec *iovp, int count)
{
	int rc, iferr;
	struct iscsi_uevent *ev = iovp[1].iov_base;
	enum iscsi_uevent_e type = ev->type;

	log_debug(7, "in %s", __FUNCTION__);

	rc = kwritev(type, iovp, count);

	do {
		if ((rc = nlpayload_read(ctrl_fd, (void*)ev,
					 sizeof(*ev), MSG_PEEK)) < 0) {
			return rc;
		}
		if (ev->type != type) {
			log_debug(1, "expecting event %d, got %d, handling...",
				  type, ev->type);
			if (ev->type == ISCSI_KEVENT_IF_ERROR) {
				if ((rc = nlpayload_read(ctrl_fd, (void*)ev,
							 sizeof(*ev), 0)) < 0) {
					return rc;
				}
				/*
				 * iferror is u32, but the kernel returns
				 * negative errno values for errors.
				 */
				iferr = ev->iferror;

				if (iferr == -ENOSYS)
					/* not fatal so let caller handle log */
					log_debug(1, "Received iferror %d: %s.",
						  iferr, strerror(-iferr));
				else if (iferr < 0)
					log_error("Received iferror %d: %s.",
						   iferr, strerror(-iferr));
				else
					log_error("Received iferror %d.",
						   iferr);
				return ev->iferror;
			}
			/*
			 * receive and queue async. event which as of
			 * today could be:
			 *	- CONN_ERROR
			 *	- RECV_PDU
			 */
			ctldev_handle();
		} else if (ev->type == ISCSI_UEVENT_GET_STATS) {
			/* kget_stats() will read */
			return 0;
		} else if (ev->type == ISCSI_UEVENT_GET_CHAP) {
			/* kget_chap() will read */
			return 0;
		} else if (ev->type == ISCSI_UEVENT_GET_HOST_STATS) {
			/* kget_host_stats() will read */
			return 0;

		} else {
			if ((rc = nlpayload_read(ctrl_fd, (void*)ev,
						 sizeof(*ev), 0)) < 0) {
				return rc;
			}
			break;
		}
	} while (ev->type != type);

	return rc;
}
Exemplo n.º 7
0
static int ctldev_handle(void)
{
	int rc, ev_size;
	struct iscsi_uevent *ev;
	iscsi_session_t *session = NULL;
	iscsi_conn_t *conn = NULL;
	char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))];
	struct nlmsghdr *nlh;
	struct iscsi_ev_context *ev_context;
	uint32_t sid = 0, cid = 0;

	log_debug(7, "in %s", __FUNCTION__);

	if ((rc = nl_read(ctrl_fd, nlm_ev,
		NLMSG_SPACE(sizeof(struct iscsi_uevent)), MSG_PEEK)) < 0) {
		log_error("can not read nlm_ev, error %d", rc);
		return rc;
	}
	nlh = (struct nlmsghdr *)nlm_ev;
	ev = (struct iscsi_uevent *)NLMSG_DATA(nlm_ev);

	log_debug(7, "%s got event type %u", __FUNCTION__, ev->type);
	/* drivers like qla4xxx can be inserted after iscsid is started */
	switch (ev->type) {
	case ISCSI_KEVENT_CREATE_SESSION:
	/* old kernels sent ISCSI_UEVENT_CREATE_SESSION on creation */
	case ISCSI_UEVENT_CREATE_SESSION:
		drop_data(nlh);
		if (!ipc_ev_clbk)
			return 0;

		if (ipc_ev_clbk->create_session)
			ipc_ev_clbk->create_session(ev->r.c_session_ret.host_no,
						    ev->r.c_session_ret.sid);
		return 0;
	case ISCSI_KEVENT_DESTROY_SESSION:
		drop_data(nlh);
		if (!ipc_ev_clbk)
			return 0;

		if (ipc_ev_clbk->destroy_session)
			ipc_ev_clbk->destroy_session(ev->r.d_session.host_no,
						     ev->r.d_session.sid);
		return 0;
	case ISCSI_KEVENT_RECV_PDU:
		sid = ev->r.recv_req.sid;
		cid = ev->r.recv_req.cid;
		break;
	case ISCSI_KEVENT_CONN_ERROR:
		sid = ev->r.connerror.sid;
		cid = ev->r.connerror.cid;
		break;
	case ISCSI_KEVENT_CONN_LOGIN_STATE:
		sid = ev->r.conn_login.sid;
		cid = ev->r.conn_login.cid;
		break;
	case ISCSI_KEVENT_UNBIND_SESSION:
		sid = ev->r.unbind_session.sid;
		/* session wide event so cid is 0 */
		cid = 0;
		break;
	case ISCSI_KEVENT_HOST_EVENT:
		switch (ev->r.host_event.code) {
		case ISCSI_EVENT_LINKUP:
			log_warning("Host%u: Link Up.",
				    ev->r.host_event.host_no);
			break;
		case ISCSI_EVENT_LINKDOWN:
			log_warning("Host%u: Link Down.",
				    ev->r.host_event.host_no);
			break;
		default:
			log_debug(7, "Host%u: Unknown host event: %u.",
				  ev->r.host_event.host_no,
				  ev->r.host_event.code);
		}

		drop_data(nlh);
		return 0;
	case ISCSI_KEVENT_PING_COMP:
		ping_event.host_no = ev->r.ping_comp.host_no;
		ping_event.pid = ev->r.ping_comp.pid;
		ping_event.status = ev->r.ping_comp.status;
		ping_event.active = 1;

		drop_data(nlh);
		return 0;
	default:
		if ((ev->type > ISCSI_UEVENT_MAX && ev->type < KEVENT_BASE) ||
		    (ev->type > ISCSI_KEVENT_MAX))
			log_error("Unknown kernel event %d. You may want to "
				  " upgrade your iscsi tools.", ev->type);
		else
			/*
			 * If another app is using the interface we might
			 * see their
			 * stuff. Just drop it.
			 */
			log_debug(7, "Got unknown event %d. Dropping.",
				  ev->type);
		drop_data(nlh);
		return 0;
	}

	/* verify connection */
	session = session_find_by_sid(sid);
	if (!session) {
		/*
		 * this can happen normally when other apps are using the
		 * nl interface.
		 */
		log_debug(1, "Could not verify connection %d:%d. Dropping "
			   "event.", sid, cid);
		drop_data(nlh);
		return -ENXIO;
	}
	conn = &session->conn[0];

	ev_size = nlh->nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr));

	ev_context = ipc_ev_clbk->get_ev_context(conn, ev_size);
	if (!ev_context) {
		log_error("Can not allocate memory for receive context.");
		drop_data(nlh);
		return -ENOMEM;
	}

	log_debug(6, "message real length is %d bytes, recv_handle %p",
		nlh->nlmsg_len, ev_context->data);

	if ((rc = nlpayload_read(ctrl_fd, ev_context->data,
				ev_size, 0)) < 0) {
		ipc_ev_clbk->put_ev_context(ev_context);
		log_error("can not read from NL socket, error %d", rc);
		/* retry later */
		return rc;
	}

	/*
	 * we sched these events because the handlers could call back
	 * into ctldev_handle
	 */
	switch (ev->type) {
	case ISCSI_KEVENT_RECV_PDU:
		rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0,
						   EV_CONN_RECV_PDU);
		break;
	case ISCSI_KEVENT_CONN_ERROR:
		memcpy(ev_context->data, &ev->r.connerror.error,
			sizeof(ev->r.connerror.error));
		rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0,
						   EV_CONN_ERROR);
		break;
	case ISCSI_KEVENT_CONN_LOGIN_STATE:
		memcpy(ev_context->data, &ev->r.conn_login.state,
			sizeof(ev->r.conn_login.state));
		rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0,
						   EV_CONN_LOGIN);
		break;
	case ISCSI_KEVENT_UNBIND_SESSION:
		rc = ipc_ev_clbk->sched_ev_context(ev_context, conn, 0,
						   EV_CONN_STOP);
		break;
	default:
		ipc_ev_clbk->put_ev_context(ev_context);
		log_error("unknown kernel event %d", ev->type);
		return -EEXIST;
	}

	if (rc)
		ipc_ev_clbk->put_ev_context(ev_context);
	return rc;
}
Exemplo n.º 8
0
int
ctldev_handle(int ctrl_fd)
{
	int rc;
	struct iscsi_uevent *ev;
	struct qelem *item;
	uiscsi_session_t *session = NULL;
	uiscsi_conn_t *conn = NULL;
	unsigned long recv_handle;
	struct nlmsghdr nlh;
	int ev_size;

	if ((rc = nl_read(ctrl_fd, &nlh, MSG_PEEK)) < 0) {
		log_error("can not read nlmsghdr, error %d", rc);
		return rc;
	}

	ev_size = nlh.nlmsg_len - NLMSG_ALIGN(sizeof(struct nlmsghdr));
	recv_handle = (unsigned long)calloc(1, ev_size);
	if (!recv_handle) {
		log_error("can not allocate memory for receive handle");
		return -ENOMEM;
	}

	log_debug(6, "message real length is %d bytes, recv_handle %p",
		nlh.nlmsg_len, (void*)recv_handle);

	if ((rc = nlpayload_read(ctrl_fd, (void*)recv_handle,
				ev_size, 0)) < 0) {
		log_error("can not read from NL socket, error %d", rc);
		return rc;
	}
	ev = (struct iscsi_uevent *)recv_handle;

	/* verify connection */
	item = provider[0].sessions.q_forw;
	while (item != &provider[0].sessions) {
		int i;
		session = (uiscsi_session_t *)item;
		for (i=0; i<ISCSI_CNX_MAX; i++) {
			if (&session->cnx[i] == (uiscsi_conn_t*)
					iscsi_ptr(ev->r.recv_req.cnx_handle) ||
			    &session->cnx[i] == (uiscsi_conn_t*)
					iscsi_ptr(ev->r.cnxerror.cnx_handle)) {
				conn = &session->cnx[i];
				break;
			}
		}
		item = item->q_forw;
	}

	if (ev->type == ISCSI_KEVENT_RECV_PDU) {
		if (conn == NULL) {
			log_error("could not verify connection 0x%p for "
				  "event RECV_PDU", conn);
			return -ENXIO;
		}

		/* produce an event, so session manager will handle */
		queue_produce(session->queue, EV_CNX_RECV_PDU, conn,
			sizeof(unsigned long), &recv_handle);
		actor_schedule(&session->mainloop);

	} else if (ev->type == ISCSI_KEVENT_CNX_ERROR) {
		if (conn == NULL) {
			log_error("could not verify connection 0x%p for "
				  "event CNX_ERR", conn);
			return -ENXIO;
		}

		/* produce an event, so session manager will handle */
		queue_produce(session->queue, EV_CNX_ERROR, conn,
			sizeof(unsigned long), (void*)&ev->r.cnxerror.error);
		actor_schedule(&session->mainloop);

	} else {
		log_error("unknown kernel event %d", ev->type);
		return -EEXIST;
	}

	return 0;
}