static bool persistent_spc2_release_lun(pr_context_t *context, int session_id, lun_t lun, scsi_command_status_t *scsi_status) { pr_info_t *pr_info = &context->pr_info[lun]; EXA_ASSERT(session_id < MAX_GLOBAL_SESSION); if (!can_use_spc2_reserve(context, lun, session_id)) { exalog_warning("iSCSI PR conflict: session %i cannot release SPC-2 " "reservation on LUN %" PRIlun ", SPC-2 reserve not " "possible on this LUN", session_id, lun); return false; } if (pr_info->spc2_reserve != SPC2_RESERVE_NONE && pr_info->spc2_reserve != session_id) { exalog_warning("iSCSI PR conflict: session %i cannot release SPC-2 " "reservation on LUN %" PRIlun ", LUN already reserved " "by %i", session_id, lun, pr_info->spc2_reserve); return false; } pr_info->spc2_reserve = SPC2_RESERVE_NONE; exalog_debug("iSCSI PR: session %i released SPC-2 reservation on LUN %" PRIlun, session_id, lun); return true; }
/** * Thread responsible for accepting connections * * It's a separate thread because we accept do some memory allocation and we * must avoid that in recv thread. * * @param unused Unused parameter */ static void accept_thread(void *unused) { exalog_as(EXAMSG_ISCSI_ID); while (algopr_run) { exa_nodeid_t node_id; const char *ip_addr; struct sockaddr_in client_address; int size = sizeof(client_address); int sock = os_accept(eth.accept_sock, (struct sockaddr *)&client_address, &size); if (sock < 0) continue; /* it's a false accept */ ip_addr = os_inet_ntoa(client_address.sin_addr); if (!suspended) { exalog_warning("Closing incoming connection from %s while not" " suspended.", ip_addr); __close_socket(sock); continue; } internal_setsock_opt(sock, SOCK_FLAGS); node_id = get_peer_id_from_ip_addr(ip_addr); if (!EXA_NODEID_VALID(node_id)) { exalog_warning("Closing incoming connection from unknown node %s.", ip_addr); __close_socket(sock); continue; } set_peer_socket(node_id, ip_addr, sock); } }
static bool persistent_reserve_lun(pr_context_t *context, int session_id, lun_t lun, pr_key_t reservation_key, pr_type_t access_type) { pr_info_t *pr_info = &context->pr_info[lun]; EXA_ASSERT(session_id < MAX_GLOBAL_SESSION); /* lun have already have a persistent reserve key and we are not a * reservation lun holder */ if (is_lun_reserved(context, lun) && !persistent_is_holder(context, session_id, lun)) { exalog_warning("iSCSI PR conflict: session %i cannot change LUN %" PRIlun " reservation, already reserved by another " "session", session_id, lun); return false; } /* lun have already have a persistent reserve key and we are a * reservation lun holder and we change access type */ if (is_lun_reserved(context, lun) && persistent_is_holder(context, session_id, lun) && pr_info->reservation_type != access_type) { exalog_warning("iSCSI PR conflict: session %i cannot change LUN %" PRIlun " reservation, reservation type mismatch (" "received %02x / current %02x)", session_id, lun, access_type, pr_info->reservation_type); return false; } pr_info->reservation_type = access_type; pr_info_set_holder(pr_info, session_id); exalog_debug("iSCSI PR: session %i got a reservation on LUN %" PRIlun, session_id, lun); return true; }
static int check_registration(const pr_context_t *context, int session_id, lun_t lun, pr_key_t reservation_key, int service_action) { const pr_info_t *pr_info = &context->pr_info[lun]; bool is_registered = pr_info_is_registered(pr_info, session_id); pr_key_t current_key = pr_info_get_registration_key(pr_info, session_id); EXA_ASSERT(session_id < MAX_GLOBAL_SESSION); /* are we registered with reservation_key ? * * FIXME: "is_registered" test added for intel iSCSI test */ if (service_action != PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY && is_registered && current_key != reservation_key) { exalog_warning("iSCSI PR conflict: " "session %i registration check on LUN %" PRIlun " failed" ", registration key mismatch" " (received %" PRIu64 " / current %" PRIu64 ")", session_id, lun, reservation_key, current_key); return SCSI_STATUS_RESERVATION_CONFLICT; } if (service_action != PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY && service_action != PR_OUT_REGISTER && !is_registered) { exalog_warning("iSCSI PR conflict: " "session %i registration check on LUN %" PRIlun " failed" ", session not registered", session_id, lun); return SCSI_STATUS_RESERVATION_CONFLICT; } return SCSI_STATUS_GOOD; }
int exa_perf_instance_static_init(void) { const char *perf_config; exaperf_err_t err; eh = exaperf_alloc(); if (eh == NULL) { exalog_error("Failed initializing exaperf"); return -ENOMEM; } perf_config = getenv("EXANODES_PERF_CONFIG"); if (perf_config == NULL) { exalog_debug("No perf config set"); return 0; } /* initialize the component */ err = exaperf_init(eh, perf_config, exa_perf_instance_print); switch (err) { case EXAPERF_SUCCESS: exalog_info("Loaded perf config '%s'", perf_config); return 0; case EXAPERF_CONF_FILE_OPEN_FAILED: exalog_warning("Perf config '%s' not found, ignored", perf_config); exaperf_free(eh); eh = NULL; return 0; default: /* FIXME Use error string instead of error code */ exalog_error("Failed loading perf config '%s' (%d)", perf_config, err); return -EINVAL; } }
/** \brief Initialization of examsgd daemon. * * Command line must contain the node name and the interface name to use * for control messages. * * Accepts hidden option -d (debugging mode). * * \param[in] argc Argument count. * \param[in] argv Array of argument values. * * \return exit code. */ int daemon_init(int argc, char *argv[]) { int s; /* getopt variables */ static struct option long_opts[] = { { "cluster-id", required_argument, NULL, 'c' }, { "help", no_argument, NULL, 'h' }, { "hostname", required_argument, NULL, 'N' }, { "incarnation", required_argument, NULL, 'I' }, { "mcast-addr", required_argument, NULL, 'm' }, { "mcast-port", required_argument, NULL, 'p' }, { "node-id", required_argument, NULL, 'i' }, { "node-name", required_argument, NULL, 'n' }, { "stats", no_argument, NULL, 's' }, { NULL, 0, NULL, 0 } }; int long_idx, c; char *e; extern char *optarg; extern int optind; /* configurable options and default values */ const char *node_name = NULL; const char *hostname = NULL; const char *mgroup = EXAMSG_MCASTIP; unsigned short mport = EXAMSG_PORT; unsigned short inca = 0; exa_uuid_t cluster_uuid; exa_nodeid_t nodeid; bool err = false; uuid_zero(&cluster_uuid); nodeid = EXA_NODEID_NONE; /* options parsing */ while ((c = os_getopt_long(argc, argv, "c:dhi:I:m:n:N:p:s", long_opts, &long_idx)) != -1) switch (c) { case 'c': if (uuid_scan(optarg, &cluster_uuid) < 0) { fprintf(stderr, "invalid cluster id: '%s'\n", optarg); return -EINVAL; } break; case 'i': nodeid = (exa_nodeid_t)strtol(optarg, &e, 10); if (*e || !EXA_NODEID_VALID(nodeid)) { fprintf(stderr, "invalid node id: '%s'\n", optarg); return -EINVAL; } break; case 'I': inca = (unsigned short)strtol(optarg, &e, 10); if (*e || inca == 0) { fprintf(stderr, "invalid incarnation: '%s'\n", optarg); return -EINVAL; } break; /* multicast group */ case 'm': mgroup = optarg; break; case 'n': node_name = optarg; break; /* hostname */ case 'N': hostname = optarg; break; /* communication port */ case 'p': mport = strtol(optarg, &e, 0); if (*e != '\0') { fprintf(stderr, "invalid port number '%s'\n", optarg); return -EINVAL; } break; case 's': examsg_show_stats(); return 0; break; /* usage */ case 'h': case '?': default: usage(argv[0]); return -EINVAL; } if (uuid_is_zero(&cluster_uuid)) { fprintf(stderr, "missing cluster id\n"); err = true; } if (nodeid == EXA_NODEID_NONE) { fprintf(stderr, "missing node id\n"); err = true; } if (node_name == NULL) { fprintf(stderr, "missing node name\n"); err = true; } if (hostname == NULL) { fprintf(stderr, "missing hostname\n"); err = true; } if (inca == 0) { fprintf(stderr, "missing incarnation\n"); err = true; } if (err) return -EINVAL; /* Get cluster id, number of nodes, node id, node name and interface parameters */ if (argc - optind != 0) { fprintf(stderr, "stray parameters\n"); usage(argv[0]); return -EINVAL; } signal(SIGTERM, sig_term); signal(SIGINT, sig_term); s = examsg_static_init(EXAMSG_STATIC_GET); if (s) { fprintf(stderr, "Can't initialize messaging layer."); return s; } exalog_static_init(); /* Log as exa_msgd by default */ exalog_as(EXAMSG_CMSGD_ID); #ifdef USE_YAOURT if (yaourt_init()) exalog_debug("Yaourt: Examsgd init OK"); else exalog_warning("Yaourt: Examsgd init FAILED (%s)", yaourt_error); #endif /* set up network communication */ return startup(&cluster_uuid, node_name, hostname, nodeid, mgroup, mport, inca); }
/* * thread responsible for receiving data for a client or a server * note when we add client, this client is effectively added in the receive queue * only few second later due to the select timeout of 3 seconds * and there are the same problem for the deleteion of a client */ static void algopr_receive_thread(void *unused) { struct pending_request pending_requests[EXA_MAX_NODES_NUMBER]; exa_select_handle_t *sh = exa_select_new_handle(); int i; int ret; payload_t *payload = NULL; struct nbd_root_list root_list_recv; /* FIXME: handle the case when we have more than 1024 open file (limit of fd_set) */ fd_set fds; exalog_as(EXAMSG_ISCSI_ID); nbd_init_root(EXA_MAX_NODES_NUMBER, sizeof(payload_t), &root_list_recv); for (i = 0; i < EXA_MAX_NODES_NUMBER; i++) request_reset(&pending_requests[i]); while (algopr_run) { int nfds = 0; FD_ZERO(&fds); /* if one node is added or deleted, this deletion or addition are effective after this */ os_thread_mutex_lock(&peers_lock); for (i = 0; i < EXA_MAX_NODES_NUMBER; i++) { int fd_act = __get_peer_socket(i); if (fd_act < 0) { payload_t *temp_payload; temp_payload = request_reset(&pending_requests[i]); if (temp_payload != NULL) { if (pending_requests[i].big_buffer) nbd_list_post(ð.root_list_big_recv.free, temp_payload->buffer, -1); nbd_list_post(&root_list_recv.free, temp_payload, -1); } temp_payload = NULL; continue; } FD_SET(fd_act, &fds); nfds = fd_act > nfds ? fd_act : nfds; } os_thread_mutex_unlock(&peers_lock); ret = exa_select_in(sh, nfds + 1, &fds); if (ret != 0 && ret != -EFAULT) exalog_error("Select upon receive failed: %s (%d)", os_strerror(-ret), ret); os_thread_mutex_lock(&peers_lock); for (i = 0; i < EXA_MAX_NODES_NUMBER; i++) { struct pending_request *req; int fd_act; fd_act = __get_peer_socket(i); if (fd_act < 0 || !FD_ISSET(fd_act, &fds)) continue; req = &pending_requests[i]; /* WARNING payload is kept from an iteration of while loop to * another, so the variable MUST be global. */ /* FIXME Remove the nbdlist which is useless as we already know * that we NEED EXA_MAX_NODES_NUMBER payload_t elements to be able * to receive simultaneously from EXA_MAX_NODES_NUMBER nodes * FIXME the LISTWAIT flag below is WRONG because waiting here * would mean deadlock... hopefully there are enough elements, and * we never wait.... */ if (payload == NULL) { payload = nbd_list_remove(&root_list_recv.free, NULL, LISTWAIT); EXA_ASSERT(payload != NULL); } if (request_init_transfer(payload, req) == 1) payload = NULL; ret = request_receive(fd_act, req); if (ret == DATA_TRANSFER_NEED_BIG_BUFFER) { req->payload->buffer = nbd_list_remove(ð.root_list_big_recv.free, NULL, LISTWAIT); EXA_ASSERT(req->payload->buffer != NULL); req->big_buffer = true; /* here we just continue because it is forbidden to call * request_receive without passing into select (as sockets are * blocking, we may remain blocked on the recv of nothing) */ continue; } if (ret == DATA_TRANSFER_PENDING) continue; if (ret == DATA_TRANSFER_ERROR) { payload_t *temp_payload = request_reset(req); if (req->big_buffer) nbd_list_post(ð.root_list_big_recv.free, temp_payload->buffer, -1); nbd_list_post(&root_list_recv.free, temp_payload, -1); __disconnect_from_peer(i); if (!suspended) exalog_warning("Failed receiving from peer %" PRInodeid " (socket %d): transfer error.", i, fd_act); continue; } if (ret == DATA_TRANSFER_COMPLETE) { payload_t *_payload = request_reset(req); /* update data network checking data */ algopr_new_msg(_payload->payload, _payload->size1, _payload->buffer, _payload->size2); nbd_list_post(&root_list_recv.free, _payload, -1); } } os_thread_mutex_unlock(&peers_lock); } nbd_close_root(&root_list_recv); exa_select_delete_handle(sh); }
/** * Process a connection that has incoming data. * * \param[in] conn_id Connection id * \param[in] sock_fd Connection socket */ static void handle_inputdata(int conn_id, int sock_fd) { char *buffer = NULL; void *data = NULL; size_t data_size; adm_command_code_t cmd_code; const struct AdmCommand *command; exa_uuid_t cluster_uuid; cl_error_desc_t err_desc; int retval; /* Receive the xml tree parsed in message */ retval = receive(sock_fd, &buffer); if (retval < 0) { if (retval == -ECONNRESET || retval == -ECONNABORTED) exalog_debug("CONNECTION %d: An error occurred : %d [%s]", conn_id, retval, exa_error_msg(retval)); else exalog_error("Socket %d peer '%s': An error occurred : %s (%d)", sock_fd, cli_peer_from_fd(sock_fd), exa_error_msg(retval), retval); close_connection(conn_id); return; } /* Parse the tree we just received and get a newly allocated payload data */ xml_command_parse(buffer, &cmd_code, &cluster_uuid, &data, &data_size, &err_desc); /* buffer is now parsed, let's free it */ os_free(buffer); if (err_desc.code != EXA_SUCCESS) { /* No need to free data buffer if parsing returned an error */ exalog_error("Failed to parse command on socket %d (from peer '%s'): %s (%d)", sock_fd, cli_peer_from_fd(sock_fd), err_desc.msg, err_desc.code); cli_command_end_complete(sock_fd, &err_desc); return; } command = adm_command_find(cmd_code); EXA_ASSERT_VERBOSE(command, "Missing implementation of command #%d", cmd_code); connectlist[conn_id].uid = get_new_cmd_uid(); retval = send_command_to_evmgr(connectlist[conn_id].uid, command, &cluster_uuid, data, data_size); if (retval < 0) { if (retval == -EXA_ERR_ADM_BUSY) exalog_warning("Running command %s (request from %s) failed: %s", adm_command_find(cmd_code)->msg, cli_get_peername(connectlist[conn_id].uid), exa_error_msg(retval)); else exalog_error("Running command %s (request from %s) failed: %s (%d)", adm_command_find(cmd_code)->msg, cli_get_peername(connectlist[conn_id].uid), exa_error_msg(retval), retval); set_error(&err_desc, retval, NULL); cli_command_end_complete(sock_fd, &err_desc); /* the command was not scheduled, reset the uid */ connectlist[conn_id].uid = CMD_UID_INVALID; } os_free(data); }
static void persistent_preempt_lun(pr_context_t *context, int session_id, lun_t lun, pr_key_t reservation_key, pr_key_t service_action_key, pr_type_t access_type, scsi_command_status_t *scsi_status) { pr_info_t *pr_info = &context->pr_info[lun]; bool all_registrants = false; unsigned int i; int id; EXA_ASSERT(session_id < MAX_GLOBAL_SESSION); /* spc 3 figure 3 pg 80 5.6.10.4.1 */ if (pr_info->reservation_type == PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS || pr_info->reservation_type == PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) { all_registrants = true; } if (!all_registrants && service_action_key == 0) { exalog_warning("iSCSI PR failed: " "session %i cannot preempt reservation on LUN %" PRIlun ", operation not allowed", session_id, lun); SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_SENSE_ASC_INVALID_FIELD_IN_PARAMETER_LIST); return; } for (i = 0; i < MAX_REGISTRATIONS; i++) { pr_key_t key = pr_info->registrations[i].key; id = pr_info->registrations[i].session_id; /* spc3r20 5.6.10.4.3 paragraphe 2 b) expept I_T nexus used for this command * * FIXME: I think that this code unregister all the sessions that are * not the preempting one and that don't have the right registration key. */ if ((service_action_key == key || service_action_key == 0) && id != session_id && id != SESSION_ID_NONE) { pr_info->registrations[i].key = 0; pr_info->registrations[i].session_id = SESSION_ID_NONE; callback_send_sense_data(context, id, lun, SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_UNIT_ATTENTION, 0); } } exalog_debug("iSCSI PR: session %i preempt on LUN %" PRIlun, session_id, lun); SCSI_STATUS_OK(scsi_status, 0); /* if there are no reservation on lun, preempt don't put another * reservation */ if (!is_lun_reserved(context, lun)) return; if ((all_registrants && service_action_key == 0) || (!all_registrants && service_action_key == get_holder_key(context, lun))) { pr_info_set_holder(pr_info, session_id); pr_info->reservation_type = access_type; } }
static void persistent_release_lun(pr_context_t *context, int session_id, lun_t lun, pr_key_t reservation_key, int reservation_type, scsi_command_status_t *scsi_status) { int id; pr_info_t *pr_info = &context->pr_info[lun]; EXA_ASSERT(session_id < MAX_GLOBAL_SESSION); /* lun already have a persistent reserve key * and we are not a reservation lun holder */ if (is_lun_reserved(context, lun) && !persistent_is_holder(context, session_id, lun)) { exalog_warning("iSCSI PR conflict: " "session %i cannot release LUN %" PRIlun " reservation" ", it does not hold the reservation", session_id, lun); SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_RESERVATION_CONFLICT, 0, 0); return; } /* lun already have a persistent reserve key * and we are a reservation lun holder */ if (is_lun_reserved(context, lun) && persistent_is_holder(context, session_id, lun) && reservation_type != pr_info->reservation_type) { exalog_warning("iSCSI PR failed: " "session %i cannot release LUN %" PRIlun " reservation" ", reservation type mismatch (received %02x / current %02x)", session_id, lun, reservation_type, pr_info->reservation_type); SCSI_STATUS_ERROR( scsi_status, SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_SENSE_ASC_INVALID_RELEASE_OF_PERSISTENT_RESERVATION); return; } /* now all registrant reservation holder will be unregistered */ if (pr_info->reservation_type != PR_TYPE_WRITE_EXCLUSIVE && pr_info->reservation_type != PR_TYPE_EXCLUSIVE_ACCESS) { unsigned int i; for (i = 0; i < MAX_REGISTRATIONS; i++) { id = pr_info->registrations[i].session_id; /* FIXME: the session that triggers the release dont't get the * unregistration callback. I understand that as the session does * not need to to be signaled with an events it has triggered. */ if (id == SESSION_ID_NONE || id == session_id) continue; callback_send_sense_data(context, id, lun, SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_UNIT_ATTENTION, SCSI_SENSE_ASC_RESERVATIONS_RELEASED); } } /* no more reservation */ pr_info->reservation_type = PR_TYPE_NONE; pr_info->holder_index = MAX_REGISTRATIONS; exalog_debug("iSCSI PR: session %i released the reservation on LUN %" PRIlun, session_id, lun); SCSI_STATUS_OK(scsi_status, 0); }
void pr_reserve_out(pr_context_t *context, lun_t lun, const unsigned char *cdb, int session_id, scsi_command_status_t *scsi_status) { pr_info_t *pr_info = &context->pr_info[lun]; EXA_ASSERT(session_id < MAX_GLOBAL_SESSION); if (cdb[0] == RESERVE_6) { if (persistent_spc2_reserve_lun(context, session_id, lun, scsi_status)) SCSI_STATUS_OK(scsi_status, 0); else SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_RESERVATION_CONFLICT, 0, 0); return; } if (cdb[0] == RELEASE_6) { if (persistent_spc2_release_lun(context, session_id, lun, scsi_status)) SCSI_STATUS_OK(scsi_status, 0); else SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_RESERVATION_CONFLICT, 0, 0); return; } if (cdb[0] == PERSISTENT_RESERVE_OUT) { /* int len = get_bigendian32(cdb + 5); // FIXME: TODO: must be checked for all service action */ int pr_scope = cdb[2] >> 4; int pr_type = cdb[2] & 0xf; const unsigned char *param = cdb + SCSI_CDB_MAX_FIXED_LENGTH; pr_key_t reservation_key = get_bigendian64(param); pr_key_t service_action_key = get_bigendian64(param + 8); unsigned char flags = param[20]; unsigned char spec_i_pt = (flags >> 3) & 1; /* unsigned char all_tg_pt = (flags >>2) & 1 ; */ int service_action = cdb[1] & 0x1f; /* spc3r23 table 116 "allowed scope" */ if (pr_scope != PR_LU_SCOPE && service_action != PR_OUT_REGISTER && service_action != PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY && service_action != PR_OUT_CLEAR) { exalog_error("iSCSI PR failed: " "reserve operation (%i) from session %i on LUN %" PRIlun " is not allowed in this scope", service_action, session_id, lun); SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_SENSE_ASC_INVALID_FIELD_IN_CDB); return; } if (check_registration(context, session_id, lun, reservation_key, service_action) != SCSI_STATUS_GOOD) { SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_RESERVATION_CONFLICT, 0, 0); return; } switch (service_action) { case PR_OUT_REGISTER: if (spec_i_pt == 0) { persistent_register_lun(context, session_id, lun, service_action_key, scsi_status); break; } /* spec_i_pt == 1 ! */ if (reservation_key != 0) /* spec_i_pt == 1 and we are on a registered nexus */ { exalog_warning("iSCSI PR conflict: " "reserve operation (%i) from session %i on LUN %" PRIlun " is not allowed, already registered nexus %" PRIu64, service_action, session_id, lun, reservation_key); SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_SENSE_ASC_INVALID_FIELD_IN_CDB); break; ; } persistent_register_lun(context, session_id, lun, service_action_key, scsi_status); break; case PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY: if (service_action_key == 0 && !pr_info_is_registered(pr_info, session_id)) { /* FIXME: is this a "nothing to do" case ? */ exalog_debug("iSCSI PR: register and ignore existing keys" " from session %i on LUN %" PRIlun " (ntd)", session_id, lun); SCSI_STATUS_OK(scsi_status, 0); break; } if (spec_i_pt == 1) { exalog_warning("iSCSI PR failed: " "reserve operation (%i) from session %i on LUN %" PRIlun " is not allowed", service_action, session_id, lun); SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_SENSE_ASC_INVALID_FIELD_IN_CDB); break; } persistent_register_lun(context, session_id, lun, service_action_key, scsi_status); break; case PR_OUT_RESERVE: if (persistent_reserve_lun(context, session_id, lun, reservation_key, pr_type)) SCSI_STATUS_OK(scsi_status, 0); else SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_RESERVATION_CONFLICT, 0, 0); break; case PR_OUT_RELEASE: persistent_release_lun(context, session_id, lun, reservation_key, pr_type, scsi_status); break; case PR_OUT_CLEAR: persistent_clear_lun(context, session_id, lun, scsi_status); break; case PR_OUT_PREEMPT: persistent_preempt_lun(context, session_id, lun, reservation_key, service_action_key, pr_type, scsi_status); break; case PR_OUT_PREEMPT_AND_ABORT: persistent_preempt_lun(context, session_id, lun, reservation_key, service_action_key, pr_type, scsi_status); break; /* FIXME : we must add abort task set and it's not done spc3 - 5.6.10.5 */ case PR_OUT_REGISTER_AND_MOVE: /* FIXME : not implemented ,not needed for windows */ exalog_error("iSCSI PR failed: " "reserve operation 'register_and_move' not supported" " (from session %i on LUN %" PRIlun ")", session_id, lun); SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, 0); break; default: exalog_error("iSCSI PR failed: " "reserve operation (%i) not expected" " (from session %i on LUN %" PRIlun ")", service_action, session_id, lun); SCSI_STATUS_ERROR(scsi_status, SCSI_STATUS_CHECK_CONDITION, SCSI_SENSE_ILLEGAL_REQUEST, 0); break; } /* PR generation sp3r23 6.11.2 */ if (scsi_status->status == SCSI_STATUS_GOOD && service_action != PR_OUT_RESERVE && service_action != PR_OUT_RELEASE) { pr_info->pr_generation++; } }