-/* We only send shutdown messages to nodes that are not part of the cluster */
-static void sctp_send_shutdown(sctp_assoc_t associd)
-{
- static char outcmsg[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
- struct msghdr outmessage;
- struct cmsghdr *cmsg;
- struct sctp_sndrcvinfo *sinfo;
- int ret;
- struct connection *con;
-
- con = nodeid2con(0,0);
- BUG_ON(con == NULL);
-
- outmessage.msg_name = NULL;
- outmessage.msg_namelen = 0;
- outmessage.msg_control = outcmsg;
- outmessage.msg_controllen = sizeof(outcmsg);
- outmessage.msg_flags = MSG_EOR;
-
- cmsg = CMSG_FIRSTHDR(&outmessage);
- cmsg->cmsg_level = IPPROTO_SCTP;
- cmsg->cmsg_type = SCTP_SNDRCV;
- cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
- outmessage.msg_controllen = cmsg->cmsg_len;
- sinfo = CMSG_DATA(cmsg);
- memset(sinfo, 0x00, sizeof(struct sctp_sndrcvinfo));
-
- sinfo->sinfo_flags |= MSG_EOF;
- sinfo->sinfo_assoc_id = associd;
-
- ret = kernel_sendmsg(con->sock, &outmessage, NULL, 0, 0);
-
- if (ret != 0)
- log_print("send EOF to node failed: %d", ret);
-}
-
-static void sctp_init_failed_foreach(struct connection *con)
-{
-
- /*
- * Don't try to recover base con and handle race where the
- * other node's assoc init creates a assoc and we get that
- * notification, then we get a notification that our attempt
- * failed due. This happens when we are still trying the primary
- * address, but the other node has already tried secondary addrs
- * and found one that worked.
- */
- if (!con->nodeid || con->sctp_assoc)
- return;
-
- log_print("Retrying SCTP association init for node %d\n", con->nodeid);
-
- con->try_new_addr = true;
- con->sctp_assoc = 0;
- if (test_and_clear_bit(CF_INIT_PENDING, &con->flags)) {
- if (!test_and_set_bit(CF_WRITE_PENDING, &con->flags))
- queue_work(send_workqueue, &con->swork);
- }
-}
-
-/* INIT failed but we don't know which node...
- restart INIT on all pending nodes */
-static void sctp_init_failed(void)
-{
- mutex_lock(&connections_lock);
-
- foreach_conn(sctp_init_failed_foreach);
-
- mutex_unlock(&connections_lock);
-}
-
-static void retry_failed_sctp_send(struct connection *recv_con,
- struct sctp_send_failed *sn_send_failed,
- char *buf)
-{
- int len = sn_send_failed->ssf_length - sizeof(struct sctp_send_failed);
- struct dlm_mhandle *mh;
- struct connection *con;
- char *retry_buf;
- int nodeid = sn_send_failed->ssf_info.sinfo_ppid;
-
- log_print("Retry sending %d bytes to node id %d", len, nodeid);
-
- if (!nodeid) {
- log_print("Shouldn't resend data via listening connection.");
- return;
- }
-
- con = nodeid2con(nodeid, 0);
- if (!con) {
- log_print("Could not look up con for nodeid %d\n",
- nodeid);
- return;
- }
-
- mh = dlm_lowcomms_get_buffer(nodeid, len, GFP_NOFS, &retry_buf);
- if (!mh) {
- log_print("Could not allocate buf for retry.");
- return;
- }
- memcpy(retry_buf, buf + sizeof(struct sctp_send_failed), len);
- dlm_lowcomms_commit_buffer(mh);
-
- /*
- * If we got a assoc changed event before the send failed event then
- * we only need to retry the send.
- */
- if (con->sctp_assoc) {
- if (!test_and_set_bit(CF_WRITE_PENDING, &con->flags))
- queue_work(send_workqueue, &con->swork);
- } else
- sctp_init_failed_foreach(con);
-}
-
-/* Something happened to an association */
-static void process_sctp_notification(struct connection *con,
- struct msghdr *msg, char *buf)
-{
- union sctp_notification *sn = (union sctp_notification *)buf;
- struct linger linger;
-
- switch (sn->sn_header.sn_type) {
- case SCTP_SEND_FAILED:
- retry_failed_sctp_send(con, &sn->sn_send_failed, buf);
- break;
- case SCTP_ASSOC_CHANGE:
- switch (sn->sn_assoc_change.sac_state) {
- case SCTP_COMM_UP:
- case SCTP_RESTART:
- {
- /* Check that the new node is in the lockspace */
- struct sctp_prim prim;
- int nodeid;
- int prim_len, ret;
- int addr_len;
- struct connection *new_con;
-
- /*
- * We get this before any data for an association.
- * We verify that the node is in the cluster and
- * then peel off a socket for it.
- */
- if ((int)sn->sn_assoc_change.sac_assoc_id <= 0) {
- log_print("COMM_UP for invalid assoc ID %d",
- (int)sn->sn_assoc_change.sac_assoc_id);
- sctp_init_failed();
- return;
- }
- memset(&prim, 0, sizeof(struct sctp_prim));
- prim_len = sizeof(struct sctp_prim);
- prim.ssp_assoc_id = sn->sn_assoc_change.sac_assoc_id;
-
- ret = kernel_getsockopt(con->sock,
- IPPROTO_SCTP,
- SCTP_PRIMARY_ADDR,
- (char*)&prim,
- &prim_len);
- if (ret < 0) {
- log_print("getsockopt/sctp_primary_addr on "
- "new assoc %d failed : %d",
- (int)sn->sn_assoc_change.sac_assoc_id,
- ret);
-
- /* Retry INIT later */
- new_con = assoc2con(sn->sn_assoc_change.sac_assoc_id);
- if (new_con)
- clear_bit(CF_CONNECT_PENDING, &con->flags);
- return;
- }
- make_sockaddr(&prim.ssp_addr, 0, &addr_len);
- if (addr_to_nodeid(&prim.ssp_addr, &nodeid)) {
- unsigned char *b=(unsigned char *)&prim.ssp_addr;
- log_print("reject connect from unknown addr");
- print_hex_dump_bytes("ss: ", DUMP_PREFIX_NONE,
- b, sizeof(struct sockaddr_storage));
- sctp_send_shutdown(prim.ssp_assoc_id);
- return;
- }
-
- new_con = nodeid2con(nodeid, GFP_NOFS);
- if (!new_con)
- return;
-
- /* Peel off a new sock */
- lock_sock(con->sock->sk);
- ret = sctp_do_peeloff(con->sock->sk,
- sn->sn_assoc_change.sac_assoc_id,
- &new_con->sock);
- release_sock(con->sock->sk);
- if (ret < 0) {
- log_print("Can't peel off a socket for "
- "connection %d to node %d: err=%d",
- (int)sn->sn_assoc_change.sac_assoc_id,
- nodeid, ret);
- return;
- }
- add_sock(new_con->sock, new_con);
-
- linger.l_onoff = 1;
- linger.l_linger = 0;
- ret = kernel_setsockopt(new_con->sock, SOL_SOCKET, SO_LINGER,
- (char *)&linger, sizeof(linger));
- if (ret < 0)
- log_print("set socket option SO_LINGER failed");
-
- log_print("connecting to %d sctp association %d",
- nodeid, (int)sn->sn_assoc_change.sac_assoc_id);
-
- new_con->sctp_assoc = sn->sn_assoc_change.sac_assoc_id;
- new_con->try_new_addr = false;
- /* Send any pending writes */
- clear_bit(CF_CONNECT_PENDING, &new_con->flags);
- clear_bit(CF_INIT_PENDING, &new_con->flags);
- if (!test_and_set_bit(CF_WRITE_PENDING, &new_con->flags)) {
- queue_work(send_workqueue, &new_con->swork);
- }
- if (!test_and_set_bit(CF_READ_PENDING, &new_con->flags))
- queue_work(recv_workqueue, &new_con->rwork);
- }
- break;
-
- case SCTP_COMM_LOST:
- case SCTP_SHUTDOWN_COMP:
- {
- con = assoc2con(sn->sn_assoc_change.sac_assoc_id);
- if (con) {
- con->sctp_assoc = 0;
- }
- }
- break;
-
- case SCTP_CANT_STR_ASSOC:
- {
- /* Will retry init when we get the send failed notification */
- log_print("Can't start SCTP association - retrying");
- }
- break;
-
- default:
- log_print("unexpected SCTP assoc change id=%d state=%d",
- (int)sn->sn_assoc_change.sac_assoc_id,
- sn->sn_assoc_change.sac_state);
- }
- default:
- ; /* fall through */
- }
-}
-