From b53e4cb4c1fb87464b663231d16e71202bf1da33 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 26 May 2010 17:46:46 +0000 Subject: [PATCH] Merge of the pr branch with additional fixes for problems found during extra testing git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1714 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- Makefile | 12 + fcst/ft_sess.c | 10 +- iscsi-scst/README | 7 + iscsi-scst/README_in-tree | 7 + iscsi-scst/kernel/Kconfig | 14 + iscsi-scst/kernel/Makefile | 2 + iscsi-scst/kernel/config.c | 4 + iscsi-scst/kernel/conn.c | 4 +- iscsi-scst/kernel/iscsi.c | 69 +- iscsi-scst/kernel/nthread.c | 27 +- iscsi-scst/kernel/session.c | 8 +- iscsi-scst/kernel/target.c | 6 +- mpt/mpt_scst.c | 14 +- mvsas_tgt/mv_tgt.c | 16 +- qla2x00t/qla2x00-target/qla2x00t.c | 15 +- qla_isp/linux/isp_scst.c | 8 +- scst/README | 64 +- scst/README_in-tree | 56 +- scst/include/scst.h | 243 ++- scst/include/scst_const.h | 88 +- scst/include/scst_debug.h | 5 + scst/include/scst_user.h | 1 + scst/kernel/in-tree/Kconfig.scst | 22 +- scst/src/Makefile | 6 +- scst/src/dev_handlers/scst_disk.c | 2 - scst/src/dev_handlers/scst_modisk.c | 2 - scst/src/dev_handlers/scst_tape.c | 2 - scst/src/dev_handlers/scst_user.c | 17 +- scst/src/dev_handlers/scst_vdisk.c | 51 +- scst/src/scst_debug.c | 91 + scst/src/scst_lib.c | 483 ++++- scst/src/scst_main.c | 67 +- scst/src/scst_pres.c | 2581 +++++++++++++++++++++++++++ scst/src/scst_pres.h | 168 ++ scst/src/scst_priv.h | 14 +- scst/src/scst_proc.c | 7 +- scst/src/scst_sysfs.c | 15 +- scst/src/scst_targ.c | 850 ++++++--- scst_local/scst_local.c | 9 +- srpt/src/ib_srpt.c | 20 +- usr/fileio/common.c | 21 +- 41 files changed, 4491 insertions(+), 617 deletions(-) create mode 100644 scst/src/scst_pres.c create mode 100644 scst/src/scst_pres.h diff --git a/Makefile b/Makefile index 47c8a729d..464ad3984 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,18 @@ help: @echo " srpt_install : srp target: install" @echo " srpt_uninstall : srp target: uninstall" @echo "" + @echo " mvsas : make MVSAS target" + @echo " mvsas_clean : mvsas target: clean " + @echo " mvsas_extraclean : mvsas target: clean + clean dependencies" + @echo " mvsas_install : mvsas target: install" + @echo " mvsas_uninstall : mvsas target: uninstall" + @echo "" + @echo " fcst : make FCoE target" + @echo " fcst_clean : FCoE target: clean " + @echo " fcst_extraclean : FCoE target: clean + clean dependencies" + @echo " fcst_install : FCoE target: install" + @echo " fcst_uninstall : FCoE target: uninstall" + @echo "" @echo " scst_local : make scst_local target" @echo " scst_local_install : scst_local target: install" @echo " scst_local_uninstall : scst_local target: uninstall" diff --git a/fcst/ft_sess.c b/fcst/ft_sess.c index ec119e987..adb3dbae7 100644 --- a/fcst/ft_sess.c +++ b/fcst/ft_sess.c @@ -56,7 +56,7 @@ static struct ft_tport *ft_tport_create(struct fc_lport *lport) if (!tport) return NULL; - tport->tgt = scst_register(&ft_scst_template, name); + tport->tgt = scst_register_target(&ft_scst_template, name); if (!tport->tgt) { kfree(tport); return NULL; @@ -94,7 +94,7 @@ static void ft_tport_delete(struct ft_tport *tport) tgt = tport->tgt; BUG_ON(!tgt); FT_SESS_DBG("delete %s\n", scst_get_tgt_name(tgt)); - scst_unregister(tgt); + scst_unregister_target(tgt); lport = tport->lport; BUG_ON(tport != lport->prov[FC_TYPE_FCP]); rcu_assign_pointer(lport->prov[FC_TYPE_FCP], NULL); @@ -214,7 +214,8 @@ static int ft_sess_create(struct ft_tport *tport, struct fc_rport_priv *rdata, ft_format_wwn(name, sizeof(name), rdata->ids.port_name); FT_SESS_DBG("register %s\n", name); - scst_sess = scst_register_session(tport->tgt, 0, name, NULL, NULL); + scst_sess = scst_register_session(tport->tgt, 0, name, sess, NULL, + NULL); if (!scst_sess) { kfree(sess); return FC_SPP_RESP_RES; /* out of resources */ @@ -236,7 +237,6 @@ static int ft_sess_create(struct ft_tport *tport, struct fc_rport_priv *rdata, sess->params = fcp_parm; rdata->prli_count++; - scst_sess_set_tgt_priv(scst_sess, sess); return 0; } @@ -474,7 +474,7 @@ void ft_recv(struct fc_lport *lport, struct fc_seq *sp, struct fc_frame *fp) /* * Release all sessions for a target. - * Called through scst_unregister() as well as directly. + * Called through scst_unregister_target() as well as directly. * Caller holds ft_lport_lock. */ int ft_tgt_release(struct scst_tgt *tgt) diff --git a/iscsi-scst/README b/iscsi-scst/README index 123af37e5..0a01938bb 100644 --- a/iscsi-scst/README +++ b/iscsi-scst/README @@ -805,6 +805,13 @@ in/out in the kernel's module Makefile: - CONFIG_SCST_ISCSI_DEBUG_DIGEST_FAILURES - simulates digest failures in random places. + + - CONFIG_SCST_ISCSI_SKIP_ISID - makes iSCSI-SCST to always use iSCSI + initiator port TransportID format 0, i.e. always identify initiators + only by names and ignore ISID part in TransportIDs. This is useful + for Persistent Reservations with initiators which don't use ISID in a + standards confirming way, for instance by always incrementing it on + each reconnect, as well as to debug Persistent Reservations. Creating version of put_page_callback patch for your kernel diff --git a/iscsi-scst/README_in-tree b/iscsi-scst/README_in-tree index 68f1a0a23..0912c8fe4 100644 --- a/iscsi-scst/README_in-tree +++ b/iscsi-scst/README_in-tree @@ -701,6 +701,13 @@ in/out in the kernel's module Makefile: - CONFIG_SCST_ISCSI_DEBUG_DIGEST_FAILURES - simulates digest failures in random places. + - CONFIG_SCST_ISCSI_SKIP_ISID - makes iSCSI-SCST to always use iSCSI + initiator port TransportID format 0, i.e. always identify initiators + only by names and ignore ISID part in TransportIDs. This is useful + for Persistent Reservations with initiators which don't use ISID in a + standards confirming way, for instance by always incrementing it on + each reconnect, as well as to debug Persistent Reservations. + Credits ------- diff --git a/iscsi-scst/kernel/Kconfig b/iscsi-scst/kernel/Kconfig index 70c282422..5c1172731 100644 --- a/iscsi-scst/kernel/Kconfig +++ b/iscsi-scst/kernel/Kconfig @@ -23,3 +23,17 @@ config SCST_ISCSI_DEBUG_DIGEST_FAILURES iSCSI initiator that is talking to SCST. If unsure, say "N". + +config CONFIG_SCST_ISCSI_SKIP_ISID + bool "Do not use ISID in initiator port transport id" + depends on SCST_ISCSI + help + Makes iSCSI-SCST to always use iSCSI initiator port + TransportID format 0, i.e. always identify initiators only by + names and ignore ISID part in TransportIDs. This is useful for + Persistent Reservations with initiators which don't use ISID + in a standards confirming way, for instance by always + incrementing it on each reconnect, as well as to debug + Persistent Reservations. + + If unsure, say "N". diff --git a/iscsi-scst/kernel/Makefile b/iscsi-scst/kernel/Makefile index f9ed4d8d0..176d4d90a 100644 --- a/iscsi-scst/kernel/Makefile +++ b/iscsi-scst/kernel/Makefile @@ -29,6 +29,8 @@ EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions #EXTRA_CFLAGS += -DCONFIG_SCST_ISCSI_DEBUG_DIGEST_FAILURES +#EXTRA_CFLAGS += -DCONFIG_SCST_ISCSI_SKIP_ISID + obj-m += iscsi-scst.o iscsi-scst-objs := iscsi.o nthread.o config.o digest.o \ conn.o session.o target.o event.o param.o diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index 0d2b03ea3..9c5c1dd03 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -260,6 +260,10 @@ static ssize_t iscsi_version_show(struct kobject *kobj, strcat(buf, "DEBUG_DIGEST_FAILURES\n"); #endif +#ifdef CONFIG_SCST_ISCSI_SKIP_ISID + strcat(buf, "CONFIG_SCST_ISCSI_SKIP_ISID\n"); +#endif + TRACE_EXIT(); return strlen(buf); } diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index 2b9a64d29..2ec41adee 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -613,12 +613,12 @@ static int conn_setup_sock(struct iscsi_conn *conn) mm_segment_t oldfs; struct iscsi_session *session = conn->session; - TRACE_DBG("%llu", (long long unsigned int)session->sid); + TRACE_DBG("%llx", (long long unsigned int)session->sid); conn->sock = SOCKET_I(conn->file->f_dentry->d_inode); if (conn->sock->ops->sendpage == NULL) { - PRINT_ERROR("Socket for sid %llu doesn't support sendpage()", + PRINT_ERROR("Socket for sid %llx doesn't support sendpage()", (long long unsigned int)session->sid); res = -EINVAL; goto out; diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 738290ca3..2068b7a9b 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -18,8 +18,11 @@ #include #include #include +#include #include #include +#include +#include #include "iscsi.h" #include "digest.h" @@ -3118,8 +3121,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd) u8 *sense = scst_cmd_get_sense_buffer(scst_cmd); int sense_len = scst_cmd_get_sense_buffer_len(scst_cmd); - if (unlikely(scst_cmd_atomic(scst_cmd))) - return SCST_TGT_RES_NEED_THREAD_CTX; + EXTRACHECKS_BUG_ON(scst_cmd_atomic(scst_cmd)); scst_cmd_set_tgt_priv(scst_cmd, NULL); @@ -3494,6 +3496,56 @@ static int iscsi_report_aen(struct scst_aen *aen) return res; } +int iscsi_get_initiator_port_transport_id(struct scst_session *scst_sess, + uint8_t **transport_id) +{ + struct iscsi_session *sess; + int res = 0; + union iscsi_sid sid; + int tr_id_size; + uint8_t *tr_id; + uint8_t q; + + TRACE_ENTRY(); + + sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess); + + sid = *(union iscsi_sid *)&sess->sid; + sid.id.tsih = 0; + + tr_id_size = 4 + strlen(sess->initiator_name) + 5 + + snprintf(&q, sizeof(q), "%llx", sid.id64) + 1; + tr_id_size = (tr_id_size + 3) & -4; + + tr_id = kzalloc(tr_id_size, GFP_KERNEL); + if (tr_id == NULL) { + PRINT_ERROR("Allocation of TrandportID (size %d) failed", + tr_id_size); + res = -ENOMEM; + goto out; + } + +#ifdef CONFIG_SCST_ISCSI_SKIP_ISID + tr_id[0] = 0x0 | SCSI_TRANSPORTID_PROTOCOLID_ISCSI; + tr_id_size = 4 + sprintf(&tr_id[4], "%s", sess->initiator_name) + 1; + tr_id_size = (tr_id_size + 3) & -4; +#else + tr_id[0] = 0x40 | SCSI_TRANSPORTID_PROTOCOLID_ISCSI; + sprintf(&tr_id[4], "%s,i,0x%llx", sess->initiator_name, sid.id64); +#endif + + put_unaligned(cpu_to_be16(tr_id_size - 4), + (__be16 *)&tr_id[2]); + + *transport_id = tr_id; + + TRACE_DBG("Created tid '%s'", &tr_id[4]); + +out: + TRACE_EXIT_RES(res); + return res; +} + void iscsi_send_nop_in(struct iscsi_conn *conn) { struct iscsi_cmnd *req, *rsp; @@ -3615,6 +3667,7 @@ struct scst_tgt_template iscsi_template = { .task_mgmt_affected_cmds_done = iscsi_task_mgmt_affected_cmds_done, .task_mgmt_fn_done = iscsi_task_mgmt_fn_done, .report_aen = iscsi_report_aen, + .get_initiator_port_transport_id = iscsi_get_initiator_port_transport_id, }; static __init int iscsi_run_threads(int count, char *name, int (*fn)(void *)) @@ -3691,6 +3744,12 @@ static int __init iscsi_init(void) #endif #endif +#ifdef CONFIG_SCST_ISCSI_SKIP_ISID + PRINT_WARNING("%s", "CONFIG_SCST_ISCSI_SKIP_ISID defined: identifying " + "initiators only by names and ignoring ISID part in " + "TransportIDs"); +#endif + ctr_major = register_chrdev(0, ctr_name, &ctr_fops); if (ctr_major < 0) { PRINT_ERROR("failed to register the control device %d", @@ -3729,6 +3788,12 @@ static int __init iscsi_init(void) if (err != 0) goto out_thr; +#ifdef CONFIG_SCST_ISCSI_SKIP_ISID + iscsi_tid_name_only = true; +#else + iscsi_tid_name_only = false; +#endif + out: return err; diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index c4ed26887..b1b4e2c80 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -326,12 +326,17 @@ void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd) struct iscsi_session *sess = conn->session; struct iscsi_conn *c; + if (sess->sess_reinst_successor != NULL) + scst_reassign_persistent_sess_states( + sess->sess_reinst_successor->scst_sess, + sess->scst_sess); + mutex_lock(&sess->target->target_mutex); /* * We can't mark sess as shutting down earlier, because until * now it might have pending commands. Otherwise, in case of - * reinstatement it might lead to data corruption, because + * reinstatement, it might lead to data corruption, because * commands in being reinstated session can be executed * after commands in the new session. */ @@ -374,6 +379,8 @@ static void close_conn(struct iscsi_conn *conn) typeof(jiffies) shut_start_waiting = start_waiting; bool pending_reported = 0, wait_expired = 0, shut_expired = 0; bool reinst; + uint32_t tid, cid; + uint64_t sid; #define CONN_PENDING_TIMEOUT ((typeof(jiffies))10*HZ) #define CONN_WAIT_TIMEOUT ((typeof(jiffies))10*HZ) @@ -548,10 +555,9 @@ static void close_conn(struct iscsi_conn *conn) wait_for_completion(&conn->ready_to_free); - TRACE_CONN_CLOSE("Notifying user space about closing connection %p", - conn); - event_send(target->tid, session->sid, conn->cid, 0, E_CONN_CLOSE, - NULL, NULL); + tid = target->tid; + sid = session->sid; + cid = conn->cid; #ifdef CONFIG_SCST_PROC mutex_lock(&target->target_mutex); @@ -561,6 +567,17 @@ static void close_conn(struct iscsi_conn *conn) kobject_put(&conn->iscsi_conn_kobj); #endif + /* + * We can't send E_CONN_CLOSE earlier, because otherwise we would have + * a race, when the user space tried to destroy session, which still + * has connections. + * + * !! All target, session and conn can be already dead here !! + */ + TRACE_CONN_CLOSE("Notifying user space about closing connection %p", + conn); + event_send(tid, sid, cid, 0, E_CONN_CLOSE, NULL, NULL); + TRACE_EXIT(); return; } diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index 9ebda5ddc..a9ee50dec 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -81,7 +81,7 @@ static int iscsi_session_alloc(struct iscsi_target *target, session->next_ttt = 1; session->scst_sess = scst_register_session(target->scst_tgt, 0, - name, NULL, NULL); + name, session, NULL, NULL); if (session->scst_sess == NULL) { PRINT_ERROR("%s", "scst_register_session() failed"); err = -ENOMEM; @@ -92,8 +92,6 @@ static int iscsi_session_alloc(struct iscsi_target *target, kfree(name); #endif - scst_sess_set_tgt_priv(session->scst_sess, session); - TRACE_MGMT_DBG("Session %p created: target %p, tid %u, sid %#Lx", session, target, target->tid, info->sid); @@ -346,7 +344,7 @@ int __del_session(struct iscsi_target *target, u64 sid) return -ENOENT; if (!list_empty(&session->conn_list)) { - PRINT_ERROR("%llu still have connections", + PRINT_ERROR("%llx still have connections", (long long unsigned int)session->sid); return -EBUSY; } @@ -523,7 +521,7 @@ static ssize_t iscsi_sess_force_close_store(struct kobject *kobj, goto out; } - PRINT_INFO("Deleting session %llu with initiator %s (%p)", + PRINT_INFO("Deleting session %llx with initiator %s (%p)", (long long unsigned int)sess->sid, sess->initiator_name, sess); list_for_each_entry(conn, &sess->conn_list, conn_list_entry) { diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index b8352ef2d..f8d345518 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -89,9 +89,9 @@ static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid, INIT_LIST_HEAD(&target->attrs_list); #endif - target->scst_tgt = scst_register(&iscsi_template, target->name); + target->scst_tgt = scst_register_target(&iscsi_template, target->name); if (!target->scst_tgt) { - PRINT_ERROR("%s", "scst_register() failed"); + PRINT_ERROR("%s", "scst_register_target() failed"); err = -EBUSY; goto out_free; } @@ -233,7 +233,7 @@ static void target_destroy(struct iscsi_target *target) } #endif - scst_unregister(target->scst_tgt); + scst_unregister_target(target->scst_tgt); kfree(target); diff --git a/mpt/mpt_scst.c b/mpt/mpt_scst.c index 4b284e320..5fe52937d 100644 --- a/mpt/mpt_scst.c +++ b/mpt/mpt_scst.c @@ -381,9 +381,9 @@ mptstm_probe(struct pci_dev *pdev, const struct pci_device_id *id) atomic_set(&tgt->sess_count, 0); init_waitqueue_head(&tgt->waitQ); - tgt->scst_tgt = scst_register(&tgt_template, MYNAM); + tgt->scst_tgt = scst_register_target(&tgt_template, MYNAM); if (tgt->scst_tgt == NULL) { - PRINT_ERROR(MYNAM ": scst_register() " + PRINT_ERROR(MYNAM ": scst_register_target() " "failed for host %p", pdev); ret = -ENODEV; @@ -399,7 +399,7 @@ mptstm_probe(struct pci_dev *pdev, const struct pci_device_id *id) PRINT_ERROR("Not enough memory to register " "target driver %s entry %s in /proc", tgt_template.name, name); - scst_unregister(tgt->scst_tgt); + scst_unregister_target(tgt->scst_tgt); ret = -ENOMEM; goto out; } @@ -437,7 +437,7 @@ static struct mpt_pci_driver mptstm_driver = { * * this function is intended to detect the target adapters that are present in * the system. Each found adapter should be registered by calling - * scst_register(). The function should return a value >= 0 to signify + * scst_register_target(). The function should return a value >= 0 to signify * the number of detected target adapters. A negative value should be * returned whenever there is an error. */ @@ -592,8 +592,6 @@ mpt_alloc_session_done(struct scst_session *scst_sess, void *data, int result) TRACE_ENTRY(); if (result == 0) { - scst_sess_set_tgt_priv(scst_sess, sess); - while (!list_empty(&sess->delayed_cmds)) { cmd = list_entry(sess->delayed_cmds.next, typeof(*cmd), delayed_cmds_entry); @@ -979,7 +977,8 @@ stmapp_tgt_command(MPT_STM_PRIV *priv, u32 reply_word) INIT_LIST_HEAD(&sess->delayed_cmds); sess->scst_sess = scst_register_session(tgt->scst_tgt, 1, - "", sess, mpt_alloc_session_done); + "", sess, sess, + mpt_alloc_session_done); if (sess->scst_sess == NULL) { PRINT_ERROR(MYNAM ": scst_register_session failed %p", tgt); @@ -990,7 +989,6 @@ stmapp_tgt_command(MPT_STM_PRIV *priv, u32 reply_word) __set_bit(MPT_SESS_INITING, &sess->sess_flags); tgt->sess[init_index] = sess; - scst_sess_set_tgt_priv(sess->scst_sess, sess); cmd->sess = sess; list_add_tail(&cmd->delayed_cmds_entry, &sess->delayed_cmds); diff --git a/mvsas_tgt/mv_tgt.c b/mvsas_tgt/mv_tgt.c index b34962ecc..314a8d8f0 100644 --- a/mvsas_tgt/mv_tgt.c +++ b/mvsas_tgt/mv_tgt.c @@ -1593,7 +1593,7 @@ int mvst_send_cmd_to_scst(struct mvs_info *mvi, } sess->scst_sess = scst_register_session( - tgt->scst_tgt, SCST_ATOMIC, name, sess, + tgt->scst_tgt, SCST_ATOMIC, name, sess, sess, mvst_alloc_session_done); kfree(name); } @@ -1606,7 +1606,6 @@ int mvst_send_cmd_to_scst(struct mvs_info *mvi, res = -EFAULT; goto out_free_sess; } - scst_sess_set_tgt_priv(sess->scst_sess, sess); /* add session data to host data structure */ list_add(&sess->sess_entry, &tgt->sess_list); @@ -2112,9 +2111,9 @@ static void mvst_register_tgt_handler(struct mvs_info *mvi) INIT_LIST_HEAD(&tgt->sess_list); init_waitqueue_head(&tgt->waitQ); - tgt->scst_tgt = scst_register(&tgt_template, MVST_NAME); + tgt->scst_tgt = scst_register_target(&tgt_template, MVST_NAME); if (!tgt->scst_tgt) { - PRINT_ERROR("mvst tgt(%ld): scst_register() " + PRINT_ERROR("mvst tgt(%ld): scst_register_target() " "failed for host %ld(%p)", mvi->instance, mvi->host_no, mvi); kfree(tgt); @@ -2141,11 +2140,11 @@ static void mvst_unregister_tgt_handler(struct mvs_info *mvi) TRACE_DBG("Shutting down host %ld(%ld,%p)", mvi->host_no, mvi->instance, mvi); - scst_unregister(tgt->scst_tgt); + scst_unregister_target(tgt->scst_tgt); mvi->flags &= ~MVF_TARGET_MODE_ENABLE; /* * Free of tgt happens via callback mvst_target_release - * called from scst_unregister, so we shouldn't touch it again + * called from scst_unregister_target, so we shouldn't touch it again */ tgt = NULL; } @@ -2340,11 +2339,12 @@ static void mvst_host_action(struct mvs_info *mvi, TRACE_DBG("Shutting down host %ld(%ld,%p)", mvi->host_no, mvi->instance, mvi); - scst_unregister(tgt->scst_tgt); + scst_unregister_target(tgt->scst_tgt); mvi->flags &= ~MVF_TARGET_MODE_ENABLE; /* * Free of tgt happens via callback mvst_target_release - * called from scst_unregister, so we shouldn't touch it again + * called from scst_unregister_target, so we shouldn't touch + * it again */ tgt = NULL; break; diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 143db1435..2800c348e 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -625,8 +625,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, /* Let's do the session creation async'ly */ sess->scst_sess = scst_register_session(tgt->scst_tgt, 1, wwn_str, - sess, q2t_alloc_session_done); - + sess, sess, q2t_alloc_session_done); if (sess->scst_sess == NULL) { PRINT_CRIT_ERROR("qla2x00t(%ld): scst_register_session() " "failed for host %ld (wwn %s, loop_id %d), all " @@ -634,7 +633,6 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport, ha->host_no, wwn_str, fcport->loop_id); goto out_free_sess_wwn; } - scst_sess_set_tgt_priv(sess->scst_sess, sess); spin_lock_irq(&ha->hardware_lock); TRACE_MGMT_DBG("Adding sess %p to tgt %p", sess, tgt); @@ -2125,8 +2123,7 @@ static int q2x_xmit_response(struct scst_cmd *scst_cmd) #endif #ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD - if (scst_cmd_atomic(scst_cmd)) - return SCST_TGT_RES_NEED_THREAD_CTX; + EXTRACHECKS_BUG_ON(scst_cmd_atomic(scst_cmd)); #endif if (is_send_status) @@ -5020,12 +5017,12 @@ static int q2t_add_target(scsi_qla_host_t *ha) if (q2t_get_target_name(ha, &wwn) != 0) goto out_free; - tgt->scst_tgt = scst_register(&tgt2x_template, wwn); + tgt->scst_tgt = scst_register_target(&tgt2x_template, wwn); kfree(wwn); if (!tgt->scst_tgt) { - PRINT_ERROR("qla2x00t(%ld): scst_register() " + PRINT_ERROR("qla2x00t(%ld): scst_register_target() " "failed for host %ld(%p)", ha->instance, ha->host_no, ha); res = -ENOMEM; @@ -5093,10 +5090,10 @@ static int q2t_remove_target(scsi_qla_host_t *ha) } TRACE_DBG("Unregistering target for host %ld(%p)", ha->host_no, ha); - scst_unregister(ha->tgt->scst_tgt); + scst_unregister_target(ha->tgt->scst_tgt); /* * Free of tgt happens via callback q2t_target_release - * called from scst_unregister, so we shouldn't touch + * called from scst_unregister_target, so we shouldn't touch * it again. */ diff --git a/qla_isp/linux/isp_scst.c b/qla_isp/linux/isp_scst.c index e75201806..743a70c82 100644 --- a/qla_isp/linux/isp_scst.c +++ b/qla_isp/linux/isp_scst.c @@ -288,7 +288,7 @@ alloc_ini(bus_chan_t *bc, uint64_t iid) GET(7), GET(6), GET(5) , GET(4), GET(3), GET(2), GET(1), GET(0)); #undef GET - nptr->ini_scst_sess = scst_register_session(bc->scst_tgt, 0, ini_name, NULL, NULL); + nptr->ini_scst_sess = scst_register_session(bc->scst_tgt, 0, ini_name, NULL, NULL, NULL); if (!nptr->ini_scst_sess) { Eprintk("cannot register SCST session\n"); kfree(nptr); @@ -1637,7 +1637,7 @@ register_hba(bus_t *bp) } isp_tgt_template.sg_tablesize = get_sg_tablesize(bp->h.r_identity); - scst_tgt = scst_register(&isp_tgt_template, name); + scst_tgt = scst_register_target(&isp_tgt_template, name); if (scst_tgt == NULL) { Eprintk("cannot register scst device %s for %s%d\n", name, bp->h.r_name, bp->h.r_inst); goto err_free_chan; @@ -1672,7 +1672,7 @@ register_hba(bus_t *bp) err_free_chan: for (chan = bp->h.r_nchannels -1; chan >= 0; chan--) { if (bchan[chan].scst_tgt) { - scst_unregister(bchan[chan].scst_tgt); + scst_unregister_target(bchan[chan].scst_tgt); } } kfree(bchan); @@ -1726,7 +1726,7 @@ unregister_hba(bus_t *bp, hba_register_t *unreg_hp) BUS_DBG(bp, "Chan %d waiting for finishing %d sessions\n", chan, atomic_read(&bc->sess_count)); wait_event(bc->wait_queue, atomic_read(&bc->sess_count) == 0); BUS_DBG(bp, "Chan %d all sessions finished\n", chan); - scst_unregister(bc->scst_tgt); + scst_unregister_target(bc->scst_tgt); } } kfree(bp->bchan); diff --git a/scst/README b/scst/README index 0b6b80a6d..2d40aaf3a 100644 --- a/scst/README +++ b/scst/README @@ -271,17 +271,20 @@ in/out in Makefile: patching is necessary for pass-through device handlers (scst_disk, etc.). - - CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ - if defined, it will be - allowed to submit pass-through commands to real SCSI devices via the SCSI - middle layer using scsi_execute_async() function from soft IRQ - context (tasklets). This used to be the default, but currently it - seems the SCSI middle layer starts expecting only thread context on - the IO submit path, so it is disabled now by default. Enabling it - will decrease amount of context switches and improve performance. It - is more or less safe, in the worst case, if in your configuration the - SCSI middle layer really doesn't expect SIRQ context in - scsi_execute_async() function, you will get a warning message in the - kernel log. + - CONFIG_SCST_TEST_IO_IN_SIRQ - if defined, allows SCST to submit selected + SCSI commands (TUR and READ/WRITE) from soft-IRQ context (tasklets). + Enabling it will decrease amount of context switches and slightly + improve performance. The goal of this option is to be able to measure + overhead of the context switches. If after enabling this option you + don't see under load in vmstat output on the target significant + decrease of amount of context switches, then your target driver + doesn't submit commands to SCST in IRQ context. For instance, + iSCSI-SCST doesn't do that, but qla2x00t with + CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD disabled - does. This option is + designed to be used with vdisk NULLIO backend. + + WARNING! Using with other backend than vdisk NULLIO is unsafe and can + lead you to a kernel crash! - CONFIG_SCST_STRICT_SECURITY - if defined, makes SCST zero allocated data buffers. Undefining it (default) considerably improves performance @@ -1285,6 +1288,34 @@ IMPORTANT: Some disk and partition table management utilities don't support work with files on such file system. +Persistent Reservations +----------------------- + +SCST implements Persistent Reservations with full set of capabilities, +including "Persistence Through Power Loss". + +The "Persistence Through Power Loss" data are saved in /var/lib/scst/pr +with files with names the same as the names of the corresponding +devices. Also this directory contains backup versions of those files +with suffix ".1". Those backup files are used in case of power or other +failure to prevent Persistent Reservation information from corruption +during update. + +The Persistent Reservations available on all transports implementing +get_initiator_port_transport_id() callback. Transports not implementing +this callback will act in one of 2 possible scenarios ("all or +nothing"): + +1. If a device has such transport connected and doesn't have persistent +reservations, it will refuse Persistent Reservations commands as if it +doesn't support them. + +2. If a device has persistent reservations, all initiators newly +connecting via such transports will not see this device. After all +persistent reservations from this device are released, upon reconnect +the initiators will see it. + + Caching ------- @@ -1473,7 +1504,8 @@ you, so the resulting performance will, in average, be better you can by manual tuning improve it even more. If you want to get maximum performance from your target, RHEL/CentOS 5.x -kernels are not recommended, because they are based on very outdated +kernels are not recommended on both target and initiators, if you are +using Linux initiators, because those kernels are based on very outdated 2.6.18 kernel, hence, missed >3 years of important improvements in the kernel's storage area. You should use at least long maintained vanilla 2.6.27.x kernel, although 2.6.29+ would be even better. @@ -1490,9 +1522,6 @@ In order to get the maximum performance you should: - Disable in Makefile CONFIG_SCST_STRICT_SERIALIZING, CONFIG_SCST_EXTRACHECKS, CONFIG_SCST_TRACING, CONFIG_SCST_DEBUG*, CONFIG_SCST_STRICT_SECURITY - - For pass-through devices enable - CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ. - 2. For target drivers: - Disable in Makefiles CONFIG_SCST_EXTRACHECKS, CONFIG_SCST_TRACING, @@ -1502,14 +1531,13 @@ In order to get the maximum performance you should: - Disable in Makefile CONFIG_SCST_TRACING and CONFIG_SCST_DEBUG. - IMPORTANT: The development version of SCST in the SVN is optimized for ========= development and bug hunting, not for performance. To reconfigure it for performance you should run "make debug2perf" in the root of your source code (e.g. trunk/). It will set the above options as needed. The only option it doesn't set is - CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ, so, if - needed, you should change it manually. + CONFIG_SCST_TEST_IO_IN_SIRQ, so, if needed, you should change + it manually. IMPORTANT: You can't use debug SCST drivers with non-debug SCST core. ========= So, after disabling both CONFIG_SCST_TRACING and CONFIG_SCST_DEBUG diff --git a/scst/README_in-tree b/scst/README_in-tree index db37ff817..e889ce23d 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -196,17 +196,20 @@ your favorite kernel configuration Makefile target, e.g. "make xconfig": patching is necessary for pass-through device handlers (scst_disk, etc.). - - CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ - if defined, it will be - allowed to submit pass-through commands to real SCSI devices via the SCSI - middle layer using scsi_execute_async() function from soft IRQ - context (tasklets). This used to be the default, but currently it - seems the SCSI middle layer starts expecting only thread context on - the IO submit path, so it is disabled now by default. Enabling it - will decrease amount of context switches and improve performance. It - is more or less safe, in the worst case, if in your configuration the - SCSI middle layer really doesn't expect SIRQ context in - scsi_execute_async() function, you will get a warning message in the - kernel log. + - CONFIG_SCST_TEST_IO_IN_SIRQ - if defined, allows SCST to submit selected + SCSI commands (TUR and READ/WRITE) from soft-IRQ context (tasklets). + Enabling it will decrease amount of context switches and slightly + improve performance. The goal of this option is to be able to measure + overhead of the context switches. If after enabling this option you + don't see under load in vmstat output on the target significant + decrease of amount of context switches, then your target driver + doesn't submit commands to SCST in IRQ context. For instance, + iSCSI-SCST doesn't do that, but qla2x00t with + CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD disabled - does. This option is + designed to be used with vdisk NULLIO backend. + + WARNING! Using with other backend than vdisk NULLIO is unsafe and can + lead you to a kernel crash! - CONFIG_SCST_STRICT_SECURITY - if defined, makes SCST zero allocated data buffers. Undefining it (default) considerably improves performance @@ -869,6 +872,34 @@ IMPORTANT: Some disk and partition table management utilities don't support work with files on such file system. +Persistent Reservations +----------------------- + +SCST implements Persistent Reservations with full set of capabilities, +including "Persistence Through Power Loss". + +The "Persistence Through Power Loss" data are saved in /var/lib/scst/pr +with files with names the same as the names of the corresponding +devices. Also this directory contains backup versions of those files +with suffix ".1". Those backup files are used in case of power or other +failure to prevent Persistent Reservation information from corruption +during update. + +The Persistent Reservations available on all transports implementing +get_initiator_port_transport_id() callback. Transports not implementing +this callback will act in one of 2 possible scenarios ("all or +nothing"): + +1. If a device has such transport connected and doesn't have persistent +reservations, it will refuse Persistent Reservations commands as if it +doesn't support them. + +2. If a device has persistent reservations, all initiators newly +connecting via such transports will not see this device. After all +persistent reservations from this device are released, upon reconnect +the initiators will see it. + + Caching ------- @@ -1068,9 +1099,6 @@ In order to get the maximum performance you should: - Disable in Makefile CONFIG_SCST_STRICT_SERIALIZING, CONFIG_SCST_EXTRACHECKS, CONFIG_SCST_TRACING, CONFIG_SCST_DEBUG*, CONFIG_SCST_STRICT_SECURITY - - For pass-through devices enable - CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ. - 2. For target drivers: - Disable in Makefiles CONFIG_SCST_EXTRACHECKS, CONFIG_SCST_TRACING, diff --git a/scst/include/scst.h b/scst/include/scst.h index a96e0106d..9e8a42e7a 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -64,7 +64,7 @@ typedef _Bool bool; #else #define SCST_VERSION_STRING_SUFFIX #endif -#define SCST_VERSION_STRING "2.0.0-pre1" SCST_VERSION_STRING_SUFFIX +#define SCST_VERSION_STRING "2.0.0-rc1" SCST_VERSION_STRING_SUFFIX #define SCST_INTERFACE_VERSION \ SCST_VERSION_STRING "$Revision$" SCST_CONST_VERSION @@ -200,22 +200,25 @@ static inline int list_is_last(const struct list_head *list, *************************************************************/ /* LUN translation (mcmd->tgt_dev assignment) */ -#define SCST_MCMD_STATE_INIT 0 +#define SCST_MCMD_STATE_INIT 0 -/* Mgmt cmd is ready for processing */ -#define SCST_MCMD_STATE_READY 1 +/* Mgmt cmd is being processed */ +#define SCST_MCMD_STATE_EXEC 1 -/* Mgmt cmd is being executing */ -#define SCST_MCMD_STATE_EXECUTING 2 +/* Waiting for affected commands done */ +#define SCST_MCMD_STATE_WAITING_AFFECTED_CMDS_DONE 2 -/* Post check when affected commands done */ -#define SCST_MCMD_STATE_POST_AFFECTED_CMDS_DONE 3 +/* Post actions when affected commands done */ +#define SCST_MCMD_STATE_AFFECTED_CMDS_DONE 3 + +/* Waiting for affected local commands finished */ +#define SCST_MCMD_STATE_WAITING_AFFECTED_CMDS_FINISHED 4 /* Target driver's task_mgmt_fn_done() is going to be called */ -#define SCST_MCMD_STATE_DONE 4 +#define SCST_MCMD_STATE_DONE 5 /* The mcmd finished */ -#define SCST_MCMD_STATE_FINISHED 5 +#define SCST_MCMD_STATE_FINISHED 6 /************************************************************* ** Constants for "atomic" parameter of SCST's functions @@ -309,9 +312,6 @@ enum scst_exec_context { */ #define SCST_PREPROCESS_STATUS_ERROR_FATAL 3 -/* Thread context requested */ -#define SCST_PREPROCESS_STATUS_NEED_THREAD 4 - /************************************************************* ** Values for AEN functions *************************************************************/ @@ -374,12 +374,6 @@ enum scst_exec_context { /* The cmd should be sent to SCSI mid-level */ #define SCST_EXEC_NOT_COMPLETED 1 -/* - * Thread context is required to execute the command. - * Exec() will be called again in the thread context. - */ -#define SCST_EXEC_NEED_THREAD 2 - /* * Set if cmd is finished and there is status/sense to be sent. * The status should be not sent (i.e. the flag not set) if the @@ -463,11 +457,7 @@ enum scst_exec_context { /* Set if the corresponding context is atomic */ #define SCST_TGT_DEV_AFTER_INIT_WR_ATOMIC 5 -#define SCST_TGT_DEV_AFTER_INIT_OTH_ATOMIC 6 -#define SCST_TGT_DEV_AFTER_RESTART_WR_ATOMIC 7 -#define SCST_TGT_DEV_AFTER_RESTART_OTH_ATOMIC 8 -#define SCST_TGT_DEV_AFTER_RX_DATA_ATOMIC 9 -#define SCST_TGT_DEV_AFTER_EXEC_ATOMIC 10 +#define SCST_TGT_DEV_AFTER_EXEC_ATOMIC 6 #define SCST_TGT_DEV_CLUST_POOL 11 @@ -735,10 +725,6 @@ struct scst_tgt_template { * This command is expected to be NON-BLOCKING. * If it is blocking, consider to set threads_num to some none 0 number. * - * Pay attention to "atomic" attribute of the cmd, which can be get - * by scst_cmd_atomic(): it is true if the function called in the - * atomic (non-sleeping) context. - * * OPTIONAL */ int (*pre_exec) (struct scst_cmd *cmd); @@ -816,6 +802,20 @@ struct scst_tgt_template { int length, int *eof, struct scst_tgt *tgt); #endif + /* + * This function returns in tr_id the corresponding to sess initiator + * port TransporID in the form as it's used by PR commands, see + * "Transport Identifiers" in SPC. Space for the initiator port + * TransporID must be allocated via kmalloc(). Caller supposed to + * kfree() it, when it isn't needed anymore. + * + * Returns 0 on success or negative error code otherwise. + * + * SHOULD HAVE, because it's required for Persistent Reservations. + */ + int (*get_initiator_port_transport_id) (struct scst_session *sess, + uint8_t **transport_id); + /* * This function allows to enable or disable particular target. * A disabled target doesn't receive and process any SCSI commands. @@ -972,7 +972,6 @@ struct scst_dev_type { */ unsigned parse_atomic:1; unsigned alloc_data_buf_atomic:1; - unsigned exec_atomic:1; unsigned dev_done_atomic:1; #ifdef CONFIG_SCST_PROC @@ -986,6 +985,13 @@ struct scst_dev_type { */ unsigned exec_sync:1; + /* + * Should be set if the device wants to receive notification of + * Persistent Reservation commands (PR OUT only) + * Note: The notification will not be send if the command failed + */ + unsigned pr_cmds_notifications:1; + /* * Called to parse CDB from the cmd and initialize * cmd->bufflen and cmd->data_direction (both - REQUIRED). @@ -1032,15 +1038,9 @@ struct scst_dev_type { * cmd->scst_cmd_done() callback. * Returns: * - SCST_EXEC_COMPLETED - the cmd is done, go to other ones - * - SCST_EXEC_NEED_THREAD - thread context is required to execute - * the command. Exec() will be called again in the thread context. * - SCST_EXEC_NOT_COMPLETED - the cmd should be sent to SCSI * mid-level. * - * Pay attention to "atomic" attribute of the cmd, which can be get - * by scst_cmd_atomic(): it is true if the function called in the - * atomic (non-sleeping) context. - * * If this function provides sync execution, you should set * exec_sync flag and consider to setup dedicated threads by * setting threads_num > 0. @@ -1385,6 +1385,9 @@ struct scst_session { /* Access control for this session and list entry there */ struct scst_acg *acg; + /* Initiator port transport id */ + uint8_t *transport_id; + /* List entry for the sessions list inside ACG */ struct list_head acg_sess_list_entry; @@ -1459,6 +1462,29 @@ struct scst_session { #endif }; +/* + * SCST_PR_ABORT_ALL TM function helper structure + */ +struct scst_pr_abort_all_pending_mgmt_cmds_counter { + /* + * How many there are pending for this cmd SCST_PR_ABORT_ALL TM + * commands. + */ + atomic_t pr_abort_pending_cnt; + + /* Saved completition routine */ + void (*saved_cmd_done) (struct scst_cmd *cmd, int next_state, + enum scst_exec_context pref_context); + + /* + * How many there are pending for this cmd SCST_PR_ABORT_ALL TM + * commands, which not yet aborted all affected commands and + * a completion to signal, when it's done. + */ + atomic_t pr_aborting_cnt; + struct completion pr_aborting_cmpl; +}; + /* * Structure to control commands' queuing and threads pool processing the queue */ @@ -1567,6 +1593,12 @@ struct scst_cmd { */ unsigned int sg_buff_modified:1; + /* + * Set if cmd buffer was vmallocated and copied from more + * then one sg chunk + */ + unsigned int sg_buff_vmallocated:1; + /* * Set if scst_cmd_init_stage1_done() called and the target * want that preprocessing_done() will be called @@ -1761,12 +1793,18 @@ struct scst_cmd { /* Used to retry commands in case of double UA */ int dbl_ua_orig_resp_data_len, dbl_ua_orig_data_direction; - /* List corresponding mgmt cmd, if any, protected by sess_list_lock */ + /* + * List of the corresponding mgmt cmds, if any. Protected by + * sess_list_lock. + */ struct list_head mgmt_cmd_list; /* List entry for dev's blocked_cmd_list */ struct list_head blocked_cmd_list_entry; + /* Counter of the corresponding SCST_PR_ABORT_ALL TM commands */ + struct scst_pr_abort_all_pending_mgmt_cmds_counter *pr_abort_counter; + struct scst_cmd *orig_cmd; /* Used to issue REQUEST SENSE */ #ifdef CONFIG_SCST_MEASURE_LATENCY @@ -1806,8 +1844,11 @@ struct scst_mgmt_cmd_stub { /* List entry in cmd->mgmt_cmd_list */ struct list_head cmd_mgmt_cmd_list_entry; - /* set if the cmd was counted in mcmd->cmd_done_wait_count */ + /* Set if the cmd was counted in mcmd->cmd_done_wait_count */ unsigned int done_counted:1; + + /* Set if the cmd was counted in mcmd->cmd_finish_wait_count */ + unsigned int finish_counted:1; }; /* @@ -1824,13 +1865,10 @@ struct scst_mgmt_cmd { int fn; /* task management function */ - unsigned int completed:1; /* set, if the mcmd is completed */ /* Set if device(s) should be unblocked after mcmd's finish */ unsigned int needs_unblocking:1; unsigned int lun_set:1; /* set, if lun field is valid */ unsigned int cmd_sn_set:1; /* set, if cmd_sn field is valid */ - /* set, if scst_mgmt_affected_cmds_done was called */ - unsigned int affected_cmds_done_called:1; /* * Number of commands to finish before sending response, @@ -1862,18 +1900,36 @@ struct scst_mgmt_cmd { /* completition status, one of the SCST_MGMT_STATUS_* constants */ int status; - /* Used for storage of target driver private stuff */ - void *tgt_priv; + /* Used for storage of target driver private stuff or origin PR cmd */ + union { + void *tgt_priv; + struct scst_cmd *origin_pr_cmd; + }; +}; + +/* + * Persistent reservations registrant + */ +struct scst_dev_registrant { + uint8_t *transport_id; + uint16_t rel_tgt_id; + uint64_t key; + + /* tgt_dev (I_T nexus) for this registrant, if any */ + struct scst_tgt_dev *tgt_dev; + + /* List entry for dev_registrants_list */ + struct list_head dev_registrants_list_entry; + + /* 2 auxiliary fields used to rollback changes for errors, etc. */ + struct list_head aux_list_entry; + uint64_t rollback_key; }; /* * SCST device */ struct scst_device { - struct scst_dev_type *handler; /* corresponding dev handler */ - - struct scst_mem_lim dev_mem_lim; - unsigned short type; /* SCSI type of the device */ /************************************************************* @@ -1890,12 +1946,15 @@ struct scst_device { /* If set, dev is read only */ unsigned short rd_only:1; + /* Set if tgt_kobj was initialized */ + unsigned short dev_kobj_initialized:1; + /**************************************************************/ /************************************************************* ** Dev's control mode page related values. Updates serialized ** by scst_block_dev(). It's long to not interfere with the - ** above flags. + ** neighbour fields. *************************************************************/ unsigned long queue_alg:4; @@ -1913,6 +1972,23 @@ struct scst_device { /**************************************************************/ + /* Set if dev is persistently reserved. Protected by dev_pr_mutex. */ + unsigned short pr_is_set:1; + + /* + * Set if there is a thread changing or going to change PR state(s). + * Protected by dev_pr_mutex. + */ + unsigned short pr_writer_active:1; + + /* + * How many threads are checking commands for PR allowance. Used to + * implement lockless read-only fast path. + */ + atomic_t pr_readers_count; + + struct scst_dev_type *handler; /* corresponding dev handler */ + /* Used for storage of dev handler private stuff */ void *dh_priv; @@ -1924,16 +2000,20 @@ struct scst_device { /* Corresponding real SCSI device, could be NULL for virtual devices */ struct scsi_device *scsi_dev; - /* Lists of commands with lock, if dedicated threads are used */ + /* List of commands with lock, if dedicated threads are used */ struct scst_cmd_threads dev_cmd_threads; + /* Memory limits for this device */ + struct scst_mem_lim dev_mem_lim; + /* How many cmds alive on this dev */ atomic_t dev_cmd_count; - /* How many write cmds alive on this dev. Temporary, ToDo */ - atomic_t write_cmd_count; - - spinlock_t dev_lock; /* device lock */ + /* + * How many there are "on_dev" commands, i.e. ones those are being + * executed by the underlying SCSI/virtual device. + */ + atomic_t on_dev_count; /* * How many times device was blocked for new cmds execution. @@ -1941,11 +2021,48 @@ struct scst_device { */ int block_count; + /* How many write cmds alive on this dev. Temporary, ToDo */ + atomic_t write_cmd_count; + + /************************************************************* + ** Persistent reservation fields. Protected by dev_pr_mutex. + *************************************************************/ + + /* True if persist through power loss is activated */ + unsigned short pr_aptpl:1; + + /* Persistent reservation type */ + uint8_t pr_type; + + /* Persistent reservation scope */ + uint8_t pr_scope; + + /* Mutex to protect PR operations */ + struct mutex dev_pr_mutex; + + /* Persistent reservation generation value */ + uint32_t pr_generation; + + /* Reference to registrant - persistent reservation holder */ + struct scst_dev_registrant *pr_holder; + + /* List of dev's registrants */ + struct list_head dev_registrants_list; + /* - * How many there are "on_dev" commands, i.e. ones those are being - * executed by the underlying SCSI/virtual device. + * Count of connected tgt_devs from transports, which don't support + * PRs, i.e. don't have get_initiator_port_transport_id(). Protected + * by scst_mutex. */ - atomic_t on_dev_count; + int not_pr_supporting_tgt_devs_num; + + /* Persist through power loss files */ + char *pr_file_name; + char *pr_file_name1; + + /**************************************************************/ + + spinlock_t dev_lock; /* device lock */ struct list_head blocked_cmd_list; /* protected by dev_lock */ @@ -1979,9 +2096,6 @@ struct scst_device { /* Threads pool type of the device. Valid only if threads_num > 0. */ enum scst_dev_type_threads_pool_type threads_pool_type; - /* Set if tgt_kobj was initialized */ - unsigned int dev_kobj_initialized:1; - /* * Used to protect sysfs attributes to be called after this * object was unregistered. @@ -2094,6 +2208,9 @@ struct scst_tgt_dev { struct scst_session *sess; /* corresponding session */ struct scst_acg_dev *acg_dev; /* corresponding acg_dev */ + /* Reference to registrant to find quicker */ + struct scst_dev_registrant *registrant; + /* List entry in dev->dev_tgt_dev_list */ struct list_head dev_tgt_dev_list_entry; @@ -2258,12 +2375,12 @@ static inline int scst_register_target_template(struct scst_tgt_template *vtt) void scst_unregister_target_template(struct scst_tgt_template *vtt); -struct scst_tgt *scst_register(struct scst_tgt_template *vtt, +struct scst_tgt *scst_register_target(struct scst_tgt_template *vtt, const char *target_name); -void scst_unregister(struct scst_tgt *tgt); +void scst_unregister_target(struct scst_tgt *tgt); struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic, - const char *initiator_name, void *data, + const char *initiator_name, void *tgt_priv, void *result_fn_data, void (*result_fn) (struct scst_session *sess, void *data, int result)); void scst_unregister_session(struct scst_session *sess, int wait, void (*unreg_done_fn) (struct scst_session *sess)); @@ -3194,6 +3311,7 @@ struct scst_trace_log { }; extern struct mutex scst_mutex; +extern bool iscsi_tid_name_only; #ifdef CONFIG_SCST_PROC @@ -3395,6 +3513,9 @@ int scst_tape_generic_dev_done(struct scst_cmd *cmd, int scst_obtain_device_parameters(struct scst_device *dev); +void scst_reassign_persistent_sess_states(struct scst_session *new_sess, + struct scst_session *old_sess); + int scst_get_max_lun_commands(struct scst_session *sess, uint64_t lun); /* diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index bf73266aa..d845facff 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -63,12 +63,12 @@ /************************************************************* ** Values for task management functions *************************************************************/ -#define SCST_ABORT_TASK 0 -#define SCST_ABORT_TASK_SET 1 -#define SCST_CLEAR_ACA 2 -#define SCST_CLEAR_TASK_SET 3 -#define SCST_LUN_RESET 4 -#define SCST_TARGET_RESET 5 +#define SCST_ABORT_TASK 0 +#define SCST_ABORT_TASK_SET 1 +#define SCST_CLEAR_ACA 2 +#define SCST_CLEAR_TASK_SET 3 +#define SCST_LUN_RESET 4 +#define SCST_TARGET_RESET 5 /** SCST extensions **/ @@ -77,20 +77,20 @@ * Aborts all tasks there, resets the reservation, if any, and sets * up the I_T Nexus loss UA. */ -#define SCST_NEXUS_LOSS_SESS 6 +#define SCST_NEXUS_LOSS_SESS 6 /* Aborts all tasks in the corresponding session */ -#define SCST_ABORT_ALL_TASKS_SESS 7 +#define SCST_ABORT_ALL_TASKS_SESS 7 /* * Notifies about I_T nexus loss event. Aborts all tasks in all sessions * of the tgt, resets the reservations, if any, and sets up the I_T Nexus * loss UA. */ -#define SCST_NEXUS_LOSS 8 +#define SCST_NEXUS_LOSS 8 /* Aborts all tasks in all sessions of the tgt */ -#define SCST_ABORT_ALL_TASKS 9 +#define SCST_ABORT_ALL_TASKS 9 /* * Internal TM command issued by SCST in scst_unregister_session(). It is the @@ -99,9 +99,24 @@ * - it doesn't call task_mgmt_fn_done() * - it doesn't queue NEXUS LOSS UA. * - * Target driver shall NEVER use it!! + * Target drivers must NEVER use it!! */ -#define SCST_UNREG_SESS_TM 10 +#define SCST_UNREG_SESS_TM 10 + +/* + * Internal TM command issued by SCST in scst_pr_abort_reg(). It aborts all + * tasks from mcmd->origin_pr_cmd->tgt_dev, except mcmd->origin_pr_cmd. + * Additionally: + * - it signals pr_aborting_cmpl completion when all affected + * commands marked as aborted. + * - it doesn't call task_mgmt_affected_cmds_done() + * - it doesn't call task_mgmt_fn_done() + * - it calls mcmd->origin_pr_cmd->scst_cmd_done() when all affected + * commands aborted. + * + * Target drivers must NEVER use it!! + */ +#define SCST_PR_ABORT_ALL 11 /************************************************************* ** Values for mgmt cmd's status field. Codes taken from iSCSI @@ -128,18 +143,23 @@ enum scst_cmd_queue_type { ** CDB flags *************************************************************/ enum scst_cdb_flags { - SCST_TRANSFER_LEN_TYPE_FIXED = 0x001, - SCST_SMALL_TIMEOUT = 0x002, - SCST_LONG_TIMEOUT = 0x004, - SCST_UNKNOWN_LENGTH = 0x008, - SCST_INFO_VALID = 0x010, /* must be single bit */ - SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED = 0x020, - SCST_IMPLICIT_HQ = 0x040, - SCST_SKIP_UA = 0x080, - SCST_WRITE_MEDIUM = 0x100, - SCST_LOCAL_CMD = 0x200, - SCST_FULLY_LOCAL_CMD = 0x400, - SCST_REG_RESERVE_ALLOWED = 0x800, + SCST_TRANSFER_LEN_TYPE_FIXED = 0x0001, + SCST_SMALL_TIMEOUT = 0x0002, + SCST_LONG_TIMEOUT = 0x0004, + SCST_UNKNOWN_LENGTH = 0x0008, + SCST_INFO_VALID = 0x0010, /* must be single bit */ + SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED = 0x0020, + SCST_IMPLICIT_HQ = 0x0040, + SCST_SKIP_UA = 0x0080, + SCST_WRITE_MEDIUM = 0x0100, + SCST_LOCAL_CMD = 0x0200, + SCST_FULLY_LOCAL_CMD = 0x0400, + SCST_REG_RESERVE_ALLOWED = 0x0800, + SCST_WRITE_EXCL_ALLOWED = 0x1000, + SCST_EXCL_ACCESS_ALLOWED = 0x2000, +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED = 0x8000, +#endif }; /************************************************************* @@ -188,6 +208,9 @@ enum scst_cdb_flags { #define scst_sense_invalid_field_in_cdb ILLEGAL_REQUEST, 0x24, 0 #define scst_sense_invalid_field_in_parm_list ILLEGAL_REQUEST, 0x26, 0 #define scst_sense_parameter_value_invalid ILLEGAL_REQUEST, 0x26, 2 +#define scst_sense_invalid_release ILLEGAL_REQUEST, 0x26, 4 +#define scst_sense_parameter_list_length_invalid \ + ILLEGAL_REQUEST, 0x1A, 0 #define scst_sense_reset_UA UNIT_ATTENTION, 0x29, 0 #define scst_sense_nexus_loss_UA UNIT_ATTENTION, 0x29, 0x7 #define scst_sense_saving_params_unsup ILLEGAL_REQUEST, 0x39, 0 @@ -202,6 +225,9 @@ enum scst_cdb_flags { #define scst_sense_invalid_message ILLEGAL_REQUEST, 0x49, 0 #define scst_sense_cleared_by_another_ini_UA UNIT_ATTENTION, 0x2F, 0 #define scst_sense_capacity_data_changed UNIT_ATTENTION, 0x2A, 0x9 +#define scst_sense_reservation_preempted UNIT_ATTENTION, 0x2A, 0x03 +#define scst_sense_reservation_released UNIT_ATTENTION, 0x2A, 0x04 +#define scst_sense_registrations_preempted UNIT_ATTENTION, 0x2A, 0x05 #define scst_sense_reported_luns_data_changed UNIT_ATTENTION, 0x3F, 0xE #define scst_sense_inquery_data_changed UNIT_ATTENTION, 0x3F, 0x3 @@ -306,6 +332,16 @@ enum scst_cdb_flags { #define SCST_CONTR_MODE_FIXED_SENSE 0 #define SCST_CONTR_MODE_DESCR_SENSE 1 +/************************************************************* + ** TransportID protocol identifiers + *************************************************************/ + +#define SCSI_TRANSPORTID_PROTOCOLID_FCP2 0 +#define SCSI_TRANSPORTID_PROTOCOLID_SPI5 1 +#define SCSI_TRANSPORTID_PROTOCOLID_SRP 4 +#define SCSI_TRANSPORTID_PROTOCOLID_ISCSI 5 +#define SCSI_TRANSPORTID_PROTOCOLID_SAS 6 + /************************************************************* ** Misc SCSI constants *************************************************************/ @@ -366,6 +402,10 @@ enum scst_cdb_flags { *************************************************************/ #define SCST_SYSFS_BLOCK_SIZE PAGE_SIZE +#define SCST_PR_DIR "/var/lib/scst/pr" + +#define TID_COMMON_SIZE 24 + #define SCST_SYSFS_KEY_MARK "[key]" #define SCST_MIN_REL_TGT_ID 1 diff --git a/scst/include/scst_debug.h b/scst/include/scst_debug.h index ebe9d5770..c509481b8 100644 --- a/scst/include/scst_debug.h +++ b/scst/include/scst_debug.h @@ -102,6 +102,7 @@ #define TRACE_SCSI 0x00001000 #define TRACE_SPECIAL 0x00002000 /* filtering debug, etc */ #define TRACE_FLOW_CONTROL 0x00004000 /* flow control in action */ +#define TRACE_PRES 0x00008000 #define TRACE_ALL 0xffffffff /* Flags 0xXXXX0000 are local for users */ @@ -204,6 +205,9 @@ do { \ #define TRACE_MGMT_DBG(args...) TRACE_DBG_FLAG(TRACE_MGMT_DEBUG, args) #define TRACE_MGMT_DBG_SPECIAL(args...) \ TRACE_DBG_FLAG(TRACE_MGMT_DEBUG|TRACE_SPECIAL, args) +#define TRACE_PR(args...) TRACE_DBG_FLAG(TRACE_PRES, args) + +const char *debug_transport_id_to_initiator_name(const uint8_t *transport_id); #define TRACE_BUFFER(message, buff, len) \ do { \ @@ -318,6 +322,7 @@ do { \ #define TRACE_DBG_SPECIAL(format, args...) do {} while (0) #define TRACE_MGMT_DBG(format, args...) do {} while (0) #define TRACE_MGMT_DBG_SPECIAL(format, args...) do {} while (0) +#define TRACE_PR(format, args...) do {} while (0) #define TRACE_BUFFER(message, buff, len) do {} while (0) #define TRACE_BUFF_FLAG(flag, message, buff, len) do {} while (0) diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index b1033250c..90ae315ac 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -96,6 +96,7 @@ struct scst_user_dev_desc { int32_t sgv_purge_interval; struct scst_user_opt opt; uint32_t block_size; + uint8_t enable_pr_cmds_notifications; char name[SCST_MAX_NAME]; char sgv_name[SCST_MAX_NAME]; }; diff --git a/scst/kernel/in-tree/Kconfig.scst b/scst/kernel/in-tree/Kconfig.scst index 83de13c56..b972c328f 100644 --- a/scst/kernel/in-tree/Kconfig.scst +++ b/scst/kernel/in-tree/Kconfig.scst @@ -92,20 +92,18 @@ config SCST_STRICT_SECURITY If unsure, say "N". -config SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ - bool "Allow pass-through commands to be sent from soft-IRQ context" +config SCST_TEST_IO_IN_SIRQ + bool "Allow test I/O from soft-IRQ context" depends on SCST help - Allows SCST to submit SCSI pass-through commands to real SCSI devices - via the SCSI middle layer using scsi_execute_async() function from - soft-IRQ context (tasklets). This used to be the default, but - currently it seems the SCSI middle layer starts expecting only thread - context on the IO submit path, so it is disabled now by default. - Enabling it will decrease amount of context switches and improve - performance. It is more or less safe. In the worst case, if in your - configuration the SCSI middle layer really doesn't expect SIRQ - context in scsi_execute_async() function, you will get a warning - message in the kernel log. + Allows SCST to submit selected SCSI commands (TUR and + READ/WRITE) from soft-IRQ context (tasklets). Enabling it will + decrease amount of context switches and slightly improve + performance. The goal of this option is to be able to measure + overhead of the context switches. See more info about it in + README.scst. + + WARNING! Improperly used, this option can lead you to a kernel crash! If unsure, say "N". diff --git a/scst/src/Makefile b/scst/src/Makefile index 0541586f8..cf259fb53 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -43,6 +43,7 @@ scst-y += scst_proc.o #scst-y += scst_sysfs.o scst-y += scst_mem.o scst-y += scst_debug.o +scst-y += scst_pres.o obj-$(CONFIG_SCST) += scst.o dev_handlers/ obj-$(BUILD_DEV) += $(DEV_HANDLERS_DIR)/ @@ -86,6 +87,7 @@ ifneq ($(MOD_VERS),) install -m 644 Module.symvers $(INSTALL_DIR_H) endif -/sbin/depmod -a $(KVER) + mkdir -p /var/lib/scst/pr @echo "****************************************************************" @echo "*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*" @echo "*!! !!*" @@ -122,8 +124,8 @@ EXTRA_CFLAGS += -I$(SCST_INC_DIR) -Wextra -Wno-unused-parameter \ EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS #EXTRA_CFLAGS += -DCONFIG_SCST_USE_EXPECTED_VALUES -#EXTRA_CFLAGS += -DALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ -#EXTRA_CFLAGS += -DABORT_CONSIDER_FINISHED_TASKS_AS_NOT_EXISTING +#EXTRA_CFLAGS += -DCONFIG_SCST_TEST_IO_IN_SIRQ +#EXTRA_CFLAGS += -DCONFIG_SCST_ABORT_CONSIDER_FINISHED_TASKS_AS_NOT_EXISTING #EXTRA_CFLAGS += -fno-inline diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c index cc2258e09..d9b4bf0f0 100644 --- a/scst/src/dev_handlers/scst_disk.c +++ b/scst/src/dev_handlers/scst_disk.c @@ -51,7 +51,6 @@ static struct scst_dev_type disk_devtype = { .type = TYPE_DISK, .parse_atomic = 1, .dev_done_atomic = 1, - .exec_atomic = 1, .attach = disk_attach, .detach = disk_detach, .parse = disk_parse, @@ -67,7 +66,6 @@ static struct scst_dev_type disk_devtype_perf = { .type = TYPE_DISK, .parse_atomic = 1, .dev_done_atomic = 1, - .exec_atomic = 1, .attach = disk_attach, .detach = disk_detach, .parse = disk_parse, diff --git a/scst/src/dev_handlers/scst_modisk.c b/scst/src/dev_handlers/scst_modisk.c index defb92214..7f1d99013 100644 --- a/scst/src/dev_handlers/scst_modisk.c +++ b/scst/src/dev_handlers/scst_modisk.c @@ -51,7 +51,6 @@ static struct scst_dev_type modisk_devtype = { .type = TYPE_MOD, .parse_atomic = 1, .dev_done_atomic = 1, - .exec_atomic = 1, .attach = modisk_attach, .detach = modisk_detach, .parse = modisk_parse, @@ -67,7 +66,6 @@ static struct scst_dev_type modisk_devtype_perf = { .type = TYPE_MOD, .parse_atomic = 1, .dev_done_atomic = 1, - .exec_atomic = 1, .attach = modisk_attach, .detach = modisk_detach, .parse = modisk_parse, diff --git a/scst/src/dev_handlers/scst_tape.c b/scst/src/dev_handlers/scst_tape.c index fddc7a504..ee9e2e140 100644 --- a/scst/src/dev_handlers/scst_tape.c +++ b/scst/src/dev_handlers/scst_tape.c @@ -56,7 +56,6 @@ static struct scst_dev_type tape_devtype = { .type = TYPE_TAPE, .parse_atomic = 1, .dev_done_atomic = 1, - .exec_atomic = 1, .attach = tape_attach, .detach = tape_detach, .parse = tape_parse, @@ -72,7 +71,6 @@ static struct scst_dev_type tape_devtype_perf = { .type = TYPE_TAPE, .parse_atomic = 1, .dev_done_atomic = 1, - .exec_atomic = 1, .attach = tape_attach, .detach = tape_detach, .parse = tape_parse, diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index e6d02a520..a8a83f123 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -928,18 +928,6 @@ static int dev_user_exec(struct scst_cmd *cmd) TRACE_ENTRY(); -#if 0 /* We set exec_atomic in 0 to let SCST core know that we need a thread - * context to complete the necessary actions, but all we are going to - * do in this function is, in fact, atomic, so let's skip this check. - */ - if (scst_cmd_atomic(cmd)) { - TRACE_DBG("%s", "User exec() can not be called in atomic " - "context, rescheduling to the thread"); - res = SCST_EXEC_NEED_THREAD; - goto out; - } -#endif - TRACE_DBG("Preparing EXEC for user space (ucmd=%p, h=%d, " "bufflen %d, data_len %d, ubuff %lx)", ucmd, ucmd->h, cmd->bufflen, cmd->data_len, ucmd->ubuff); @@ -2891,7 +2879,6 @@ static int dev_user_register_dev(struct file *file, dev->devtype.threads_num = -1; dev->devtype.parse_atomic = 1; dev->devtype.alloc_data_buf_atomic = 1; - dev->devtype.exec_atomic = 0; /* no point to make it 1 */ dev->devtype.dev_done_atomic = 1; #ifdef CONFIG_SCST_PROC dev->devtype.no_proc = 1; @@ -2905,8 +2892,8 @@ static int dev_user_register_dev(struct file *file, dev->devtype.exec = dev_user_exec; dev->devtype.on_free_cmd = dev_user_on_free_cmd; dev->devtype.task_mgmt_fn = dev_user_task_mgmt_fn; - - dev->devtype.parent = &dev_user_devtype; + if (dev_desc->enable_pr_cmds_notifications) + dev->devtype.pr_cmds_notifications = 1; init_completion(&dev->cleanup_cmpl); dev->block = block; diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 247e808b8..962cced3e 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -429,7 +429,6 @@ static struct scst_dev_type vdisk_file_devtype = { .exec_sync = 1, .threads_num = -1, .parse_atomic = 1, - .exec_atomic = 0, .dev_done_atomic = 1, .attach = vdisk_attach, .detach = vdisk_detach, @@ -465,7 +464,6 @@ static struct scst_dev_type vdisk_blk_devtype = { .type = TYPE_DISK, .threads_num = 0, .parse_atomic = 1, - .exec_atomic = 0, .dev_done_atomic = 1, #ifdef CONFIG_SCST_PROC .no_proc = 1, @@ -499,7 +497,6 @@ static struct scst_dev_type vdisk_null_devtype = { .type = TYPE_DISK, .threads_num = 0, .parse_atomic = 1, - .exec_atomic = 1, .dev_done_atomic = 1, #ifdef CONFIG_SCST_PROC .no_proc = 1, @@ -533,7 +530,6 @@ static struct scst_dev_type vcdrom_devtype = { .exec_sync = 1, .threads_num = -1, .parse_atomic = 1, - .exec_atomic = 0, .dev_done_atomic = 1, .attach = vdisk_attach, .detach = vdisk_detach, @@ -643,7 +639,7 @@ static int vdisk_get_file_size(const char *filename, bool blockio, } if (S_ISREG(inode->i_mode)) - /* Nothing to do*/; + /* Nothing to do */; else if (S_ISBLK(inode->i_mode)) inode = inode->i_bdev->bd_inode; else { @@ -1286,8 +1282,7 @@ static void vdisk_exec_inquiry(struct scst_cmd *cmd) TRACE_ENTRY(); - buf = kzalloc(INQ_BUF_SZ, - scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL); + buf = kzalloc(INQ_BUF_SZ, GFP_KERNEL); if (buf == NULL) { scst_set_busy(cmd); goto out; @@ -1357,8 +1352,25 @@ static void vdisk_exec_inquiry(struct scst_cmd *cmd) num += 4; - /* Binary */ - buf[num + 0] = 0x01; + /* + * Relative target port identifier + */ + buf[num + 0] = 0x01; /* binary */ + /* Relative target port id */ + buf[num + 1] = 0x10 | 0x04; + + put_unaligned(cpu_to_be16(cmd->tgt->rel_tgt_id), + (__be16 *)&buf[num + 4 + 2]); + + buf[num + 3] = 4; + num += buf[num + 3]; + + num += 4; + + /* + * IEEE id + */ + buf[num + 0] = 0x01; /* binary */ /* EUI-64 */ buf[num + 1] = 0x02; @@ -1662,8 +1674,7 @@ static void vdisk_exec_mode_sense(struct scst_cmd *cmd) TRACE_ENTRY(); - buf = kzalloc(MSENSE_BUF_SZ, - scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL); + buf = kzalloc(MSENSE_BUF_SZ, GFP_KERNEL); if (buf == NULL) { scst_set_busy(cmd); goto out; @@ -2206,12 +2217,9 @@ static void vdisk_exec_prevent_allow_medium_removal(struct scst_cmd *cmd) TRACE_DBG("PERSIST/PREVENT 0x%02x", cmd->cdb[4]); - if (cmd->dev->type == TYPE_ROM) { - spin_lock(&virt_dev->flags_lock); - virt_dev->prevent_allow_medium_removal = - cmd->cdb[4] & 0x01 ? 1 : 0; - spin_unlock(&virt_dev->flags_lock); - } + spin_lock(&virt_dev->flags_lock); + virt_dev->prevent_allow_medium_removal = cmd->cdb[4] & 0x01 ? 1 : 0; + spin_unlock(&virt_dev->flags_lock); return; } @@ -2876,7 +2884,16 @@ static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd, dev->swp = DEF_SWP; dev->tas = DEF_TAS; + spin_lock(&virt_dev->flags_lock); virt_dev->prevent_allow_medium_removal = 0; + spin_unlock(&virt_dev->flags_lock); + } else if (mcmd->fn == SCST_PR_ABORT_ALL) { + struct scst_device *dev = tgt_dev->dev; + struct scst_vdisk_dev *virt_dev = + (struct scst_vdisk_dev *)dev->dh_priv; + spin_lock(&virt_dev->flags_lock); + virt_dev->prevent_allow_medium_removal = 0; + spin_unlock(&virt_dev->flags_lock); } TRACE_EXIT(); diff --git a/scst/src/scst_debug.c b/scst/src/scst_debug.c index a1967f64a..4cd08455a 100644 --- a/scst/src/scst_debug.c +++ b/scst/src/scst_debug.c @@ -137,4 +137,95 @@ void debug_print_buffer(const void *data, int len) } EXPORT_SYMBOL(debug_print_buffer); +#ifdef CONFIG_SCST_DEBUG + +/* + * This function converts transport_id in a string form into internal per-CPU + * static buffer. This buffer isn't anyhow protected, because it's acceptable + * if the name corrupted in the debug logs because of the race for this buffer. + * + * Note! You can't call this function 2 or more times in a single logging + * (printk) statement, because then each new call of this functon will override + * data written in this buffer by the previous call. You should instead split + * that logging statement on smaller statements each calling + * debug_transport_id_to_initiator_name() only once. + */ +const char *debug_transport_id_to_initiator_name(const uint8_t *transport_id) +{ + /* + * No external protection, because it's acceptable if the name + * corrupted in the debug logs because of the race for this + * buffer. + */ +#define SIZEOF_NAME_BUF 256 + static char name_bufs[NR_CPUS][SIZEOF_NAME_BUF]; + char *name_buf; + unsigned long flags; + + sBUG_ON(transport_id == NULL); /* better to catch it not under lock */ + + spin_lock_irqsave(&trace_buf_lock, flags); + + name_buf = name_bufs[smp_processor_id()]; + + /* + * To prevent external racing with us users from accidentally + * missing their NULL terminator. + */ + memset(name_buf, 0, SIZEOF_NAME_BUF); + smp_mb(); + + switch (transport_id[0] & 0x0f) { + case SCSI_TRANSPORTID_PROTOCOLID_ISCSI: + scnprintf(name_buf, SIZEOF_NAME_BUF, "%s", + &transport_id[4]); + break; + case SCSI_TRANSPORTID_PROTOCOLID_FCP2: + scnprintf(name_buf, SIZEOF_NAME_BUF, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + transport_id[8], transport_id[9], + transport_id[10], transport_id[11], + transport_id[12], transport_id[13], + transport_id[14], transport_id[15]); + break; + case SCSI_TRANSPORTID_PROTOCOLID_SPI5: + scnprintf(name_buf, SIZEOF_NAME_BUF, + "%x:%x", be16_to_cpu((uint16_t)transport_id[2]), + be16_to_cpu((uint16_t)transport_id[6])); + break; + case SCSI_TRANSPORTID_PROTOCOLID_SRP: + scnprintf(name_buf, SIZEOF_NAME_BUF, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + transport_id[8], transport_id[9], + transport_id[10], transport_id[11], + transport_id[12], transport_id[13], + transport_id[14], transport_id[15], + transport_id[16], transport_id[17], + transport_id[18], transport_id[19], + transport_id[20], transport_id[21], + transport_id[22], transport_id[23]); + break; + case SCSI_TRANSPORTID_PROTOCOLID_SAS: + scnprintf(name_buf, SIZEOF_NAME_BUF, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + transport_id[4], transport_id[5], + transport_id[6], transport_id[7], + transport_id[8], transport_id[9], + transport_id[10], transport_id[11]); + break; + default: + scnprintf(name_buf, SIZEOF_NAME_BUF, + "(Not known protocol ID %x)", transport_id[0] & 0x0f); + break; + } + + spin_unlock_irqrestore(&trace_buf_lock, flags); + + return name_buf; +#undef SIZEOF_NAME_BUF +} + +#endif /* CONFIG_SCST_DEBUG */ + #endif /* CONFIG_SCST_DEBUG || CONFIG_SCST_TRACING */ diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 164244eeb..c6b56de4b 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -36,6 +36,8 @@ #include "scst_priv.h" #include "scst_mem.h" +#include "scst_pres.h" + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) struct scsi_io_context { unsigned int full_cdb_used:1; @@ -160,17 +162,25 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0x00, "MMMMMMMMMMMMMMMM", "TEST UNIT READY", /* let's be HQ to don't look dead under high load */ SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_IMPLICIT_HQ| - SCST_REG_RESERVE_ALLOWED, + SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_EXCL_ACCESS_ALLOWED, 0, get_trans_len_none}, {0x01, " M ", "REWIND", SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none}, {0x01, "O V OO OO ", "REZERO UNIT", - SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, + SCST_DATA_NONE, SCST_WRITE_EXCL_ALLOWED, + 0, get_trans_len_none}, {0x02, "VVVVVV V ", "REQUEST BLOCK ADDR", SCST_DATA_NONE, SCST_SMALL_TIMEOUT, 0, get_trans_len_none}, {0x03, "MMMMMMMMMMMMMMMM", "REQUEST SENSE", SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_SKIP_UA|SCST_LOCAL_CMD| - SCST_REG_RESERVE_ALLOWED, + SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED| + SCST_EXCL_ACCESS_ALLOWED, 4, get_trans_len_1}, {0x04, "M O O ", "FORMAT UNIT", SCST_DATA_WRITE, SCST_LONG_TIMEOUT|SCST_UNKNOWN_LENGTH|SCST_WRITE_MEDIUM, @@ -178,22 +188,36 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0x04, " O ", "FORMAT", SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none}, {0x05, "VMVVVV V ", "READ BLOCK LIMITS", - SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_REG_RESERVE_ALLOWED, + SCST_DATA_READ, SCST_SMALL_TIMEOUT| + SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED| + SCST_EXCL_ACCESS_ALLOWED, 0, get_trans_len_block_limit}, {0x07, " O ", "INITIALIZE ELEMENT STATUS", SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none}, {0x07, "OVV O OV ", "REASSIGN BLOCKS", SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none}, {0x08, "O ", "READ(6)", - SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 4, get_trans_len_1_256}, + SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_EXCL_ALLOWED, + 4, get_trans_len_1_256}, {0x08, " MV OO OV ", "READ(6)", - SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 2, get_trans_len_3}, + SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED| + SCST_WRITE_EXCL_ALLOWED, + 2, get_trans_len_3}, {0x08, " M ", "GET MESSAGE(6)", SCST_DATA_READ, FLAG_NONE, 2, get_trans_len_3}, {0x08, " O ", "RECEIVE", SCST_DATA_READ, FLAG_NONE, 2, get_trans_len_3}, {0x0A, "O ", "WRITE(6)", - SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, + SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_MEDIUM, 4, get_trans_len_1_256}, {0x0A, " M O OV ", "WRITE(6)", SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, @@ -216,30 +240,41 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_DATA_NONE, SCST_LONG_TIMEOUT|SCST_WRITE_MEDIUM, 0, get_trans_len_none}, {0x0F, "VOVVVV V ", "READ REVERSE", - SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 2, get_trans_len_3}, + SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED| + SCST_WRITE_EXCL_ALLOWED, + 2, get_trans_len_3}, {0x10, "VM V V ", "WRITE FILEMARKS", SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none}, {0x10, " O O ", "SYNCHRONIZE BUFFER", SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, {0x11, "VMVVVV ", "SPACE", - SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none}, + SCST_DATA_NONE, SCST_LONG_TIMEOUT| + SCST_WRITE_EXCL_ALLOWED, + 0, get_trans_len_none}, {0x12, "MMMMMMMMMMMMMMMM", "INQUIRY", SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_IMPLICIT_HQ|SCST_SKIP_UA| - SCST_REG_RESERVE_ALLOWED, + SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED|SCST_EXCL_ACCESS_ALLOWED, 4, get_trans_len_1}, {0x13, "VOVVVV ", "VERIFY(6)", SCST_DATA_NONE, SCST_TRANSFER_LEN_TYPE_FIXED| - SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED, + SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED| + SCST_WRITE_EXCL_ALLOWED, 2, get_trans_len_3}, {0x14, "VOOVVV ", "RECOVER BUFFERED DATA", - SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 2, get_trans_len_3}, + SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED| + SCST_WRITE_EXCL_ALLOWED, + 2, get_trans_len_3}, {0x15, "OMOOOOOOOOOOOOOO", "MODE SELECT(6)", SCST_DATA_WRITE, SCST_LOCAL_CMD, 4, get_trans_len_1}, {0x16, "MMMMMMMMMMMMMMMM", "RESERVE", - SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD, + SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD| + SCST_WRITE_EXCL_ALLOWED|SCST_EXCL_ACCESS_ALLOWED, 0, get_trans_len_none}, {0x17, "MMMMMMMMMMMMMMMM", "RELEASE", - SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD|SCST_REG_RESERVE_ALLOWED, + SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD| + SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED|SCST_EXCL_ACCESS_ALLOWED, 0, get_trans_len_none}, {0x18, "OOOOOOOO ", "COPY", SCST_DATA_WRITE, SCST_LONG_TIMEOUT, 2, get_trans_len_3}, @@ -272,25 +307,39 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0x24, "V VVM ", "SET WINDOW", SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_3}, {0x25, "M MM M ", "READ CAPACITY", - SCST_DATA_READ, SCST_IMPLICIT_HQ|SCST_REG_RESERVE_ALLOWED, + SCST_DATA_READ, SCST_IMPLICIT_HQ| + SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED| + SCST_EXCL_ACCESS_ALLOWED, 0, get_trans_len_read_capacity}, {0x25, " O ", "GET WINDOW", SCST_DATA_READ, FLAG_NONE, 6, get_trans_len_3}, {0x28, "M MMMM ", "READ(10)", - SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 7, get_trans_len_2}, + SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_EXCL_ALLOWED, + 7, get_trans_len_2}, {0x28, " O ", "GET MESSAGE(10)", SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2}, {0x29, "V VV O ", "READ GENERATION", SCST_DATA_READ, FLAG_NONE, 8, get_trans_len_1}, {0x2A, "O MO M ", "WRITE(10)", - SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, + SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_MEDIUM, 7, get_trans_len_2}, {0x2A, " O ", "SEND MESSAGE(10)", SCST_DATA_WRITE, FLAG_NONE, 7, get_trans_len_2}, {0x2A, " O ", "SEND(10)", SCST_DATA_WRITE, FLAG_NONE, 7, get_trans_len_2}, {0x2B, " O ", "LOCATE", - SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none}, + SCST_DATA_NONE, SCST_LONG_TIMEOUT| + SCST_WRITE_EXCL_ALLOWED, + 0, get_trans_len_none}, {0x2B, " O ", "POSITION TO ELEMENT", SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none}, {0x2B, "O OO O ", "SEEK(10)", @@ -305,22 +354,27 @@ static const struct scst_sdbops scst_scsi_op_table[] = { 7, get_trans_len_2}, {0x2F, "O OO O ", "VERIFY(10)", SCST_DATA_NONE, SCST_TRANSFER_LEN_TYPE_FIXED| - SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED, + SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED| + SCST_WRITE_EXCL_ALLOWED, 7, get_trans_len_2}, {0x33, "O OO O ", "SET LIMITS(10)", SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, {0x34, " O ", "READ POSITION", - SCST_DATA_READ, SCST_SMALL_TIMEOUT, 7, get_trans_len_read_pos}, + SCST_DATA_READ, SCST_SMALL_TIMEOUT| + SCST_WRITE_EXCL_ALLOWED, + 7, get_trans_len_read_pos}, {0x34, " O ", "GET DATA BUFFER STATUS", SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2}, {0x34, "O OO O ", "PRE-FETCH", - SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, + SCST_DATA_NONE, SCST_WRITE_EXCL_ALLOWED, + 0, get_trans_len_none}, {0x35, "O OO O ", "SYNCHRONIZE CACHE", SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, {0x36, "O OO O ", "LOCK UNLOCK CACHE", SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, {0x37, "O O ", "READ DEFECT DATA(10)", - SCST_DATA_READ, FLAG_NONE, 8, get_trans_len_1}, + SCST_DATA_READ, SCST_WRITE_EXCL_ALLOWED, + 8, get_trans_len_1}, {0x37, " O ", "INIT ELEMENT STATUS WRANGE", SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none}, {0x38, " O O ", "MEDIUM SCAN", @@ -350,7 +404,10 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0x43, " O ", "READ TOC/PMA/ATIP", SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2}, {0x44, " M ", "REPORT DENSITY SUPPORT", - SCST_DATA_READ, SCST_REG_RESERVE_ALLOWED, 7, get_trans_len_2}, + SCST_DATA_READ, SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED| + SCST_EXCL_ACCESS_ALLOWED, + 7, get_trans_len_2}, {0x44, " O ", "READ HEADER", SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_2}, {0x45, " O ", "PLAY AUDIO(10)", @@ -370,7 +427,10 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0x4C, "OOOOOOOOOOOOOOOO", "LOG SELECT", SCST_DATA_WRITE, SCST_SMALL_TIMEOUT, 7, get_trans_len_2}, {0x4D, "OOOOOOOOOOOOOOOO", "LOG SENSE", - SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_REG_RESERVE_ALLOWED, + SCST_DATA_READ, SCST_SMALL_TIMEOUT| + SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED| + SCST_EXCL_ACCESS_ALLOWED, 7, get_trans_len_2}, {0x4E, " O ", "STOP PLAY/SCAN", SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, @@ -392,7 +452,8 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD, 0, get_trans_len_none}, {0x57, "OOOOOOOOOOOOOOOO", "RELEASE(10)", - SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD|SCST_REG_RESERVE_ALLOWED, + SCST_DATA_NONE, SCST_SMALL_TIMEOUT|SCST_LOCAL_CMD| + SCST_REG_RESERVE_ALLOWED, 0, get_trans_len_none}, {0x58, " O ", "REPAIR TRACK", SCST_DATA_NONE, SCST_WRITE_MEDIUM, 0, get_trans_len_none}, @@ -405,9 +466,17 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0x5D, " O ", "SEND CUE SHEET", SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_3}, {0x5E, "OOOOO OOOO ", "PERSISTENT RESERV IN", - SCST_DATA_READ, FLAG_NONE, 5, get_trans_len_4}, + SCST_DATA_READ, SCST_SMALL_TIMEOUT| + SCST_LOCAL_CMD| + SCST_WRITE_EXCL_ALLOWED| + SCST_EXCL_ACCESS_ALLOWED, + 5, get_trans_len_4}, {0x5F, "OOOOO OOOO ", "PERSISTENT RESERV OUT", - SCST_DATA_WRITE, FLAG_NONE, 5, get_trans_len_4}, + SCST_DATA_WRITE, SCST_SMALL_TIMEOUT| + SCST_LOCAL_CMD| + SCST_WRITE_EXCL_ALLOWED| + SCST_EXCL_ACCESS_ALLOWED, + 5, get_trans_len_4}, /* 16-bytes length CDB */ {0x80, "O OO O ", "XDWRITE EXTENDED", @@ -423,13 +492,28 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0x84, "OOOOOOOOOOOOOOOO", "RECEIVE COPY RESULT", SCST_DATA_WRITE, FLAG_NONE, 10, get_trans_len_4}, {0x86, "OOOOOOOOOO ", "ACCESS CONTROL IN", - SCST_DATA_NONE, SCST_REG_RESERVE_ALLOWED, 0, get_trans_len_none}, + SCST_DATA_NONE, SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED| + SCST_EXCL_ACCESS_ALLOWED, + 0, get_trans_len_none}, {0x87, "OOOOOOOOOO ", "ACCESS CONTROL OUT", - SCST_DATA_NONE, SCST_REG_RESERVE_ALLOWED, 0, get_trans_len_none}, + SCST_DATA_NONE, SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED| + SCST_EXCL_ACCESS_ALLOWED, + 0, get_trans_len_none}, {0x88, "M MMMM ", "READ(16)", - SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 10, get_trans_len_4}, + SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_EXCL_ALLOWED, + 10, get_trans_len_4}, {0x8A, "O OO O ", "WRITE(16)", - SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, + SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_MEDIUM, 10, get_trans_len_4}, {0x8C, "OOOOOOOOOO ", "READ ATTRIBUTE", SCST_DATA_READ, FLAG_NONE, 10, get_trans_len_4}, @@ -443,15 +527,20 @@ static const struct scst_sdbops scst_scsi_op_table[] = { SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED, 10, get_trans_len_4}, {0x90, "O OO O ", "PRE-FETCH(16)", - SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, + SCST_DATA_NONE, SCST_WRITE_EXCL_ALLOWED, + 0, get_trans_len_none}, {0x91, "O OO O ", "SYNCHRONIZE CACHE(16)", SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, {0x91, " M ", "SPACE(16)", - SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none}, + SCST_DATA_NONE, SCST_LONG_TIMEOUT| + SCST_WRITE_EXCL_ALLOWED, + 0, get_trans_len_none}, {0x92, "O OO O ", "LOCK UNLOCK CACHE(16)", SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, {0x92, " O ", "LOCATE(16)", - SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none}, + SCST_DATA_NONE, SCST_LONG_TIMEOUT| + SCST_WRITE_EXCL_ALLOWED, + 0, get_trans_len_none}, {0x93, "O O ", "WRITE SAME(16)", SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, 10, get_trans_len_4}, @@ -465,14 +554,17 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0xA0, "VVVVVVVVVV M ", "REPORT LUNS", SCST_DATA_READ, SCST_SMALL_TIMEOUT|SCST_IMPLICIT_HQ|SCST_SKIP_UA| SCST_FULLY_LOCAL_CMD|SCST_LOCAL_CMD| - SCST_REG_RESERVE_ALLOWED, + SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED|SCST_EXCL_ACCESS_ALLOWED, 6, get_trans_len_4}, {0xA1, " O ", "BLANK", SCST_DATA_NONE, SCST_LONG_TIMEOUT, 0, get_trans_len_none}, {0xA3, " O ", "SEND KEY", SCST_DATA_WRITE, FLAG_NONE, 8, get_trans_len_2}, {0xA3, "OOOOO OOOO ", "REPORT DEVICE IDENTIDIER", - SCST_DATA_READ, SCST_REG_RESERVE_ALLOWED, 6, get_trans_len_4}, + SCST_DATA_READ, SCST_REG_RESERVE_ALLOWED| + SCST_WRITE_EXCL_ALLOWED|SCST_EXCL_ACCESS_ALLOWED, + 6, get_trans_len_4}, {0xA3, " M ", "MAINTENANCE(IN)", SCST_DATA_READ, FLAG_NONE, 6, get_trans_len_4}, {0xA4, " O ", "REPORT KEY", @@ -490,11 +582,20 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0xA8, " O ", "GET MESSAGE(12)", SCST_DATA_READ, FLAG_NONE, 6, get_trans_len_4}, {0xA8, "O OO O ", "READ(12)", - SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED, 6, get_trans_len_4}, + SCST_DATA_READ, SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_EXCL_ALLOWED, + 6, get_trans_len_4}, {0xA9, " O ", "PLAY TRACK RELATIVE(12)", SCST_DATA_NONE, FLAG_NONE, 0, get_trans_len_none}, {0xAA, "O OO O ", "WRITE(12)", - SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED|SCST_WRITE_MEDIUM, + SCST_DATA_WRITE, SCST_TRANSFER_LEN_TYPE_FIXED| +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + SCST_TEST_IO_IN_SIRQ_ALLOWED| +#endif + SCST_WRITE_MEDIUM, 6, get_trans_len_4}, {0xAA, " O ", "SEND MESSAGE(12)", SCST_DATA_WRITE, FLAG_NONE, 6, get_trans_len_4}, @@ -509,7 +610,8 @@ static const struct scst_sdbops scst_scsi_op_table[] = { 6, get_trans_len_4}, {0xAF, "O OO O ", "VERIFY(12)", SCST_DATA_NONE, SCST_TRANSFER_LEN_TYPE_FIXED| - SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED, + SCST_VERIFY_BYTCHK_MISMATCH_ALLOWED| + SCST_WRITE_EXCL_ALLOWED, 6, get_trans_len_4}, #if 0 /* No need to support at all */ {0xB0, " OO O ", "SEARCH DATA HIGH(12)", @@ -528,7 +630,8 @@ static const struct scst_sdbops scst_scsi_op_table[] = { {0xB6, " M ", "SET STREAMING", SCST_DATA_WRITE, FLAG_NONE, 9, get_trans_len_2}, {0xB7, " O ", "READ DEFECT DATA(12)", - SCST_DATA_READ, FLAG_NONE, 9, get_trans_len_1}, + SCST_DATA_READ, SCST_WRITE_EXCL_ALLOWED, + 9, get_trans_len_1}, {0xB8, " O ", "READ ELEMENT STATUS", SCST_DATA_READ, FLAG_NONE, 7, get_trans_len_3_read_elem_stat}, {0xB9, " O ", "READ CD MSF", @@ -574,8 +677,8 @@ static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev); static void scst_release_space(struct scst_cmd *cmd); static void scst_unblock_cmds(struct scst_device *dev); static void scst_clear_reservation(struct scst_tgt_dev *tgt_dev); -static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, - struct scst_acg_dev *acg_dev); +static int scst_alloc_add_tgt_dev(struct scst_session *sess, + struct scst_acg_dev *acg_dev, struct scst_tgt_dev **out_tgt_dev); static void scst_tgt_retry_timer_fn(unsigned long arg); #ifdef CONFIG_SCST_DEBUG_TM @@ -1540,7 +1643,7 @@ static void scst_check_reassign_sess(struct scst_session *sess) { struct scst_acg *acg, *old_acg; struct scst_acg_dev *acg_dev; - int i; + int i, rc; struct list_head *shead; struct scst_tgt_dev *tgt_dev; bool luns_changed = false; @@ -1592,8 +1695,10 @@ retry_add: TRACE_MGMT_DBG("sess %p: Allocing new tgt_dev for LUN %lld", sess, (unsigned long long)acg_dev->lun); - tgt_dev = scst_alloc_add_tgt_dev(sess, acg_dev); - if (tgt_dev == NULL) { + rc = scst_alloc_add_tgt_dev(sess, acg_dev, &tgt_dev); + if (rc == -EPERM) + continue; + else if (rc != 0) { add_failed = true; break; } @@ -2026,7 +2131,8 @@ restart: return; } -bool scst_is_relative_target_port_id_unique(uint16_t id, struct scst_tgt *t) +bool scst_is_relative_target_port_id_unique(uint16_t id, + const struct scst_tgt *t) { bool res = true; struct scst_tgt_template *tgtt; @@ -2165,6 +2271,15 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev) dev->dev_double_ua_possible = 1; dev->queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER; + mutex_init(&dev->dev_pr_mutex); + atomic_set(&dev->pr_readers_count, 0); + dev->pr_generation = 0; + dev->pr_is_set = 0; + dev->pr_holder = NULL; + dev->pr_scope = SCOPE_LU; + dev->pr_type = TYPE_UNSPECIFIED; + INIT_LIST_HEAD(&dev->dev_registrants_list); + scst_init_threads(&dev->dev_cmd_threads); *out_dev = dev; @@ -2802,14 +2917,15 @@ void scst_tgt_dev_stop_threads(struct scst_tgt_dev *tgt_dev) * scst_mutex supposed to be held, there must not be parallel activity in this * session. */ -static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, - struct scst_acg_dev *acg_dev) +static int scst_alloc_add_tgt_dev(struct scst_session *sess, + struct scst_acg_dev *acg_dev, struct scst_tgt_dev **out_tgt_dev) { + int res = 0; int ini_sg, ini_unchecked_isa_dma, ini_use_clustering; struct scst_tgt_dev *tgt_dev; struct scst_device *dev = acg_dev->dev; struct list_head *sess_tgt_dev_list_head; - int rc, i, sl; + int i, sl; uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN]; TRACE_ENTRY(); @@ -2820,8 +2936,9 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, tgt_dev = kmem_cache_zalloc(scst_tgtd_cachep, GFP_KERNEL); #endif if (tgt_dev == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", - "Allocation of scst_tgt_dev failed"); + TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of scst_tgt_dev " + "failed"); + res = -ENOMEM; goto out; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17) @@ -2878,18 +2995,6 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, if (sess->tgt->tgtt->rdy_to_xfer_atomic) __set_bit(SCST_TGT_DEV_AFTER_INIT_WR_ATOMIC, &tgt_dev->tgt_dev_flags); - if (dev->handler->exec_atomic) - __set_bit(SCST_TGT_DEV_AFTER_INIT_OTH_ATOMIC, - &tgt_dev->tgt_dev_flags); - } - if (dev->handler->exec_atomic) { - if (sess->tgt->tgtt->rdy_to_xfer_atomic) - __set_bit(SCST_TGT_DEV_AFTER_RESTART_WR_ATOMIC, - &tgt_dev->tgt_dev_flags); - __set_bit(SCST_TGT_DEV_AFTER_RESTART_OTH_ATOMIC, - &tgt_dev->tgt_dev_flags); - __set_bit(SCST_TGT_DEV_AFTER_RX_DATA_ATOMIC, - &tgt_dev->tgt_dev_flags); } if (dev->handler->dev_done_atomic && sess->tgt->tgtt->xmit_response_atomic) { @@ -2901,17 +3006,34 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, dev->d_sense, SCST_LOAD_SENSE(scst_sense_reset_UA)); scst_alloc_set_UA(tgt_dev, sense_buffer, sl, 0); - rc = scst_tgt_dev_setup_threads(tgt_dev); - if (rc != 0) - goto out_free; + if (sess->tgt->tgtt->get_initiator_port_transport_id == NULL) { + if (!list_empty(&dev->dev_registrants_list)) { + PRINT_WARNING("Initiators from target %s can't connect " + "to device %s, because the device has PR " + "registrants and the target doesn't support " + "Persistent Reservations", sess->tgt->tgtt->name, + dev->virt_name); + res = -EPERM; + goto out_free; + } + dev->not_pr_supporting_tgt_devs_num++; + } + + res = scst_pr_init_tgt_dev(tgt_dev); + if (res != 0) + goto out_dec_free; + + res = scst_tgt_dev_setup_threads(tgt_dev); + if (res != 0) + goto out_pr_clear; if (dev->handler && dev->handler->attach_tgt) { TRACE_DBG("Calling dev handler's attach_tgt(%p)", tgt_dev); - rc = dev->handler->attach_tgt(tgt_dev); + res = dev->handler->attach_tgt(tgt_dev); TRACE_DBG("%s", "Dev handler's attach_tgt() returned"); - if (rc != 0) { + if (res != 0) { PRINT_ERROR("Device handler's %s attach_tgt() " - "failed: %d", dev->handler->name, rc); + "failed: %d", dev->handler->name, res); goto out_stop_threads; } } @@ -2927,18 +3049,25 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, list_add_tail(&tgt_dev->sess_tgt_dev_list_entry, sess_tgt_dev_list_head); + *out_tgt_dev = tgt_dev; + out: - TRACE_EXIT(); - return tgt_dev; + TRACE_EXIT_RES(res); + return res; out_stop_threads: scst_tgt_dev_stop_threads(tgt_dev); +out_pr_clear: + scst_pr_clear_tgt_dev(tgt_dev); + +out_dec_free: + if (tgt_dev->sess->tgt->tgtt->get_initiator_port_transport_id == NULL) + dev->not_pr_supporting_tgt_devs_num--; + out_free: scst_free_all_UA(tgt_dev); - kmem_cache_free(scst_tgtd_cachep, tgt_dev); - tgt_dev = NULL; goto out; } @@ -2983,7 +3112,11 @@ static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev) list_del(&tgt_dev->sess_tgt_dev_list_entry); + if (tgt_dev->sess->tgt->tgtt->get_initiator_port_transport_id == NULL) + dev->not_pr_supporting_tgt_devs_num--; + scst_clear_reservation(tgt_dev); + scst_pr_clear_tgt_dev(tgt_dev); scst_free_all_UA(tgt_dev); if (dev->handler && dev->handler->detach_tgt) { @@ -3014,11 +3147,11 @@ int scst_sess_alloc_tgt_devs(struct scst_session *sess) list_for_each_entry(acg_dev, &sess->acg->acg_dev_list, acg_dev_list_entry) { - tgt_dev = scst_alloc_add_tgt_dev(sess, acg_dev); - if (tgt_dev == NULL) { - res = -ENOMEM; + res = scst_alloc_add_tgt_dev(sess, acg_dev, &tgt_dev); + if (res == -EPERM) + continue; + else if (res != 0) goto out_free; - } } out: @@ -3083,11 +3216,12 @@ int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev, list_add_tail(&acg_dev->dev_acg_dev_list_entry, &dev->dev_acg_dev_list); list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) { - tgt_dev = scst_alloc_add_tgt_dev(sess, acg_dev); - if (tgt_dev == NULL) { - res = -ENOMEM; + res = scst_alloc_add_tgt_dev(sess, acg_dev, &tgt_dev); + if (res == -EPERM) + continue; + else if (res != 0) goto out_free; - } + list_add_tail(&tgt_dev->extra_tgt_dev_list_entry, &tmp_tgt_dev_list); } @@ -3646,6 +3780,8 @@ void scst_free_session(struct scst_session *sess) mutex_unlock(&scst_mutex); + kfree(sess->transport_id); + scst_sess_sysfs_put(sess); /* must not be called under scst_mutex */ TRACE_EXIT(); @@ -3984,7 +4120,7 @@ int scst_alloc_request(struct scst_cmd *cmd) { int res = 0; struct scsi_request *req; - int gm = scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL; + int gm = GFP_KERNEL; TRACE_ENTRY(); @@ -4315,7 +4451,7 @@ int scst_scsi_exec_async(struct scst_cmd *cmd, struct request *rq; struct scsi_io_context *sioc; int write = (cmd->data_direction & SCST_DATA_WRITE) ? WRITE : READ; - gfp_t gfp = scst_cmd_atomic(cmd) ? GFP_ATOMIC : GFP_KERNEL; + gfp_t gfp = GFP_KERNEL; int cmd_len = cmd->cdb_len; if (cmd->ext_cdb_len == 0) { @@ -4477,6 +4613,95 @@ out: } EXPORT_SYMBOL(scst_copy_sg); +int scst_get_full_buf(struct scst_cmd *cmd, uint8_t **buf) +{ + int res = 0; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(cmd->sg_buff_vmallocated); + + if (scst_get_buf_count(cmd) > 1) { + int len; + uint8_t *tmp_buf; + int full_size; + + full_size = 0; + len = scst_get_buf_first(cmd, &tmp_buf); + while (len > 0) { + full_size += len; + scst_put_buf(cmd, tmp_buf); + len = scst_get_buf_next(cmd, &tmp_buf); + } + + *buf = vmalloc(full_size); + if (*buf == NULL) { + TRACE(TRACE_OUT_OF_MEM, "vmalloc() failed for opcode " + "%x", cmd->cdb[0]); + res = -ENOMEM; + goto out; + } + cmd->sg_buff_vmallocated = 1; + + if (scst_cmd_get_data_direction(cmd) == SCST_DATA_WRITE) { + uint8_t *buf_ptr; + + buf_ptr = *buf; + + len = scst_get_buf_first(cmd, &tmp_buf); + while (len > 0) { + memcpy(buf_ptr, tmp_buf, len); + buf_ptr += len; + + scst_put_buf(cmd, tmp_buf); + len = scst_get_buf_next(cmd, &tmp_buf); + } + } + res = full_size; + } else + res = scst_get_buf_first(cmd, buf); + +out: + TRACE_EXIT_RES(res); + return res; +} + +void scst_put_full_buf(struct scst_cmd *cmd, uint8_t *buf) +{ + TRACE_ENTRY(); + + if (buf == NULL) + goto out; + + if (cmd->sg_buff_vmallocated) { + if (scst_cmd_get_data_direction(cmd) == SCST_DATA_READ) { + int len; + uint8_t *tmp_buf, *buf_p; + + buf_p = buf; + + len = scst_get_buf_first(cmd, &tmp_buf); + while (len > 0) { + memcpy(tmp_buf, buf_p, len); + buf_p += len; + + scst_put_buf(cmd, tmp_buf); + len = scst_get_buf_next(cmd, &tmp_buf); + } + + } + + cmd->sg_buff_vmallocated = 0; + + vfree(buf); + } else + scst_put_buf(cmd, buf); + +out: + TRACE_EXIT(); + return; +} + static const int SCST_CDB_LENGTH[8] = { 6, 10, 10, -1, 16, 12, -1, -1 }; #define SCST_CDB_GROUP(opcode) ((opcode >> 5) & 0x7) @@ -4584,14 +4809,16 @@ static int get_trans_len_prevent_allow_medium_removal(struct scst_cmd *cmd, uint8_t off) { if ((cmd->cdb[4] & 3) == 0) - cmd->op_flags |= SCST_REG_RESERVE_ALLOWED; + cmd->op_flags |= SCST_REG_RESERVE_ALLOWED | + SCST_WRITE_EXCL_ALLOWED | SCST_EXCL_ACCESS_ALLOWED; return 0; } static int get_trans_len_start_stop(struct scst_cmd *cmd, uint8_t off) { if ((cmd->cdb[4] & 0xF1) == 0x1) - cmd->op_flags |= SCST_REG_RESERVE_ALLOWED; + cmd->op_flags |= SCST_REG_RESERVE_ALLOWED | + SCST_WRITE_EXCL_ALLOWED | SCST_EXCL_ACCESS_ALLOWED; return 0; } @@ -4605,8 +4832,8 @@ static int get_trans_len_3_read_elem_stat(struct scst_cmd *cmd, uint8_t off) cmd->bufflen |= ((u32)p[2]); if ((cmd->cdb[6] & 0x2) == 0x2) - cmd->op_flags |= SCST_REG_RESERVE_ALLOWED; - + cmd->op_flags |= SCST_REG_RESERVE_ALLOWED | + SCST_WRITE_EXCL_ALLOWED | SCST_EXCL_ACCESS_ALLOWED; return 0; } @@ -6414,6 +6641,88 @@ int scst_get_max_lun_commands(struct scst_session *sess, uint64_t lun) } EXPORT_SYMBOL(scst_get_max_lun_commands); +/** + * scst_reassign_persistent_sess_states() - reassigns persistent states + * + * Reassigns persistent states from old_sess to new_sess. + */ +void scst_reassign_persistent_sess_states(struct scst_session *new_sess, + struct scst_session *old_sess) +{ + struct scst_device *dev; + + TRACE_ENTRY(); + + TRACE_DBG("Reassigning persistent states from old_sess %p to " + "new_sess %p", old_sess, new_sess); + + if ((new_sess == NULL) || (old_sess == NULL)) { + TRACE_DBG("%s", "new_sess or old_sess is NULL"); + goto out; + } + + if ((new_sess->transport_id == NULL) || + (old_sess->transport_id == NULL)) { + TRACE_DBG("%s", "new_sess or old_sess doesn't support PRs"); + goto out; + } + + mutex_lock(&scst_mutex); + + list_for_each_entry(dev, &scst_dev_list, dev_list_entry) { + struct scst_tgt_dev *tgt_dev; + struct scst_tgt_dev *new_tgt_dev = NULL, *old_tgt_dev = NULL; + + TRACE_DBG("Processing dev %s", dev->virt_name); + + list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, + dev_tgt_dev_list_entry) { + if (tgt_dev->sess == new_sess) { + new_tgt_dev = tgt_dev; + if (old_tgt_dev != NULL) + break; + } + if (tgt_dev->sess == old_sess) { + old_tgt_dev = tgt_dev; + if (new_tgt_dev != NULL) + break; + } + } + + if ((new_tgt_dev == NULL) || (old_tgt_dev == NULL)) { + TRACE_DBG("new_tgt_dev %p or old_sess %p is NULL, " + "skipping (dev %s)", new_tgt_dev, old_tgt_dev, + dev->virt_name); + continue; + } + + scst_pr_write_lock(dev); + + if (old_tgt_dev->registrant != NULL) { + TRACE_PR("Reassigning reg %p from tgt_dev %p to %p", + old_tgt_dev->registrant, old_tgt_dev, + new_tgt_dev); + + if (new_tgt_dev->registrant != NULL) + new_tgt_dev->registrant->tgt_dev = NULL; + + new_tgt_dev->registrant = old_tgt_dev->registrant; + new_tgt_dev->registrant->tgt_dev = new_tgt_dev; + + old_tgt_dev->registrant = NULL; + } + + scst_pr_write_unlock(dev); + } + + mutex_unlock(&scst_mutex); + +out: + TRACE_EXIT(); + return; +} +EXPORT_SYMBOL(scst_reassign_persistent_sess_states); + /** * scst_get_next_lexem() - parse and return next lexem in the string * diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index ad23d50b9..c6f2ae90e 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -33,6 +33,7 @@ #include "scst.h" #include "scst_priv.h" #include "scst_mem.h" +#include "scst_pres.h" #if defined(CONFIG_HIGHMEM4G) || defined(CONFIG_HIGHMEM64G) #warning "HIGHMEM kernel configurations are fully supported, but not\ @@ -214,6 +215,10 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, goto out_err; } + if (vtt->get_initiator_port_transport_id == NULL) + PRINT_WARNING("Target driver %s doesn't support Persistent " + "Reservations", vtt->name); + if (vtt->threads_num < 0) { PRINT_ERROR("Wrong threads_num value %d for " "target \"%s\"", vtt->threads_num, @@ -223,13 +228,12 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, } #ifndef CONFIG_SCST_PROC - if (!vtt->enable_target || !vtt->is_target_enabled) { + if (!vtt->enable_target || !vtt->is_target_enabled) PRINT_WARNING("Target driver %s doesn't have enable_target() " "and/or is_target_enabled() method(s). This is unsafe " "and can lead that initiators connected on the " "initialization time can see an unexpected set of " "devices or no devices at all!", vtt->name); - } if (((vtt->add_target != NULL) && (vtt->del_target == NULL)) || ((vtt->add_target == NULL) && (vtt->del_target != NULL))) { @@ -333,7 +337,7 @@ void scst_unregister_target_template(struct scst_tgt_template *vtt) restart: list_for_each_entry(tgt, &vtt->tgt_list, tgt_list_entry) { mutex_unlock(&scst_mutex); - scst_unregister(tgt); + scst_unregister_target(tgt); mutex_lock(&scst_mutex); goto restart; } @@ -365,7 +369,7 @@ EXPORT_SYMBOL(scst_unregister_target_template); * Registers a target for template vtt and returns new target structure on * success or NULL otherwise. */ -struct scst_tgt *scst_register(struct scst_tgt_template *vtt, +struct scst_tgt *scst_register_target(struct scst_tgt_template *vtt, const char *target_name) { struct scst_tgt *tgt; @@ -391,6 +395,7 @@ struct scst_tgt *scst_register(struct scst_tgt_template *vtt, int len = strlen(target_name) + strlen(SCST_DEFAULT_ACG_NAME) + 1 + 1; + tgt->default_group_name = kmalloc(len, GFP_KERNEL); if (tgt->default_group_name == NULL) { TRACE(TRACE_OUT_OF_MEM, "Allocation of default " @@ -505,7 +510,7 @@ out_err: vtt->name, rc); goto out; } -EXPORT_SYMBOL(scst_register); +EXPORT_SYMBOL(scst_register_target); static inline int test_sess_list(struct scst_tgt *tgt) { @@ -519,7 +524,7 @@ static inline int test_sess_list(struct scst_tgt *tgt) /** * scst_unregister_target() - unregister target */ -void scst_unregister(struct scst_tgt *tgt) +void scst_unregister_target(struct scst_tgt *tgt) { struct scst_session *sess; struct scst_tgt_template *vtt = tgt->tgtt; @@ -585,7 +590,7 @@ again: TRACE_EXIT(); return; } -EXPORT_SYMBOL(scst_unregister); +EXPORT_SYMBOL(scst_unregister_target); static int scst_susp_wait(bool interruptible) { @@ -956,14 +961,6 @@ static int scst_dev_handler_check(struct scst_dev_type *dev_handler) } #endif - if (dev_handler->exec == NULL) { -#ifdef CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ - dev_handler->exec_atomic = 1; -#else - dev_handler->exec_atomic = 0; -#endif - } - if (dev_handler->alloc_data_buf == NULL) dev_handler->alloc_data_buf_atomic = 1; @@ -1009,14 +1006,6 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler, if (res != 0) goto out; - list_for_each_entry(dev, &scst_dev_list, dev_list_entry) { - if (strcmp(dev->virt_name, dev_name) == 0) { - PRINT_ERROR("Device %s already exists", dev_name); - res = -EEXIST; - goto out; - } - } - res = scst_suspend_activity(true); if (res != 0) goto out; @@ -1026,6 +1015,14 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler, goto out_resume; } + list_for_each_entry(dev, &scst_dev_list, dev_list_entry) { + if (strcmp(dev->virt_name, dev_name) == 0) { + PRINT_ERROR("Device %s already exists", dev_name); + res = -EEXIST; + goto out_up; + } + } + res = scst_alloc_device(GFP_KERNEL, &dev); if (res != 0) goto out_up; @@ -1051,12 +1048,18 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler, goto out_free_del; } - rc = scst_assign_dev_handler(dev, dev_handler); + rc = scst_pr_init_dev(dev); if (rc != 0) { res = rc; goto out_free_del; } + rc = scst_assign_dev_handler(dev, dev_handler); + if (rc != 0) { + res = rc; + goto out_pr_clear_dev; + } + out_up: mutex_unlock(&scst_mutex); @@ -1073,6 +1076,9 @@ out: TRACE_EXIT_RES(res); return res; +out_pr_clear_dev: + scst_pr_clear_dev(dev); + out_free_del: list_del(&dev->dev_list_entry); @@ -1117,6 +1123,8 @@ void scst_unregister_virtual_device(int id) scst_acg_remove_dev(acg_dev->acg, dev, true); } + scst_pr_clear_dev(dev); + scst_assign_dev_handler(dev, &scst_null_devtype); PRINT_INFO("Detached from virtual device %s (id %d)", @@ -1773,6 +1781,7 @@ static void scst_stop_all_threads(void) return; } +/* It does NOT stop ran threads on error! */ static int scst_start_all_threads(int num) { int res; @@ -1969,14 +1978,14 @@ static void __init scst_print_config(void) (j == i) ? "" : ", "); #endif -#ifdef CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ i += snprintf(&buf[i], sizeof(buf) - i, - "%sALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ", + "%sTEST_IO_IN_SIRQ", (j == i) ? "" : ", "); #endif #ifdef CONFIG_SCST_STRICT_SECURITY - i += snprintf(&buf[i], sizeof(buf) - i, "%sSCST_STRICT_SECURITY", + i += snprintf(&buf[i], sizeof(buf) - i, "%sSTRICT_SECURITY", (j == i) ? "" : ", "); #endif @@ -2190,6 +2199,10 @@ static int __init init_scst(void) res = scst_proc_init_module(); if (res != 0) goto out_thread_free; +#else + res = scst_pr_check_pr_path(); + if (res != 0) + goto out_thread_free; #endif PRINT_INFO("SCST version %s loaded successfully (max mem for " diff --git a/scst/src/scst_pres.c b/scst/src/scst_pres.c new file mode 100644 index 000000000..3a640dfca --- /dev/null +++ b/scst/src/scst_pres.c @@ -0,0 +1,2581 @@ +/* + * scst_pres.c + * + * Copyright (C) 2009 - 2010 Alexey Obitotskiy + * Copyright (C) 2009 - 2010 Open-E, Inc. + * Copyright (C) 2009 - 2010 Vladislav Bolkhovitin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) +#include +#endif + +#include "scst.h" +#include "scst_const.h" +#include "scst_priv.h" +#include "scst_pres.h" + +#define SCST_PR_ROOT_ENTRY "pr" +#define SCST_PR_FILE_SIGN 0xBBEEEEAAEEBBDD77LLU +#define SCST_PR_FILE_VERSION 1LLU + +#define FILE_BUFFER_SIZE 512 + +#ifndef isblank +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif + +/* + * Experimental hack to support CONFIG_SCST_ISCSI_SKIP_ISID. ISCSI-SCST sets + * this variable if CONFIG_SCST_ISCSI_SKIP_ISID defined, then tid_equal() + * uses it to perform for iSCSI TransportIDs only initiator names comparison + * ignoring the ISID part. + */ +bool iscsi_tid_name_only = false; +EXPORT_SYMBOL(iscsi_tid_name_only); + +static inline int tid_size(const uint8_t *tid) +{ + sBUG_ON(tid == NULL); + + if ((tid[0] & 0x0f) == SCSI_TRANSPORTID_PROTOCOLID_ISCSI) + return be16_to_cpu(get_unaligned((__be16 *)&tid[2])) + 4; + else + return TID_COMMON_SIZE; +} + +/* Secures tid by setting 0 in the last byte of NULL-terminated tid's */ +static inline void tid_secure(uint8_t *tid) +{ + if ((tid[0] & 0x0f) == SCSI_TRANSPORTID_PROTOCOLID_ISCSI) { + int size = tid_size(tid); + tid[size - 1] = '\0'; + } + + return; +} + +/* Returns false if tid's are not equal, true otherwise */ +static bool tid_equal(const uint8_t *tid_a, const uint8_t *tid_b) +{ + int len; + + if (tid_a == NULL || tid_b == NULL) + return false; + + if ((tid_a[0] & 0x0f) != (tid_b[0] & 0x0f)) { + TRACE_DBG("%s", "Different protocol IDs"); + return false; + } + + if ((tid_a[0] & 0x0f) == SCSI_TRANSPORTID_PROTOCOLID_ISCSI) { + const uint8_t tid_a_fmt = tid_a[0] & 0xc0; + const uint8_t tid_b_fmt = tid_b[0] & 0xc0; + int tid_a_len, tid_a_max = tid_size(tid_a) - 4; + int tid_b_len, tid_b_max = tid_size(tid_b) - 4; + int i; + + tid_a += 4; + tid_b += 4; + + if (tid_a_fmt == 0x00) + tid_a_len = strnlen(tid_a, tid_a_max); + else if (tid_a_fmt == 0x40) { + if ((tid_a_fmt != tid_b_fmt) || iscsi_tid_name_only) { + uint8_t *p = strnchr(tid_a, tid_a_max, ','); + if (p == NULL) + goto out_error; + tid_a_len = p - tid_a; + + sBUG_ON(tid_a_len > tid_a_max); + sBUG_ON(tid_a_len < 0); + } else + tid_a_len = strnlen(tid_a, tid_a_max); + } else + goto out_error; + + if (tid_b_fmt == 0x00) + tid_b_len = strnlen(tid_b, tid_b_max); + else if (tid_b_fmt == 0x40) { + if ((tid_a_fmt != tid_b_fmt) || iscsi_tid_name_only) { + uint8_t *p = strnchr(tid_b, tid_b_max, ','); + if (p == NULL) + goto out_error; + tid_b_len = p - tid_b; + + sBUG_ON(tid_b_len > tid_b_max); + sBUG_ON(tid_b_len < 0); + } else + tid_b_len = strnlen(tid_b, tid_b_max); + } else + goto out_error; + + if (tid_a_len != tid_b_len) + return false; + + len = tid_a_len; + + /* ISCSI names are case insensitive */ + for (i = 0; i < len; i++) + if (tolower(tid_a[i]) != tolower(tid_b[i])) + return false; + return true; + } else + len = TID_COMMON_SIZE; + + return (memcmp(tid_a, tid_b, len) == 0); + +out_error: + PRINT_ERROR("%s", "Invalid initiator port transport id"); + return false; +} + +/* Must be called under dev_pr_mutex */ +static inline void scst_pr_set_holder(struct scst_device *dev, + struct scst_dev_registrant *holder, uint8_t scope, uint8_t type) +{ + dev->pr_is_set = 1; + dev->pr_scope = scope; + dev->pr_type = type; + if (dev->pr_type != TYPE_EXCLUSIVE_ACCESS_ALL_REG && + dev->pr_type != TYPE_WRITE_EXCLUSIVE_ALL_REG) + dev->pr_holder = holder; +} + +/* Must be called under dev_pr_mutex */ +static bool scst_pr_is_holder(struct scst_device *dev, + struct scst_dev_registrant *reg) +{ + bool res = false; + + TRACE_ENTRY(); + + if (!dev->pr_is_set) + goto out; + + if (dev->pr_type == TYPE_EXCLUSIVE_ACCESS_ALL_REG || + dev->pr_type == TYPE_WRITE_EXCLUSIVE_ALL_REG) { + res = (reg != NULL); + } else + res = (dev->pr_holder == reg); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* Must be called under dev_pr_mutex */ +static void scst_pr_dump_prs(struct scst_device *dev) +{ + TRACE_PR("Persistent reservations for device %s:", dev->virt_name); + + if (list_empty(&dev->dev_registrants_list)) + TRACE_PR("%s", " No registrants"); + else { + struct scst_dev_registrant *reg; + int i = 0; + list_for_each_entry(reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + TRACE_PR(" [%d] registrant %s/%d, key '%016llx' " + "(reg %p, tgt_dev %p)", i++, + debug_transport_id_to_initiator_name( + reg->transport_id), + reg->rel_tgt_id, reg->key, reg, reg->tgt_dev); + } + } + + if (dev->pr_is_set) { + struct scst_dev_registrant *holder = dev->pr_holder; + if (holder != NULL) + TRACE_PR("Reservation holder is %s/%d (key '%016llx', " + "scope %x, type %x, reg %p, tgt_dev %p)", + debug_transport_id_to_initiator_name( + holder->transport_id), + holder->rel_tgt_id, holder->key, dev->pr_scope, + dev->pr_type, holder, holder->tgt_dev); + else + TRACE_PR("All registrants are reservation holders " + "(scope %x, type %x)", dev->pr_scope, + dev->pr_type); + } else + TRACE_PR("%s", "Not reserved"); + + return; +} + +/* dev_pr_mutex must be locked */ +static void scst_pr_find_registrants_list_all(struct scst_device *dev, + struct scst_dev_registrant *exclude_reg, struct list_head *list) +{ + struct scst_dev_registrant *reg; + + TRACE_ENTRY(); + + TRACE_PR("Finding all registered records for device '%s' " + "with exclude reg key '%016llx'", + dev->virt_name, exclude_reg->key); + + list_for_each_entry(reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + if (reg == exclude_reg) + continue; + TRACE_PR("Adding registrant %s/%d (%p) to find list (key '%016llx')", + debug_transport_id_to_initiator_name(reg->transport_id), + reg->rel_tgt_id, reg, reg->key); + list_add_tail(®->aux_list_entry, list); + } + + TRACE_EXIT(); + return; +} + +/* dev_pr_mutex must be locked */ +static void scst_pr_find_registrants_list_key(struct scst_device *dev, + uint64_t key, struct list_head *list) +{ + struct scst_dev_registrant *reg; + + TRACE_ENTRY(); + + TRACE_PR("Finding registrants for device '%s' with key '%016llx'", + dev->virt_name, key); + + list_for_each_entry(reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + if (reg->key == key) { + TRACE_PR("Adding registrant %s/%d (%p) to the find " + "list (key '%016llx')", + debug_transport_id_to_initiator_name( + reg->transport_id), + reg->rel_tgt_id, reg->tgt_dev, key); + list_add_tail(®->aux_list_entry, list); + } + } + + TRACE_EXIT(); + return; +} + +/* dev_pr_mutex must be locked */ +static struct scst_dev_registrant *scst_pr_find_reg_not_used_first( + struct scst_device *dev, const uint8_t *transport_id, + const uint16_t rel_tgt_id, bool not_used_only) +{ + struct scst_dev_registrant *reg, *res = NULL, *res_used = NULL; + + TRACE_ENTRY(); + + list_for_each_entry(reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + if ((reg->rel_tgt_id == rel_tgt_id) && + tid_equal(reg->transport_id, transport_id)) { + if (reg->tgt_dev == NULL) { + res = reg; + break; + } else if (!not_used_only && (res_used == NULL)) + res_used = reg; + } + } + + if (res == NULL) + res = res_used; + + TRACE_EXIT_HRES(res); + return res; +} + +/* Must be called under dev_pr_mutex */ +static void scst_pr_clear_reservation(struct scst_device *dev) +{ + TRACE_ENTRY(); + + WARN_ON(!dev->pr_is_set); + + dev->pr_is_set = 0; + dev->pr_scope = SCOPE_LU; + dev->pr_type = TYPE_UNSPECIFIED; + + dev->pr_holder = NULL; + + TRACE_EXIT(); + return; +} + +/* Must be called under dev_pr_mutex */ +static void scst_pr_clear_holder(struct scst_device *dev) +{ + TRACE_ENTRY(); + + WARN_ON(!dev->pr_is_set); + + if (dev->pr_type == TYPE_WRITE_EXCLUSIVE_ALL_REG || + dev->pr_type == TYPE_EXCLUSIVE_ACCESS_ALL_REG) { + if (list_empty(&dev->dev_registrants_list)) + scst_pr_clear_reservation(dev); + } else + scst_pr_clear_reservation(dev); + + dev->pr_holder = NULL; + + TRACE_EXIT(); + return; +} + +/* Must be called under dev_pr_mutex */ +static struct scst_dev_registrant *scst_pr_add_registrant( + struct scst_device *dev, const uint8_t *transport_id, + const uint16_t rel_tgt_id, uint64_t key, + struct scst_tgt_dev *tgt_dev, gfp_t gfp_flags) +{ + struct scst_dev_registrant *reg = NULL; + + TRACE_ENTRY(); + + sBUG_ON(dev == NULL); + sBUG_ON(transport_id == NULL); + + TRACE_PR("Registering %s/%d (dev %s, tgt_dev %p)", + debug_transport_id_to_initiator_name(transport_id), + rel_tgt_id, dev->virt_name, tgt_dev); + + reg = kzalloc(sizeof(*reg), gfp_flags); + if (reg == NULL) { + PRINT_ERROR("%s", "Unable to allocate registration record"); + goto out; + } + + reg->transport_id = kmalloc(tid_size(transport_id), gfp_flags); + if (reg->transport_id == NULL) { + PRINT_ERROR("%s", "Unable to allocate initiator port " + "transport id"); + goto out_free; + } + memcpy(reg->transport_id, transport_id, tid_size(transport_id)); + + reg->rel_tgt_id = rel_tgt_id; + reg->key = key; + reg->tgt_dev = tgt_dev; + + if (tgt_dev == NULL) { + struct scst_tgt_dev *t; + /* + * We can't use scst_mutex here, because of circular + * locking dependency with dev_pr_mutex. + */ + spin_lock_bh(&dev->dev_lock); + list_for_each_entry(t, &dev->dev_tgt_dev_list, + dev_tgt_dev_list_entry) { + if (tid_equal(t->sess->transport_id, transport_id) && + (t->sess->tgt->rel_tgt_id == rel_tgt_id) && + (t->registrant == NULL)) { + /* + * We must assign here, because t can die + * immediately after we release dev_lock. + */ + TRACE_PR("Found tgt_dev %p", t); + reg->tgt_dev = t; + t->registrant = reg; + break; + } + } + spin_unlock_bh(&dev->dev_lock); + } else + tgt_dev->registrant = reg; + + list_add_tail(®->dev_registrants_list_entry, + &dev->dev_registrants_list); + + TRACE_PR("Reg %p registered (dev %s, tgt_dev %p)", reg, + dev->virt_name, reg->tgt_dev); + +out: + TRACE_EXIT_HRES((unsigned long)reg); + return reg; + +out_free: + kfree(reg); + reg = NULL; + goto out; +} + +/* Must be called under dev_pr_mutex */ +static void scst_pr_remove_registrant(struct scst_device *dev, + struct scst_dev_registrant *reg) +{ + TRACE_ENTRY(); + + TRACE_PR("Removing registrant %s/%d (reg %p, tgt_dev %p, key '%016llx', " + "dev %s)", debug_transport_id_to_initiator_name(reg->transport_id), + reg->rel_tgt_id, reg, reg->tgt_dev, reg->key, dev->virt_name); + + list_del(®->dev_registrants_list_entry); + + if (scst_pr_is_holder(dev, reg)) + scst_pr_clear_holder(dev); + + if (reg->tgt_dev) + reg->tgt_dev->registrant = NULL; + + kfree(reg->transport_id); + kfree(reg); + + TRACE_EXIT(); + return; +} + +/* Must be called under dev_pr_mutex */ +static void scst_pr_send_ua_reg(struct scst_device *dev, + struct scst_dev_registrant *reg, + int key, int asc, int ascq) +{ + static uint8_t ua[SCST_STANDARD_SENSE_LEN]; + + TRACE_ENTRY(); + + scst_set_sense(ua, sizeof(ua), dev->d_sense, key, asc, ascq); + + TRACE_PR("Queuing UA [%x %x %x]: registrant %s/%d (%p), tgt_dev %p, " + "key '%016llx'", ua[2], ua[12], ua[13], + debug_transport_id_to_initiator_name(reg->transport_id), + reg->rel_tgt_id, reg, reg->tgt_dev, reg->key); + + if (reg->tgt_dev) + scst_check_set_UA(reg->tgt_dev, ua, sizeof(ua), 0); + + TRACE_EXIT(); + return; +} + +/* Must be called under dev_pr_mutex */ +static void scst_pr_send_ua_all(struct scst_device *dev, + struct scst_dev_registrant *exclude_reg, + int key, int asc, int ascq) +{ + struct scst_dev_registrant *reg; + + TRACE_ENTRY(); + + list_for_each_entry(reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + if (reg != exclude_reg) + scst_pr_send_ua_reg(dev, reg, key, asc, ascq); + } + + TRACE_EXIT(); + return; +} + +/* Must be called under dev_pr_mutex */ +static void scst_pr_abort_reg(struct scst_device *dev, + struct scst_cmd *pr_cmd, struct scst_dev_registrant *reg) +{ + struct scst_session *sess; + uint64_t packed_lun; + int rc; + + TRACE_ENTRY(); + + if (reg->tgt_dev == NULL) { + TRACE_PR("Registrant %s/%d (%p, key '0x%016llx') has no session", + debug_transport_id_to_initiator_name(reg->transport_id), + reg->rel_tgt_id, reg, reg->key); + goto out; + } + + sess = reg->tgt_dev->sess; + + TRACE_PR("Aborting %d commands for %s/%d (reg %p, key '0x%016llx', " + "tgt_dev %p, sess %p)", + atomic_read(®->tgt_dev->tgt_dev_cmd_count), + debug_transport_id_to_initiator_name(reg->transport_id), + reg->rel_tgt_id, reg, reg->key, reg->tgt_dev, sess); + + packed_lun = scst_pack_lun(reg->tgt_dev->lun, sess->acg->addr_method); + + rc = scst_rx_mgmt_fn_lun(sess, SCST_PR_ABORT_ALL, + (uint8_t *)&packed_lun, sizeof(packed_lun), SCST_NON_ATOMIC, + pr_cmd); + if (rc != 0) { + /* + * There's nothing more we can do here... Hopefully, it would + * never happen. + */ + PRINT_ERROR("SCST_PR_ABORT_ALL failed %d (sess %p)", + rc, sess); + } + +out: + TRACE_EXIT(); + return; +} + +#ifndef CONFIG_SCST_PROC + +/* Abstract vfs_unlink & path_put for different kernel versions */ +static inline void scst_pr_vfs_unlink_and_put(struct nameidata *nd) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + vfs_unlink(nd->dentry->d_parent->d_inode, nd->dentry); + dput(nd->dentry); + mntput(nd->mnt); +#else + vfs_unlink(nd->path.dentry->d_parent->d_inode, + nd->path.dentry); + path_put(&nd->path); +#endif +} + +static inline void scst_pr_path_put(struct nameidata *nd) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) + dput(nd->dentry); + mntput(nd->mnt); +#else + path_put(&nd->path); +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) +static int scst_pr_vfs_fsync(struct file *file, loff_t loff, loff_t len) +{ + int res; + + res = sync_page_range(file->f_dentry->d_inode, file->f_mapping, + loff, len); + return res; +} +#endif + +/* Called under scst_mutex */ +static int scst_pr_do_load_device_file(struct scst_device *dev, + const char *file_name) +{ + int res = 0, rc; + struct file *file = NULL; + struct inode *inode; + char *buf = NULL; + loff_t file_size, pos, data_size; + uint64_t sign, version; + mm_segment_t old_fs; + uint8_t pr_is_set, aptpl; + uint64_t key; + uint16_t rel_tgt_id; + + TRACE_ENTRY(); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + TRACE_PR("Loading persistent file '%s'", file_name); + + file = filp_open(file_name, O_RDONLY, 0); + if (IS_ERR(file)) { + res = PTR_ERR(file); + TRACE_PR("Unable to open file '%s' - error %d", file_name, res); + goto out; + } + + inode = file->f_dentry->d_inode; + + if (S_ISREG(inode->i_mode)) + /* Nothing to do */; + else if (S_ISBLK(inode->i_mode)) + inode = inode->i_bdev->bd_inode; + else { + PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode); + goto out_close; + } + + file_size = inode->i_size; + + /* Let's limit the file size by some reasonable number */ + if ((file_size == 0) || (file_size >= 15*1024*1024)) { + PRINT_ERROR("Invalid PR file size %d", (int)file_size); + res = -EINVAL; + goto out_close; + } + + buf = vmalloc(file_size); + if (buf == NULL) { + res = -ENOMEM; + PRINT_ERROR("%s", "Unable to allocate buffer"); + goto out_close; + } + + pos = 0; + rc = vfs_read(file, buf, file_size, &pos); + if (rc != file_size) { + PRINT_ERROR("Unable to read file '%s' - error %d", file_name, + rc); + res = rc; + goto out_close; + } + + data_size = 0; + data_size += sizeof(sign); + data_size += sizeof(version); + data_size += sizeof(aptpl); + data_size += sizeof(pr_is_set); + data_size += sizeof(dev->pr_type); + data_size += sizeof(dev->pr_scope); + + if (file_size < data_size) { + res = -EINVAL; + PRINT_ERROR("Invalid file '%s' - size too small", file_name); + goto out_close; + } + + pos = 0; + + sign = get_unaligned((uint64_t *)&buf[pos]); + if (sign != SCST_PR_FILE_SIGN) { + res = -EINVAL; + PRINT_ERROR("Invalid persistent file signature %016llx " + "(expected %016llx)", sign, SCST_PR_FILE_SIGN); + goto out_close; + } + pos += sizeof(sign); + + version = get_unaligned((uint64_t *)&buf[pos]); + if (version != SCST_PR_FILE_VERSION) { + res = -EINVAL; + PRINT_ERROR("Invalid persistent file version %016llx " + "(expected %016llx)", version, SCST_PR_FILE_VERSION); + goto out_close; + } + pos += sizeof(version); + + while (data_size < file_size) { + uint8_t *tid; + + data_size++; + tid = &buf[data_size]; + data_size += tid_size(tid); + data_size += sizeof(key); + data_size += sizeof(rel_tgt_id); + + if (data_size > file_size) { + res = -EINVAL; + PRINT_ERROR("Invalid file '%s' - size mismatch have " + "%lld expected %lld", file_name, file_size, + data_size); + goto out_close; + } + } + + aptpl = buf[pos]; + dev->pr_aptpl = aptpl ? 1 : 0; + pos += sizeof(aptpl); + + pr_is_set = buf[pos]; + dev->pr_is_set = pr_is_set ? 1 : 0; + pos += sizeof(pr_is_set); + + dev->pr_type = buf[pos]; + pos += sizeof(dev->pr_type); + + dev->pr_scope = buf[pos]; + pos += sizeof(dev->pr_scope); + + while (pos < file_size) { + uint8_t is_holder; + uint8_t *tid; + struct scst_dev_registrant *reg = NULL; + + is_holder = buf[pos++]; + + tid = &buf[pos]; + pos += tid_size(tid); + + key = get_unaligned((uint64_t *)&buf[pos]); + pos += sizeof(key); + + rel_tgt_id = get_unaligned((uint16_t *)&buf[pos]); + pos += sizeof(rel_tgt_id); + + /* + * Add a registrant without initiator name and without + * attaching to a tgt_dev. The attachment will be done in + * scst_pr_init_tgt_dev. + */ + reg = scst_pr_add_registrant(dev, tid, rel_tgt_id, key, NULL, + GFP_KERNEL); + if (reg == NULL) { + res = -ENOMEM; + goto out_close; + } + + if (is_holder) + dev->pr_holder = reg; + } + +out_close: + filp_close(file, NULL); + +out: + if (buf != NULL) + vfree(buf); + + set_fs(old_fs); + + TRACE_EXIT_RES(res); + return res; +} + +static int scst_pr_load_device_file(struct scst_device *dev) +{ + int res; + + TRACE_ENTRY(); + + if (dev->pr_file_name == NULL || dev->pr_file_name1 == NULL) { + PRINT_ERROR("Invalid file paths for '%s'", dev->virt_name); + res = -EINVAL; + goto out; + } + + res = scst_pr_do_load_device_file(dev, dev->pr_file_name); + if (res == 0) + goto out; + else if (res == -ENOMEM) + goto out; + + res = scst_pr_do_load_device_file(dev, dev->pr_file_name1); + + scst_pr_dump_prs(dev); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static int scst_pr_copy_file(const char *src, const char *dest) +{ + int res = 0; + struct inode *inode; + loff_t file_size, pos; + uint8_t *buf = NULL; + struct file *file_src = NULL, *file_dest = NULL; + mm_segment_t old_fs = get_fs(); + + TRACE_ENTRY(); + + if (src == NULL || dest == NULL) { + res = -EINVAL; + PRINT_ERROR("%s", "Invalid persistent files path - backup " + "skipped"); + goto out; + } + + TRACE_PR("Copying '%s' into '%s'", src, dest); + + set_fs(KERNEL_DS); + + file_src = filp_open(src, O_RDONLY, 0); + if (IS_ERR(file_src)) { + res = PTR_ERR(file_src); + TRACE_PR("Unable to open file '%s' - error %d", src, + res); + goto out_free; + } + + file_dest = filp_open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(file_dest)) { + res = PTR_ERR(file_dest); + TRACE_PR("Unable to open backup file '%s' - error %d", dest, + res); + goto out_close; + } + + inode = file_src->f_dentry->d_inode; + + if (S_ISREG(inode->i_mode)) + /* Nothing to do */; + else if (S_ISBLK(inode->i_mode)) + inode = inode->i_bdev->bd_inode; + else { + PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode); + res = -EINVAL; + set_fs(old_fs); + goto out_skip; + } + + file_size = inode->i_size; + + buf = vmalloc(file_size); + if (buf == NULL) { + res = -ENOMEM; + PRINT_ERROR("%s", "Unable to allocate temporary buffer"); + goto out_skip; + } + + pos = 0; + res = vfs_read(file_src, buf, file_size, &pos); + if (res != file_size) { + PRINT_ERROR("Unable to read file '%s' - error %d", src, res); + goto out_skip; + } + + pos = 0; + res = vfs_write(file_dest, buf, file_size, &pos); + if (res != file_size) { + PRINT_ERROR("Unable to write to '%s' - error %d", dest, res); + goto out_skip; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + res = scst_pr_vfs_fsync(file_dest, 0, file_size); +#else + res = vfs_fsync(file_dest, file_dest->f_path.dentry, 0); +#endif + if (res != 0) { + PRINT_ERROR("fsync() of the backup PR file failed: %d", res); + goto out_skip; + } + +out_skip: + filp_close(file_dest, NULL); + +out_close: + filp_close(file_src, NULL); + +out_free: + if (buf != NULL) + vfree(buf); + + set_fs(old_fs); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static void scst_pr_remove_device_files(struct scst_tgt_dev *tgt_dev) +{ + int res = 0; + struct scst_device *dev = tgt_dev->dev; + struct nameidata nd; + mm_segment_t old_fs = get_fs(); + + TRACE_ENTRY(); + + set_fs(KERNEL_DS); + + res = path_lookup(dev->pr_file_name, 0, &nd); + if (!res) + scst_pr_vfs_unlink_and_put(&nd); + else + TRACE_DBG("Unable to lookup file '%s' - error %d", + dev->pr_file_name, res); + + res = path_lookup(dev->pr_file_name1, 0, &nd); + if (!res) + scst_pr_vfs_unlink_and_put(&nd); + else + TRACE_DBG("Unable to lookup file '%s' - error %d", + dev->pr_file_name1, res); + + set_fs(old_fs); + + TRACE_EXIT(); + return; +} + +/* Must be called under dev_pr_mutex */ +void scst_pr_sync_device_file(struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd) +{ + int res = 0; + struct scst_device *dev = tgt_dev->dev; + struct file *file; + mm_segment_t old_fs = get_fs(); + loff_t pos = 0; + uint64_t sign; + uint64_t version; + uint8_t pr_is_set, aptpl; + + TRACE_ENTRY(); + + if ((dev->pr_aptpl == 0) || list_empty(&dev->dev_registrants_list)) { + scst_pr_remove_device_files(tgt_dev); + goto out; + } + + scst_pr_copy_file(dev->pr_file_name, dev->pr_file_name1); + + set_fs(KERNEL_DS); + + file = filp_open(dev->pr_file_name, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(file)) { + res = PTR_ERR(file); + PRINT_ERROR("Unable to (re)create PR file '%s' - error %d", + dev->pr_file_name, res); + goto out_set_fs; + } + + TRACE_PR("Updating pr file '%s'", dev->pr_file_name); + + /* + * signature + */ + sign = 0; + pos = 0; + res = vfs_write(file, (char *)&sign, sizeof(sign), &pos); + if (res != sizeof(sign)) + goto write_error; + + /* + * version + */ + version = SCST_PR_FILE_VERSION; + res = vfs_write(file, (char *)&version, sizeof(version), &pos); + if (res != sizeof(version)) + goto write_error; + + /* + * APTPL + */ + aptpl = dev->pr_aptpl; + res = vfs_write(file, (char *)&aptpl, sizeof(aptpl), &pos); + if (res != sizeof(aptpl)) + goto write_error; + + /* + * reservation + */ + pr_is_set = dev->pr_is_set; + res = vfs_write(file, (char *)&pr_is_set, sizeof(pr_is_set), &pos); + if (res != sizeof(pr_is_set)) + goto write_error; + + res = vfs_write(file, &dev->pr_type, sizeof(dev->pr_type), &pos); + if (res != sizeof(dev->pr_type)) + goto write_error; + + res = vfs_write(file, &dev->pr_scope, sizeof(dev->pr_scope), &pos); + if (res != sizeof(dev->pr_scope)) + goto write_error; + + /* + * registration records + */ + if (!list_empty(&dev->dev_registrants_list)) { + struct scst_dev_registrant *reg; + + list_for_each_entry(reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + uint8_t is_holder = 0; + int size; + + is_holder = (dev->pr_holder == reg); + + res = vfs_write(file, &is_holder, sizeof(is_holder), + &pos); + if (res != sizeof(is_holder)) + goto write_error; + + size = tid_size(reg->transport_id); + res = vfs_write(file, reg->transport_id, size, &pos); + if (res != size) + goto write_error; + + res = vfs_write(file, (char *)®->key, + sizeof(reg->key), &pos); + if (res != sizeof(reg->key)) + goto write_error; + + res = vfs_write(file, (char *)®->rel_tgt_id, + sizeof(reg->rel_tgt_id), &pos); + if (res != sizeof(reg->rel_tgt_id)) + goto write_error; + } + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + res = scst_pr_vfs_fsync(file, 0, pos); +#else + res = vfs_fsync(file, file->f_path.dentry, 0); +#endif + if (res != 0) { + PRINT_ERROR("fsync() of the PR file failed: %d", res); + goto write_error_close; + } + + sign = SCST_PR_FILE_SIGN; + pos = 0; + res = vfs_write(file, (char *)&sign, sizeof(sign), &pos); + if (res != sizeof(sign)) + goto write_error; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) + res = scst_pr_vfs_fsync(file, 0, sizeof(sign)); +#else + res = vfs_fsync(file, file->f_path.dentry, 0); +#endif + if (res != 0) { + PRINT_ERROR("fsync() of the PR file failed: %d", res); + goto write_error_close; + } + + res = 0; + + filp_close(file, NULL); + +out_set_fs: + set_fs(old_fs); + +out: + if (res != 0) { + PRINT_CRIT_ERROR("Unable to save persistent information " + "(target %s, initiator %s, device %s)", + tgt_dev->sess->tgt->tgt_name, + tgt_dev->sess->initiator_name, dev->virt_name); +#if 0 /* + * Looks like it's safer to return SUCCESS and expect operator's + * intervention to be able to save the PR's state next time, than + * to return HARDWARE ERROR and screw up all the interaction with + * the affected initiator. + */ + if (cmd != NULL) + scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); +#endif + } + + TRACE_EXIT_RES(res); + return; + +write_error: + PRINT_ERROR("Error writing to '%s' - error %d", dev->pr_file_name, res); + +write_error_close: + filp_close(file, NULL); + { + struct nameidata nd; + int rc; + + rc = path_lookup(dev->pr_file_name, 0, &nd); + if (!rc) + scst_pr_vfs_unlink_and_put(&nd); + else + TRACE_PR("Unable to lookup '%s' - error %d", + dev->pr_file_name, rc); + } + goto out_set_fs; +} + +int scst_pr_check_pr_path(void) +{ + int res; + struct nameidata nd; + mm_segment_t old_fs = get_fs(); + + TRACE_ENTRY(); + + set_fs(KERNEL_DS); + + res = path_lookup(SCST_PR_DIR, 0, &nd); + if (res != 0) { + PRINT_ERROR("Unable to find %s (err %d), you should create " + "this directory manually or reinstall SCST", + SCST_PR_DIR, res); + goto out_setfs; + } + + scst_pr_path_put(&nd); + +out_setfs: + set_fs(old_fs); + + TRACE_EXIT_RES(res); + return res; +} + +#endif /* CONFIG_SCST_PROC */ + +/* Called under scst_mutex */ +int scst_pr_init_dev(struct scst_device *dev) +{ + int res = 0; + uint8_t q; + int name_len; + + TRACE_ENTRY(); + + name_len = snprintf(&q, sizeof(q), "%s/%s", SCST_PR_DIR, dev->virt_name) + 1; + dev->pr_file_name = kmalloc(name_len, GFP_KERNEL); + if (dev->pr_file_name == NULL) { + PRINT_ERROR("Allocation of device '%s' file path failed", + dev->virt_name); + res = -ENOMEM; + goto out; + } else + snprintf(dev->pr_file_name, name_len, "%s/%s", SCST_PR_DIR, + dev->virt_name); + + name_len = snprintf(&q, sizeof(q), "%s/%s.1", SCST_PR_DIR, dev->virt_name) + 1; + dev->pr_file_name1 = kmalloc(name_len, GFP_KERNEL); + if (dev->pr_file_name1 == NULL) { + PRINT_ERROR("Allocation of device '%s' backup file path failed", + dev->virt_name); + res = -ENOMEM; + goto out_free_name; + } else + snprintf(dev->pr_file_name1, name_len, "%s/%s.1", SCST_PR_DIR, + dev->virt_name); + +#ifndef CONFIG_SCST_PROC + res = scst_pr_load_device_file(dev); + if (res == -ENOENT) + res = 0; +#endif + + if (res != 0) + goto out_free_name1; + +out: + TRACE_EXIT_RES(res); + return res; + +out_free_name1: + kfree(dev->pr_file_name1); + dev->pr_file_name1 = NULL; + +out_free_name: + kfree(dev->pr_file_name); + dev->pr_file_name = NULL; + goto out; +} + +/* Called under scst_mutex */ +void scst_pr_clear_dev(struct scst_device *dev) +{ + struct scst_dev_registrant *reg, *tmp_reg; + + TRACE_ENTRY(); + + list_for_each_entry_safe(reg, tmp_reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + scst_pr_remove_registrant(dev, reg); + } + + kfree(dev->pr_file_name); + kfree(dev->pr_file_name1); + + TRACE_EXIT(); + return; +} + +/* Called under scst_mutex */ +int scst_pr_init_tgt_dev(struct scst_tgt_dev *tgt_dev) +{ + int res = 0; + struct scst_dev_registrant *reg; + struct scst_device *dev = tgt_dev->dev; + const uint8_t *transport_id = tgt_dev->sess->transport_id; + const uint16_t rel_tgt_id = tgt_dev->sess->tgt->rel_tgt_id; + + TRACE_ENTRY(); + + if (tgt_dev->sess->transport_id == NULL) + goto out; + + TRACE_PR("Looking for not used registrant %s/%d (tgt_dev %p, dev %p)", + debug_transport_id_to_initiator_name(transport_id), + rel_tgt_id, tgt_dev, dev); + + scst_pr_write_lock(dev); + + reg = scst_pr_find_reg_not_used_first(dev, transport_id, rel_tgt_id, + true); + if (reg != NULL) { + TRACE_PR("Assigning reg %p to tgt_dev %p", reg, tgt_dev); + tgt_dev->registrant = reg; + reg->tgt_dev = tgt_dev; + } + + scst_pr_write_unlock(dev); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* Called under scst_mutex */ +void scst_pr_clear_tgt_dev(struct scst_tgt_dev *tgt_dev) +{ + TRACE_ENTRY(); + + if (tgt_dev->registrant != NULL) { + struct scst_dev_registrant *reg = tgt_dev->registrant; + struct scst_device *dev = tgt_dev->dev; + struct scst_tgt_dev *t; + + scst_pr_write_lock(dev); + + tgt_dev->registrant = NULL; + reg->tgt_dev = NULL; + + list_for_each_entry(t, &dev->dev_tgt_dev_list, + dev_tgt_dev_list_entry) { + if ((t->registrant == NULL) && + (t->sess->tgt->rel_tgt_id == reg->rel_tgt_id) && + tid_equal(t->sess->transport_id, reg->transport_id)) { + TRACE_PR("Reassigning reg %s/%d (%p) to tgt_dev " + "%p (being cleared tgt_dev %p)", + debug_transport_id_to_initiator_name( + reg->transport_id), + reg->rel_tgt_id, reg, t, tgt_dev); + t->registrant = reg; + reg->tgt_dev = t; + break; + } + } + + scst_pr_write_unlock(dev); + } + + TRACE_EXIT(); + return; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +static int scst_pr_register_with_spec_i_pt(struct scst_cmd *cmd, + uint8_t *buffer, int buffer_size, struct list_head *rollback_list) +{ + int res = 0; + int offset, ext_size; + uint64_t action_key; + struct scst_device *dev = cmd->dev; + struct scst_session *sess = cmd->sess; + const uint16_t rel_tgt_id = sess->tgt->rel_tgt_id; + struct scst_dev_registrant *reg; + uint8_t *transport_id; + + action_key = get_unaligned((__be64 *)&buffer[8]); + + ext_size = be32_to_cpu(get_unaligned((__be32 *)&buffer[24])); + if ((ext_size + 28) > buffer_size) { + TRACE_PR("Invalid buffer size %d (max %d)", buffer_size, + ext_size + 28); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid)); + res = -EINVAL; + goto out; + } + + offset = 0; + while (offset < ext_size) { + transport_id = &buffer[28 + offset]; + + if ((offset + tid_size(transport_id)) > ext_size) { + TRACE_PR("Invalid transport_id size %d (max %d)", + tid_size(transport_id), ext_size - offset); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + res = -EINVAL; + goto out; + } + tid_secure(transport_id); + offset += tid_size(transport_id); + } + + offset = 0; + while (offset < ext_size) { + struct scst_tgt_dev *t; + + transport_id = &buffer[28 + offset]; + + if ((transport_id[0] & 0x0f) == SCSI_TRANSPORTID_PROTOCOLID_ISCSI && + (transport_id[0] & 0xc0) == 0) { + TRACE_PR("Wildcard iSCSI TransportID %s", + &transport_id[4]); + /* + * We can't use scst_mutex here, because of circular + * locking dependency with dev_pr_mutex. + */ + spin_lock_bh(&dev->dev_lock); + list_for_each_entry(t, &dev->dev_tgt_dev_list, + dev_tgt_dev_list_entry) { + if (!tid_equal(t->sess->transport_id, + transport_id)) + continue; + if (t->registrant == NULL) { + reg = scst_pr_add_registrant(dev, + t->sess->transport_id, + rel_tgt_id, action_key, t, + GFP_ATOMIC); + if (reg == NULL) { + spin_unlock_bh(&dev->dev_lock); + scst_set_busy(cmd); + res = -ENOMEM; + goto out; + } + } else { + reg = t->registrant; + TRACE_PR("Changing key of reg %p " + "(tgt_dev %p)", reg, t); + reg->rollback_key = reg->key; + reg->key = action_key; + } + list_add_tail(®->aux_list_entry, + rollback_list); + } + spin_unlock_bh(&dev->dev_lock); + } else { + reg = scst_pr_find_reg_not_used_first(dev, transport_id, + rel_tgt_id, false); + if (reg != NULL) { + TRACE_PR("Changing key of reg %p (tgt_dev %p)", + reg, reg->tgt_dev); + reg->rollback_key = reg->key; + reg->key = action_key; + goto rollback_add; + } + + reg = scst_pr_add_registrant(dev, transport_id, + rel_tgt_id, action_key, NULL, GFP_KERNEL); + if (reg == NULL) { + scst_set_busy(cmd); + res = -ENOMEM; + goto out; + } + +rollback_add: + list_add_tail(®->aux_list_entry, + rollback_list); + } + offset += tid_size(transport_id); + } +out: + return res; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +static void scst_pr_unregister(struct scst_device *dev, + struct scst_dev_registrant *reg) +{ + bool is_holder; + uint8_t pr_type; + + TRACE_ENTRY(); + + TRACE_PR("Unregistering key '%0llx'", reg->key); + + is_holder = scst_pr_is_holder(dev, reg); + pr_type = dev->pr_type; + + scst_pr_remove_registrant(dev, reg); + + if (is_holder && !dev->pr_is_set) { + /* A registration just released */ + switch (pr_type) { + case TYPE_WRITE_EXCLUSIVE_REGONLY: + case TYPE_EXCLUSIVE_ACCESS_REGONLY: + scst_pr_send_ua_all(dev, NULL, + SCST_LOAD_SENSE(scst_sense_reservation_released)); + break; + } + } + + TRACE_EXIT(); + return; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) +{ + int aptpl, spec_i_pt, all_tg_pt; + uint64_t key, action_key; + struct scst_device *dev = cmd->dev; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; + struct scst_session *sess = cmd->sess; + struct scst_dev_registrant *reg, *treg; + LIST_HEAD(rollback_list); + + TRACE_ENTRY(); + + aptpl = buffer[20] & 0x01; + spec_i_pt = (buffer[20] >> 3) & 0x01; + all_tg_pt = (buffer[20] >> 2) & 0x01; + key = get_unaligned((__be64 *)&buffer[0]); + action_key = get_unaligned((__be64 *)&buffer[8]); + + if (spec_i_pt == 0 && buffer_size != 24) { + TRACE_PR("Invalid buffer size %d", buffer_size); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid)); + goto out; + } + + if (all_tg_pt) { + TRACE_PR("%s", "ALL_TG_PT not supported"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + goto out; + } + +#ifdef CONFIG_SCST_PROC + if (aptpl) { + TRACE_PR("%s", "APTL not supported"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + goto out; + } +#endif + + reg = tgt_dev->registrant; + + TRACE_PR("Register: initiator %s/%d (%p), key '%0llx', action_key '%0llx' " + "(tgt_dev %p)", + debug_transport_id_to_initiator_name(sess->transport_id), + sess->tgt->rel_tgt_id, reg, key, action_key, tgt_dev); + + if (reg == NULL) { + TRACE_PR("tgt_dev %p is not registered yet - registering", + tgt_dev); + if (key) { + TRACE_PR("%s", "Key must be zero on new registration"); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out; + } + if (action_key) { + if (spec_i_pt) { + int rc; + rc = scst_pr_register_with_spec_i_pt(cmd, + buffer, buffer_size, &rollback_list); + if (rc != 0) + goto out_rollback; + } + + /* + * tgt_dev can be among TIDs for + * scst_pr_register_with_spec_i_pt() + */ + if (tgt_dev->registrant == NULL) { + reg = scst_pr_add_registrant(dev, + sess->transport_id, + sess->tgt->rel_tgt_id, + action_key, tgt_dev, GFP_KERNEL); + if (reg == NULL) { + scst_set_busy(cmd); + goto out_rollback; + } + } + } else + TRACE_PR("%s", "Doing nothing - action_key is zero"); + } else { + if (reg->key != key) { + TRACE_PR("tgt_dev %p already registered - reservation " + "key ('%0llx') mismatch", tgt_dev, reg->key); + scst_set_cmd_error_status(cmd, + SAM_STAT_RESERVATION_CONFLICT); + goto out; + } + if (spec_i_pt) { + TRACE_PR("%s", "spec_i_pt must be zero in this case"); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE( + scst_sense_invalid_field_in_cdb)); + goto out; + } + if (action_key == 0) + scst_pr_unregister(dev, reg); + else + reg->key = action_key; + } + + dev->pr_generation++; + + dev->pr_aptpl = aptpl; + + scst_pr_dump_prs(dev); + +out: + list_for_each_entry(reg, &rollback_list, aux_list_entry) { + reg->rollback_key = 0; + } + + TRACE_EXIT(); + return; + +out_rollback: + list_for_each_entry_safe(reg, treg, &rollback_list, aux_list_entry) { + list_del(®->aux_list_entry); + if (reg->rollback_key == 0) + scst_pr_remove_registrant(dev, reg); + else { + reg->key = reg->rollback_key; + reg->rollback_key = 0; + } + } + goto out; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_register_and_ignore(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size) +{ + int aptpl, all_tg_pt; + uint64_t action_key; + struct scst_dev_registrant *reg = NULL; + struct scst_device *dev = cmd->dev; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; + struct scst_session *sess = cmd->sess; + + TRACE_ENTRY(); + + aptpl = buffer[20] & 0x01; + all_tg_pt = (buffer[20] >> 2) & 0x01; + action_key = get_unaligned((__be64 *)&buffer[8]); + + if (buffer_size != 24) { + TRACE_PR("Invalid buffer size %d", buffer_size); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid)); + goto out; + } + + if (all_tg_pt) { + TRACE_PR("%s", "ALL_TG_PT not supported"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + goto out; + } + +#ifdef CONFIG_SCST_PROC + if (aptpl) { + TRACE_PR("%s", "APTL not supported"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + goto out; + } +#endif + + reg = tgt_dev->registrant; + + TRACE_PR("Register and ignore: initiator %s/%d (%p), action_key " + "'%016llx' (tgt_dev %p)", + debug_transport_id_to_initiator_name(sess->transport_id), + sess->tgt->rel_tgt_id, reg, action_key, tgt_dev); + + if (reg == NULL) { + TRACE_PR("Tgt_dev %p is not registered yet - trying to " + "register", tgt_dev); + if (action_key) { + reg = scst_pr_add_registrant(dev, sess->transport_id, + sess->tgt->rel_tgt_id, action_key, + cmd->tgt_dev, GFP_KERNEL); + if (reg == NULL) { + scst_set_busy(cmd); + goto out; + } + } else + TRACE_PR("%s", "Doing nothing, action_key is zero"); + } else { + if (action_key == 0) + scst_pr_unregister(dev, reg); + else + reg->key = action_key; + } + + dev->pr_generation++; + + dev->pr_aptpl = aptpl; + + scst_pr_dump_prs(dev); + +out: + TRACE_EXIT(); + return; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_register_and_move(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size) +{ + int aptpl; + int unreg; + int tid_buffer_size; + uint64_t key, action_key; + struct scst_device *dev = cmd->dev; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; + struct scst_session *sess = cmd->sess; + struct scst_dev_registrant *reg, *reg_move; + const uint8_t *transport_id = NULL; + uint8_t *transport_id_move = NULL; + uint16_t rel_tgt_id_move; + + TRACE_ENTRY(); + + aptpl = buffer[17] & 0x01; + key = get_unaligned((__be64 *)&buffer[0]); + action_key = get_unaligned((__be64 *)&buffer[8]); + unreg = (buffer[17] >> 1) & 0x01; + tid_buffer_size = be32_to_cpu(get_unaligned((__be32 *)&buffer[20])); + +#ifdef CONFIG_SCST_PROC + if (aptpl) { + TRACE_PR("%s", "APTL not supported"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + goto out; + } +#endif + + if ((tid_buffer_size + 24) > buffer_size) { + TRACE_PR("Invalid buffer size %d (%d)", + buffer_size, tid_buffer_size + 24); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + goto out; + } + + if (tid_buffer_size < 24) { + TRACE_PR("%s", "Transport id buffer too small"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + goto out; + } + + reg = tgt_dev->registrant; + /* We already checked reg is not NULL */ + if (reg->key != key) { + TRACE_PR("Registrant's %s/%d (%p) key %016llx mismatch with " + "%016llx (tgt_dev %p)", + debug_transport_id_to_initiator_name(reg->transport_id), + reg->rel_tgt_id, reg, reg->key, key, tgt_dev); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out; + } + + if (!dev->pr_is_set) { + TRACE_PR("%s", "There must be a PR"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out; + } + + /* + * This check also required by table "PERSISTENT RESERVE OUT service + * actions that are allowed in the presence of various reservations". + */ + if (!scst_pr_is_holder(dev, reg)) { + TRACE_PR("Registrant %s/%d (%p) is not a holder (tgt_dev %p)", + debug_transport_id_to_initiator_name( + reg->transport_id), reg->rel_tgt_id, + reg, tgt_dev); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out; + } + + if (action_key == 0) { + TRACE_PR("%s", "Action key must be non-zero"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out; + } + + transport_id = sess->transport_id; + transport_id_move = (uint8_t *)&buffer[24]; + rel_tgt_id_move = be16_to_cpu(get_unaligned((__be16 *)&buffer[18])); + + if ((tid_size(transport_id_move) + 24) > buffer_size) { + TRACE_PR("Invalid buffer size %d (%d)", + buffer_size, tid_size(transport_id_move) + 24); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + goto out; + } + + tid_secure(transport_id_move); + + if (dev->pr_type == TYPE_WRITE_EXCLUSIVE_ALL_REG || + dev->pr_type == TYPE_EXCLUSIVE_ACCESS_ALL_REG) { + TRACE_PR("Unable to finish operation due to wrong reservation " + "type %02x", dev->pr_type); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out; + } + + if (tid_equal(transport_id, transport_id_move)) { + TRACE_PR("%s", "Equal transport id's"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_parm_list)); + goto out; + } + + reg_move = scst_pr_find_reg_not_used_first(dev, transport_id_move, + rel_tgt_id_move, false); + if (reg_move == NULL) { + reg_move = scst_pr_add_registrant(dev, transport_id_move, + rel_tgt_id_move, action_key, NULL, GFP_KERNEL); + if (reg_move == NULL) { + scst_set_busy(cmd); + goto out; + } + } else { + TRACE_PR("Changing key for reg %p", reg); + reg_move->key = action_key; + } + + TRACE_PR("Register and move: from initiator %s/%d (%p, tgt_dev %p) to " + "initiator %s/%d (%p, tgt_dev %p), key '%016llx' (unreg %d)", + debug_transport_id_to_initiator_name(reg->transport_id), + reg->rel_tgt_id, reg, reg->tgt_dev, + debug_transport_id_to_initiator_name(transport_id_move), + rel_tgt_id_move, reg_move, reg_move->tgt_dev, action_key, + unreg); + + /* Move the holder */ + scst_pr_set_holder(dev, reg_move, dev->pr_scope, dev->pr_type); + + if (unreg) + scst_pr_remove_registrant(dev, reg); + + dev->pr_generation++; + + dev->pr_aptpl = aptpl; + + scst_pr_dump_prs(dev); + +out: + TRACE_EXIT(); + return; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_reserve(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) +{ + uint8_t scope, type; + uint64_t key; + struct scst_device *dev = cmd->dev; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; + struct scst_dev_registrant *reg; + + TRACE_ENTRY(); + + key = get_unaligned((__be64 *)&buffer[0]); + scope = (cmd->cdb[2] & 0x0f) >> 4; + type = cmd->cdb[2] & 0x0f; + + if (buffer_size != 24) { + TRACE_PR("Invalid buffer size %d", buffer_size); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid)); + goto out; + } + + if (!scst_pr_type_valid(type)) { + TRACE_PR("Invalid reservation type %d", type); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out; + } + + if (((cmd->cdb[2] & 0x0f) >> 4) != SCOPE_LU) { + TRACE_PR("Invalid reservation scope %d", scope); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out; + } + + reg = tgt_dev->registrant; + + TRACE_PR("Reserve: initiator %s/%d (%p), key '%016llx', scope %d, " + "type %d (tgt_dev %p)", + debug_transport_id_to_initiator_name(cmd->sess->transport_id), + cmd->sess->tgt->rel_tgt_id, reg, key, scope, type, tgt_dev); + + /* We already checked reg is not NULL */ + if (reg->key != key) { + TRACE_PR("Registrant's %p key %016llx mismatch with %016llx", + reg, reg->key, key); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out; + } + + if (!dev->pr_is_set) + scst_pr_set_holder(dev, reg, scope, type); + else { + if (!scst_pr_is_holder(dev, reg)) { + /* + * This check also required by table "PERSISTENT + * RESERVE OUT service actions that are allowed in the + * presence of various reservations". + */ + TRACE_PR("Only holder can override - reg %p is not a " + "holder", reg); + scst_set_cmd_error_status(cmd, + SAM_STAT_RESERVATION_CONFLICT); + goto out; + } else { + if (dev->pr_scope != scope || dev->pr_type != type) { + TRACE_PR("Error overriding scope or type for " + "reg %p", reg); + scst_set_cmd_error_status(cmd, + SAM_STAT_RESERVATION_CONFLICT); + goto out; + } else + TRACE_PR("Do nothing: reservation of reg %p " + "is the same", reg); + } + } + + scst_pr_dump_prs(dev); + +out: + TRACE_EXIT(); + return; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_release(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) +{ + int scope, type; + uint64_t key; + struct scst_device *dev = cmd->dev; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; + struct scst_dev_registrant *reg; + uint8_t cur_pr_type; + + TRACE_ENTRY(); + + key = get_unaligned((__be64 *)&buffer[0]); + scope = (cmd->cdb[2] & 0x0f) >> 4; + type = cmd->cdb[2] & 0x0f; + + if (buffer_size != 24) { + TRACE_PR("Invalid buffer size %d", buffer_size); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid)); + goto out; + } + + if (!dev->pr_is_set) { + TRACE_PR("%s", "There is no PR - do nothing"); + goto out; + } + + reg = tgt_dev->registrant; + + TRACE_PR("Release: initiator %s/%d (%p), key '%016llx', scope %d, type " + "%d (tgt_dev %p)", debug_transport_id_to_initiator_name( + cmd->sess->transport_id), + cmd->sess->tgt->rel_tgt_id, reg, key, scope, type, tgt_dev); + + /* We already checked reg is not NULL */ + if (reg->key != key) { + TRACE_PR("Registrant's %p key %016llx mismatch with %016llx", + reg, reg->key, key); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out; + } + + if (!scst_pr_is_holder(dev, reg)) { + TRACE_PR("Registrant %p is not a holder - do nothing", reg); + goto out; + } + + if (dev->pr_scope != scope || dev->pr_type != type) { + TRACE_PR("%s", "Released scope or type do not match with " + "holder"); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_release)); + goto out; + } + + cur_pr_type = dev->pr_type; /* it will be cleared */ + + scst_pr_clear_reservation(dev); + + switch (cur_pr_type) { + case TYPE_WRITE_EXCLUSIVE_REGONLY: + case TYPE_EXCLUSIVE_ACCESS_REGONLY: + case TYPE_WRITE_EXCLUSIVE_ALL_REG: + case TYPE_EXCLUSIVE_ACCESS_ALL_REG: + scst_pr_send_ua_all(dev, reg, + SCST_LOAD_SENSE(scst_sense_reservation_released)); + } + + scst_pr_dump_prs(dev); + +out: + TRACE_EXIT(); + return; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_clear(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) +{ + int scope, type; + uint64_t key; + struct scst_device *dev = cmd->dev; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; + struct scst_dev_registrant *reg, *r, *t; + + TRACE_ENTRY(); + + key = get_unaligned((__be64 *)&buffer[0]); + scope = (cmd->cdb[2] & 0x0f) >> 4; + type = cmd->cdb[2] & 0x0f; + + if (buffer_size != 24) { + TRACE_PR("Invalid buffer size %d", buffer_size); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid)); + goto out; + } + + reg = tgt_dev->registrant; + + TRACE_PR("Clear: initiator %s/%d (%p), key '%016llx' (tgt_dev %p)", + debug_transport_id_to_initiator_name(cmd->sess->transport_id), + cmd->sess->tgt->rel_tgt_id, reg, key, tgt_dev); + + /* We already checked reg is not NULL */ + if (reg->key != key) { + TRACE_PR("Registrant's %p key %016llx mismatch with %016llx", + reg, reg->key, key); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out; + } + + scst_pr_send_ua_all(dev, reg, + SCST_LOAD_SENSE(scst_sense_reservation_preempted)); + + list_for_each_entry_safe(r, t, &dev->dev_registrants_list, + dev_registrants_list_entry) { + scst_pr_remove_registrant(dev, r); + } + + dev->pr_generation++; + + scst_pr_dump_prs(dev); + +out: + TRACE_EXIT(); + return; +} + +static void scst_pr_do_preempt(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size, bool abort) +{ + uint64_t key, action_key; + int scope, type; + struct scst_device *dev = cmd->dev; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; + struct scst_dev_registrant *reg, *r, *rt; + LIST_HEAD(preempt_list); + + TRACE_ENTRY(); + + key = get_unaligned((__be64 *)&buffer[0]); + action_key = get_unaligned((__be64 *)&buffer[8]); + scope = (cmd->cdb[2] & 0x0f) >> 4; + type = cmd->cdb[2] & 0x0f; + + if (buffer_size != 24) { + TRACE_PR("Invalid buffer size %d", buffer_size); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_parameter_list_length_invalid)); + goto out; + } + + if (!scst_pr_type_valid(type)) { + TRACE_PR("Invalid reservation type %d", type); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out; + } + + reg = tgt_dev->registrant; + + TRACE_PR("Preempt%s: initiator %s/%d (%p), key '%016llx', action_key " + "'%016llx', scope %x type %x (tgt_dev %p)", + abort ? " and abort" : "", + debug_transport_id_to_initiator_name(cmd->sess->transport_id), + cmd->sess->tgt->rel_tgt_id, reg, key, action_key, scope, type, + tgt_dev); + + /* We already checked reg is not NULL */ + if (reg->key != key) { + TRACE_PR("Registrant's %p key %016llx mismatch with %016llx", + reg, reg->key, key); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out; + } + + if (!dev->pr_is_set) { + scst_pr_find_registrants_list_key(dev, action_key, + &preempt_list); + if (list_empty(&preempt_list)) + goto out_error; + list_for_each_entry_safe(r, rt, &preempt_list, aux_list_entry) { + if (r != reg) + scst_pr_send_ua_reg(dev, r, SCST_LOAD_SENSE( + scst_sense_registrations_preempted)); + scst_pr_remove_registrant(dev, r); + } + goto done; + } + + if (dev->pr_type == TYPE_WRITE_EXCLUSIVE_ALL_REG || + dev->pr_type == TYPE_EXCLUSIVE_ACCESS_ALL_REG) { + if (action_key == 0) { + scst_pr_find_registrants_list_all(dev, reg, + &preempt_list); + list_for_each_entry_safe(r, rt, &preempt_list, + aux_list_entry) { + if (r != reg) + scst_pr_send_ua_reg(dev, r, + SCST_LOAD_SENSE( + scst_sense_registrations_preempted)); + else + reg = NULL; + scst_pr_remove_registrant(dev, r); + } + if (reg != NULL) + scst_pr_set_holder(dev, reg, scope, type); + } else { + scst_pr_find_registrants_list_key(dev, action_key, + &preempt_list); + if (list_empty(&preempt_list)) + goto out_error; + list_for_each_entry_safe(r, rt, &preempt_list, + aux_list_entry) { + if (r != reg) + scst_pr_send_ua_reg(dev, r, + SCST_LOAD_SENSE( + scst_sense_registrations_preempted)); + else + reg = NULL; + scst_pr_remove_registrant(dev, r); + } + } + goto done; + } + + sBUG_ON(dev->pr_holder == NULL); + + if (dev->pr_holder->key != action_key) { + if (action_key == 0) { + scst_set_cmd_error(cmd, SCST_LOAD_SENSE( + scst_sense_invalid_field_in_parm_list)); + goto out; + } else { + scst_pr_find_registrants_list_key(dev, action_key, + &preempt_list); + if (list_empty(&preempt_list)) + goto out_error; + list_for_each_entry_safe(r, rt, &preempt_list, + aux_list_entry) { + if (r != reg) + scst_pr_send_ua_reg(dev, r, + SCST_LOAD_SENSE( + scst_sense_registrations_preempted)); + else + reg = NULL; + scst_pr_remove_registrant(dev, r); + } + goto done; + } + } + + scst_pr_find_registrants_list_key(dev, action_key, + &preempt_list); + + list_for_each_entry_safe(r, rt, &preempt_list, aux_list_entry) { + if (abort) + scst_pr_abort_reg(dev, cmd, r); + if (r != reg) + scst_pr_send_ua_reg(dev, r, SCST_LOAD_SENSE( + scst_sense_registrations_preempted)); + else + reg = NULL; + scst_pr_remove_registrant(dev, r); + } + + if (dev->pr_type != type || dev->pr_scope != scope) + list_for_each_entry(r, &dev->dev_registrants_list, + dev_registrants_list_entry) { + if (r != reg) + scst_pr_send_ua_reg(dev, r, SCST_LOAD_SENSE( + scst_sense_reservation_released)); + } + + if (reg != NULL) + scst_pr_set_holder(dev, reg, scope, type); + +done: + dev->pr_generation++; + + scst_pr_dump_prs(dev); + +out: + TRACE_EXIT(); + return; + +out_error: + TRACE_PR("Invalid key '%016llx'", action_key); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_preempt(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) +{ + TRACE_ENTRY(); + + scst_pr_do_preempt(cmd, buffer, buffer_size, false); + + TRACE_EXIT(); + return; +} + +static void scst_cmd_done_pr_preempt(struct scst_cmd *cmd, int next_state, + enum scst_exec_context pref_context) +{ + void (*saved_cmd_done) (struct scst_cmd *cmd, int next_state, + enum scst_exec_context pref_context); + + TRACE_ENTRY(); + + saved_cmd_done = NULL; /* to remove warning that it's used not inited */ + + if (cmd->pr_abort_counter != NULL) { + if (!atomic_dec_and_test(&cmd->pr_abort_counter->pr_abort_pending_cnt)) + goto out; + saved_cmd_done = cmd->pr_abort_counter->saved_cmd_done; + kfree(cmd->pr_abort_counter); + cmd->pr_abort_counter = NULL; + } + + saved_cmd_done(cmd, next_state, pref_context); + +out: + TRACE_EXIT(); + return; +} + +/* + * Called with dev_pr_mutex locked, no IRQ. Expects session_list_lock + * not locked + */ +void scst_pr_preempt_and_abort(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size) +{ + TRACE_ENTRY(); + + cmd->pr_abort_counter = kzalloc(sizeof(*cmd->pr_abort_counter), + GFP_KERNEL); + if (cmd->pr_abort_counter == NULL) { + PRINT_ERROR("Unable to allocate PR abort counter (size %d)", + sizeof(*cmd->pr_abort_counter)); + scst_set_busy(cmd); + goto out; + } + + /* 1 to protect cmd from be done by the TM thread too early */ + atomic_set(&cmd->pr_abort_counter->pr_abort_pending_cnt, 1); + atomic_set(&cmd->pr_abort_counter->pr_aborting_cnt, 1); + init_completion(&cmd->pr_abort_counter->pr_aborting_cmpl); + + cmd->pr_abort_counter->saved_cmd_done = cmd->scst_cmd_done; + cmd->scst_cmd_done = scst_cmd_done_pr_preempt; + + scst_pr_do_preempt(cmd, buffer, buffer_size, true); + + if (!atomic_dec_and_test(&cmd->pr_abort_counter->pr_aborting_cnt)) + wait_for_completion(&cmd->pr_abort_counter->pr_aborting_cmpl); + +out: + TRACE_EXIT(); + return; +} + +/* Checks if this is a Compatible Reservation Handling (CRH) case */ +bool scst_pr_crh_case(struct scst_cmd *cmd) +{ + bool allowed; + struct scst_device *dev = cmd->dev; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; + struct scst_dev_registrant *reg; + uint8_t type; + + TRACE_ENTRY(); + + TRACE_DBG("Test if there is a CRH case for command '%s' (0x%x) from " + "'%s'", cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); + + if (!dev->pr_is_set) { + TRACE_PR("%s", "PR not set"); + allowed = false; + goto out; + } + + reg = tgt_dev->registrant; + type = dev->pr_type; + + switch (type) { + case TYPE_WRITE_EXCLUSIVE: + case TYPE_EXCLUSIVE_ACCESS: + WARN_ON(dev->pr_holder == NULL); + if (reg == dev->pr_holder) + allowed = true; + else + allowed = false; + break; + + case TYPE_WRITE_EXCLUSIVE_REGONLY: + case TYPE_EXCLUSIVE_ACCESS_REGONLY: + case TYPE_WRITE_EXCLUSIVE_ALL_REG: + case TYPE_EXCLUSIVE_ACCESS_ALL_REG: + allowed = (reg != NULL); + break; + + default: + PRINT_ERROR("Invalid PR type %x", type); + allowed = false; + break; + } + + if (!allowed) + TRACE_PR("Command '%s' (0x%x) from '%s' rejected due " + "to not CRH reservation", cmd->op_name, cmd->cdb[0], + cmd->sess->initiator_name); + else + TRACE_DBG("Command %s (0x%x) from '%s' is allowed to execute " + "due to CRH", cmd->op_name, cmd->cdb[0], + cmd->sess->initiator_name); + +out: + TRACE_EXIT_RES(allowed); + return allowed; + +} + +/* Check if command allowed in presence of reservation */ +bool scst_pr_is_cmd_allowed(struct scst_cmd *cmd) +{ + bool allowed; + struct scst_device *dev = cmd->dev; + struct scst_tgt_dev *tgt_dev = cmd->tgt_dev; + struct scst_dev_registrant *reg; + uint8_t type; + bool unlock; + + TRACE_ENTRY(); + + unlock = scst_pr_read_lock(dev); + + TRACE_DBG("Testing if command '%s' (0x%x) from '%s' allowed to execute", + cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); + + /* Recheck, because it can change while we were waiting for the lock */ + if (unlikely(!dev->pr_is_set)) { + allowed = true; + goto out_unlock; + } + + reg = tgt_dev->registrant; + type = dev->pr_type; + + switch (type) { + case TYPE_WRITE_EXCLUSIVE: + if (reg && reg == dev->pr_holder) + allowed = true; + else + allowed = (cmd->op_flags & SCST_WRITE_EXCL_ALLOWED) != 0; + break; + + case TYPE_EXCLUSIVE_ACCESS: + if (reg && reg == dev->pr_holder) + allowed = true; + else + allowed = (cmd->op_flags & SCST_EXCL_ACCESS_ALLOWED) != 0; + break; + + case TYPE_WRITE_EXCLUSIVE_REGONLY: + case TYPE_WRITE_EXCLUSIVE_ALL_REG: + if (reg) + allowed = true; + else + allowed = (cmd->op_flags & SCST_WRITE_EXCL_ALLOWED) != 0; + break; + + case TYPE_EXCLUSIVE_ACCESS_REGONLY: + case TYPE_EXCLUSIVE_ACCESS_ALL_REG: + if (reg) + allowed = true; + else + allowed = (cmd->op_flags & SCST_EXCL_ACCESS_ALLOWED) != 0; + break; + + default: + PRINT_ERROR("Invalid PR type %x", type); + allowed = false; + break; + } + + if (!allowed) + TRACE_PR("Command '%s' (0x%x) from '%s' rejected due " + "to PR", cmd->op_name, cmd->cdb[0], + cmd->sess->initiator_name); + else + TRACE_DBG("Command %s (0x%x) from '%s' is allowed to execute", + cmd->op_name, cmd->cdb[0], cmd->sess->initiator_name); + +out_unlock: + scst_pr_read_unlock(dev, unlock); + + TRACE_EXIT_RES(allowed); + return allowed; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_read_keys(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) +{ + int i, offset = 0, size, size_max; + struct scst_device *dev = cmd->dev; + struct scst_dev_registrant *reg; + + TRACE_ENTRY(); + + if (buffer_size < 8) { + TRACE_PR("buffer_size too small: %d. expected >= 8 " + "(buffer %p)", buffer_size, buffer); + goto skip; + } + + TRACE_PR("Read Keys (dev %s): PRGen %d", dev->virt_name, + dev->pr_generation); + + put_unaligned(cpu_to_be32(dev->pr_generation), (__be32 *)&buffer[0]); + + offset = 8; + size = 0; + size_max = buffer_size - 8; + + i = 0; + list_for_each_entry(reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + if (size_max - size > 8) { + TRACE_PR("Read Keys (dev %s): key 0x%llx", + dev->virt_name, reg->key); + + WARN_ON(reg->key == 0); + + put_unaligned(reg->key, + (__be64 *)&buffer[offset + 8 * i]); + + offset += 8; + } + size += 8; + } + + put_unaligned(cpu_to_be32(size), (__be32 *)&buffer[4]); + +skip: + scst_set_resp_data_len(cmd, offset); + + TRACE_EXIT(); + return; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_read_reservation(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size) +{ + struct scst_device *dev = cmd->dev; + uint8_t b[24]; + int size = 0; + + TRACE_ENTRY(); + + if (buffer_size < 8) { + TRACE_PR("buffer_size too small: %d. expected >= 8 " + "(buffer %p)", buffer_size, buffer); + goto skip; + } + + memset(b, 0, sizeof(b)); + + put_unaligned(cpu_to_be32(dev->pr_generation), (__be32 *)&buffer[0]); + + if (!dev->pr_is_set) { + TRACE_PR("Read Reservation: no reservations for dev %s", + dev->virt_name); + b[4] = + b[5] = + b[6] = + b[7] = 0; + + size = 8; + } else { + uint64_t key = dev->pr_holder ? dev->pr_holder->key : 0; + + TRACE_PR("Read Reservation: dev %s, holder %p, key 0x%llx, " + "scope %d, type %d", dev->virt_name, dev->pr_holder, + key, dev->pr_scope, dev->pr_type); + + b[4] = + b[5] = + b[6] = 0; + b[7] = 0x10; + + put_unaligned(key, (__be64 *)&b[8]); + b[21] = dev->pr_scope << 4 | dev->pr_type; + + size = 24; + } + + memset(buffer, 0, buffer_size); + memcpy(buffer, b, min(size, buffer_size)); + +skip: + scst_set_resp_data_len(cmd, size); + + TRACE_EXIT(); + return; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_report_caps(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size) +{ + int offset = 0; + unsigned int crh = 1; + unsigned int atp_c = 0; + unsigned int sip_c = 1; +#ifdef CONFIG_SCST_PROC + unsigned int ptpl_c = 0; +#else + unsigned int ptpl_c = 1; +#endif + struct scst_device *dev = cmd->dev; + + TRACE_ENTRY(); + + if (buffer_size < 8) { + TRACE_PR("buffer_size too small: %d. expected >= 8 " + "(buffer %p)", buffer_size, buffer); + goto skip; + } + + TRACE_PR("Reporting capabilities (dev %s): crh %x, sip_c %x, " + "atp_c %x, ptpl_c %x, pr_aptpl %x", dev->virt_name, + crh, sip_c, atp_c, ptpl_c, dev->pr_aptpl); + + buffer[0] = 0; + buffer[1] = 8; + + buffer[2] = crh << 4 | sip_c << 3 | atp_c << 2 | ptpl_c; + buffer[3] = (1 << 7) | (dev->pr_aptpl > 0 ? 1 : 0); + + /* All commands supported */ + buffer[4] = 0xEA; + buffer[5] = 0x1; + + offset += 8; + +skip: + scst_set_resp_data_len(cmd, offset); + + TRACE_EXIT(); + return; +} + +/* Called with dev_pr_mutex locked, no IRQ */ +void scst_pr_read_full_status(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size) +{ + int offset = 0, size, size_max; + struct scst_device *dev = cmd->dev; + struct scst_dev_registrant *reg; + + TRACE_ENTRY(); + + if (buffer_size < 8) + goto skip; + + put_unaligned(cpu_to_be32(dev->pr_generation), (__be32 *)&buffer[0]); + offset += 8; + + size = 0; + size_max = buffer_size - 8; + + list_for_each_entry(reg, &dev->dev_registrants_list, + dev_registrants_list_entry) { + int ts; + int rec_len; + + ts = tid_size(reg->transport_id); + rec_len = 24 + ts; + + if (size_max - size > rec_len) { + memset(&buffer[offset], 0, rec_len); + + put_unaligned(reg->key, (__be64 *)(&buffer[offset])); + + if (dev->pr_is_set && scst_pr_is_holder(dev, reg)) { + buffer[offset + 12] = 1; + buffer[offset + 13] = (dev->pr_scope << 8) | dev->pr_type; + } + + put_unaligned(cpu_to_be16(reg->rel_tgt_id), + (__be16 *)&buffer[offset + 18]); + put_unaligned(cpu_to_be32(ts), + (__be32 *)&buffer[offset + 20]); + + memcpy(&buffer[offset + 24], reg->transport_id, ts); + + offset += rec_len; + } + size += rec_len; + } + + put_unaligned(cpu_to_be32(size), (__be32 *)&buffer[4]); + +skip: + scst_set_resp_data_len(cmd, offset); + + TRACE_EXIT(); + return; +} diff --git a/scst/src/scst_pres.h b/scst/src/scst_pres.h new file mode 100644 index 000000000..a8669c1c1 --- /dev/null +++ b/scst/src/scst_pres.h @@ -0,0 +1,168 @@ +/* + * scst_pres.c + * + * Copyright (C) 2009 - 2010 Alexey Obitotskiy + * Copyright (C) 2009 - 2010 Open-E, Inc. + * Copyright (C) 2009 - 2010 Vladislav Bolkhovitin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef SCST_PRES_H_ +#define SCST_PRES_H_ + +#define PR_REGISTER 0x00 +#define PR_RESERVE 0x01 +#define PR_RELEASE 0x02 +#define PR_CLEAR 0x03 +#define PR_PREEMPT 0x04 +#define PR_PREEMPT_AND_ABORT 0x05 +#define PR_REGISTER_AND_IGNORE 0x06 +#define PR_REGISTER_AND_MOVE 0x07 + +#define PR_READ_KEYS 0x00 +#define PR_READ_RESERVATION 0x01 +#define PR_REPORT_CAPS 0x02 +#define PR_READ_FULL_STATUS 0x03 + +#define TYPE_UNSPECIFIED (-1) +#define TYPE_WRITE_EXCLUSIVE 0x01 +#define TYPE_EXCLUSIVE_ACCESS 0x03 +#define TYPE_WRITE_EXCLUSIVE_REGONLY 0x05 +#define TYPE_EXCLUSIVE_ACCESS_REGONLY 0x06 +#define TYPE_WRITE_EXCLUSIVE_ALL_REG 0x07 +#define TYPE_EXCLUSIVE_ACCESS_ALL_REG 0x08 + +#define SCOPE_LU 0x00 + +static inline bool scst_pr_type_valid(uint8_t type) +{ + switch (type) { + case TYPE_WRITE_EXCLUSIVE: + case TYPE_EXCLUSIVE_ACCESS: + case TYPE_WRITE_EXCLUSIVE_REGONLY: + case TYPE_EXCLUSIVE_ACCESS_REGONLY: + case TYPE_WRITE_EXCLUSIVE_ALL_REG: + case TYPE_EXCLUSIVE_ACCESS_ALL_REG: + return true; + default: + return false; + } +} + +#ifndef CONFIG_SCST_PROC +int scst_pr_check_pr_path(void); +#endif + +static inline bool scst_pr_read_lock(struct scst_device *dev) +{ + bool unlock = false; + + TRACE_ENTRY(); + + atomic_inc(&dev->pr_readers_count); + smp_mb__after_atomic_inc(); /* to sync with scst_pr_write_lock() */ + + if (unlikely(dev->pr_writer_active)) { + unlock = true; + atomic_dec(&dev->pr_readers_count); + mutex_lock(&dev->dev_pr_mutex); + } + + TRACE_EXIT_RES(unlock); + return unlock; +} + +static inline void scst_pr_read_unlock(struct scst_device *dev, bool unlock) +{ + TRACE_ENTRY(); + + if (unlikely(unlock)) + mutex_unlock(&dev->dev_pr_mutex); + else { + /* + * To sync with scst_pr_write_lock(). We need it to ensure + * order of our reads with the writer's writes. + */ + smp_mb__before_atomic_dec(); + atomic_dec(&dev->pr_readers_count); + } + + TRACE_EXIT(); + return; +} + +static inline void scst_pr_write_lock(struct scst_device *dev) +{ + TRACE_ENTRY(); + + mutex_lock(&dev->dev_pr_mutex); + + dev->pr_writer_active = 1; + + /* to sync with scst_pr_read_lock() and unlock() */ + smp_mb(); + + while (atomic_read(&dev->pr_readers_count) != 0) { + TRACE_DBG("Waiting for %d readers (dev %p)", + atomic_read(&dev->pr_readers_count), dev); + msleep(1); + } + + TRACE_EXIT(); + return; +} + +static inline void scst_pr_write_unlock(struct scst_device *dev) +{ + TRACE_ENTRY(); + + dev->pr_writer_active = 0; + + mutex_unlock(&dev->dev_pr_mutex); + + TRACE_EXIT(); + return; +} + +int scst_pr_init_dev(struct scst_device *dev); +void scst_pr_clear_dev(struct scst_device *dev); + +int scst_pr_init_tgt_dev(struct scst_tgt_dev *tgt_dev); +void scst_pr_clear_tgt_dev(struct scst_tgt_dev *tgt_dev); + +bool scst_pr_crh_case(struct scst_cmd *cmd); +bool scst_pr_is_cmd_allowed(struct scst_cmd *cmd); + +void scst_pr_register(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size); +void scst_pr_register_and_ignore(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size); +void scst_pr_register_and_move(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size); +void scst_pr_reserve(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size); +void scst_pr_release(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size); +void scst_pr_clear(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size); +void scst_pr_preempt(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size); +void scst_pr_preempt_and_abort(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size); + +void scst_pr_read_keys(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size); +void scst_pr_read_reservation(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size); +void scst_pr_report_caps(struct scst_cmd *cmd, uint8_t *buffer, int buffer_size); +void scst_pr_read_full_status(struct scst_cmd *cmd, uint8_t *buffer, + int buffer_size); + +#ifndef CONFIG_SCST_PROC +void scst_pr_sync_device_file(struct scst_tgt_dev *tgt_dev, struct scst_cmd *cmd); +#endif + +#endif /* SCST_PRES_H_ */ diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index e12e257d4..0e535aed2 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -108,7 +108,7 @@ extern unsigned long scst_trace_flag; **/ #define SCST_CMD_STATE_RES_CONT_NEXT SCST_EXEC_COMPLETED #define SCST_CMD_STATE_RES_CONT_SAME SCST_EXEC_NOT_COMPLETED -#define SCST_CMD_STATE_RES_NEED_THREAD SCST_EXEC_NEED_THREAD +#define SCST_CMD_STATE_RES_NEED_THREAD (SCST_EXEC_NOT_COMPLETED+1) /** ** Maximum count of uncompleted commands that an initiator could @@ -197,6 +197,10 @@ static inline bool scst_set_io_context(struct scst_cmd *cmd, { bool res; +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + return false; +#endif + if (cmd->cmd_threads == &scst_main_cmd_threads) { EXTRACHECKS_BUG_ON(in_interrupt()); /* @@ -414,6 +418,9 @@ int scst_alloc_space(struct scst_cmd *cmd); int scst_lib_init(void); void scst_lib_exit(void); +int scst_get_full_buf(struct scst_cmd *cmd, uint8_t **buf); +void scst_put_full_buf(struct scst_cmd *cmd, uint8_t *buf); + uint64_t scst_pack_lun(const uint64_t lun, unsigned int addr_method); uint64_t scst_unpack_lun(const uint8_t *lun, int len); @@ -567,7 +574,7 @@ int scst_set_pending_UA(struct scst_cmd *cmd); void scst_report_luns_changed(struct scst_acg *acg); void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, - int other_ini, int call_dev_task_mgmt_fn); + bool other_ini, bool call_dev_task_mgmt_fn); void scst_process_reset(struct scst_device *dev, struct scst_session *originator, struct scst_cmd *exclude_cmd, struct scst_mgmt_cmd *mcmd, bool setUA); @@ -732,8 +739,9 @@ static inline int scst_sn_before(uint32_t seq1, uint32_t seq2) return (int32_t)(seq1-seq2) < 0; } -bool scst_is_relative_target_port_id_unique(uint16_t id, struct scst_tgt *t); int gen_relative_target_port_id(uint16_t *id); +bool scst_is_relative_target_port_id_unique(uint16_t id, + const struct scst_tgt *t); #ifdef CONFIG_SCST_MEASURE_LATENCY diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index 7ac82e5c8..1e178ee2f 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -112,6 +112,7 @@ static struct scst_trace_log scst_proc_trace_tbl[] = { { TRACE_MGMT, "mgmt" }, { TRACE_MGMT_DEBUG, "mgmt_dbg" }, { TRACE_FLOW_CONTROL, "flow_control" }, + { TRACE_PRES, "pr" }, { 0, NULL } }; @@ -2311,12 +2312,12 @@ static int scst_version_info_show(struct seq_file *seq, void *v) seq_printf(seq, "USE_EXPECTED_VALUES\n"); #endif -#ifdef CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ - seq_printf(seq, "ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ\n"); +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + seq_printf(seq, "TEST_IO_IN_SIRQ\n"); #endif #ifdef CONFIG_SCST_STRICT_SECURITY - seq_printf(seq, "SCST_STRICT_SECURITY\n"); + seq_printf(seq, "STRICT_SECURITY\n"); #endif TRACE_EXIT(); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 87852db3f..9086de38e 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -85,6 +85,7 @@ static struct scst_trace_log scst_trace_tbl[] = { { TRACE_MGMT, "mgmt" }, { TRACE_MGMT_DEBUG, "mgmt_dbg" }, { TRACE_FLOW_CONTROL, "flow_control" }, + { TRACE_PRES, "pr" }, { 0, NULL } }; @@ -694,7 +695,7 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) retval = sysfs_create_file(tgt->tgt_luns_kobj, &scst_luns_mgmt.attr); if (retval != 0) { - PRINT_ERROR("Can't add tgt attr %s for tgt %s", + PRINT_ERROR("Can't add attribute %s for tgt %s", scst_luns_mgmt.attr.name, tgt->tgt_name); goto out; } @@ -710,7 +711,7 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) retval = sysfs_create_file(tgt->tgt_ini_grp_kobj, &scst_ini_group_mgmt.attr); if (retval != 0) { - PRINT_ERROR("Can't add tgt attr %s for tgt %s", + PRINT_ERROR("Can't add attribute %s for tgt %s", scst_ini_group_mgmt.attr.name, tgt->tgt_name); goto out; } @@ -2445,7 +2446,7 @@ static ssize_t scst_rel_tgt_id_store(struct kobject *kobj, if (res != 0) goto out_err; - TRACE_DBG("Try to set relative target port id %d", + TRACE_DBG("Trying to set relative target port id %d", (uint16_t)rel_tgt_id); if (rel_tgt_id < SCST_MIN_REL_TGT_ID || @@ -3051,7 +3052,7 @@ static ssize_t scst_trace_level_show(const struct scst_trace_log *local_tbl, #endif " special, scsi, mgmt, minor,\n" " mgmt_dbg, scsi_serializing,\n" - " retry, recv_bot, send_bot, recv_top,\n" + " retry, recv_bot, send_bot, recv_top, pr\n" " send_top%s]", help != NULL ? help : ""); return pos; @@ -3288,12 +3289,12 @@ static ssize_t scst_version_show(struct kobject *kobj, strcat(buf, "USE_EXPECTED_VALUES\n"); #endif -#ifdef CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ - strcat(buf, "ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ\n"); +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + strcat(buf, "TEST_IO_IN_SIRQ\n"); #endif #ifdef CONFIG_SCST_STRICT_SECURITY - strcat(buf, "SCST_STRICT_SECURITY\n"); + strcat(buf, "STRICT_SECURITY\n"); #endif TRACE_EXIT(); diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index a88938c2b..14c9885ea 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -32,6 +32,7 @@ #include "scst.h" #include "scst_priv.h" +#include "scst_pres.h" #if 0 /* Temporary left for future performance investigations */ /* Deleting it don't forget to delete write_cmd_count */ @@ -191,6 +192,12 @@ static int scst_init_cmd(struct scst_cmd *cmd, enum scst_exec_context *context) EXTRACHECKS_BUG_ON(*context == SCST_CONTEXT_SAME); +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + scst_get_cdb_info(cmd); + if (cmd->op_flags & SCST_TEST_IO_IN_SIRQ_ALLOWED) + goto out; +#endif + /* Small context optimization */ if (((*context == SCST_CONTEXT_TASKLET) || (*context == SCST_CONTEXT_DIRECT_ATOMIC)) && @@ -199,11 +206,8 @@ static int scst_init_cmd(struct scst_cmd *cmd, enum scst_exec_context *context) if (!test_bit(SCST_TGT_DEV_AFTER_INIT_WR_ATOMIC, &cmd->tgt_dev->tgt_dev_flags)) *context = SCST_CONTEXT_THREAD; - } else { - if (!test_bit(SCST_TGT_DEV_AFTER_INIT_OTH_ATOMIC, - &cmd->tgt_dev->tgt_dev_flags)) - *context = SCST_CONTEXT_THREAD; - } + } else + *context = SCST_CONTEXT_THREAD; } out: @@ -1000,21 +1004,16 @@ void scst_restart_cmd(struct scst_cmd *cmd, int status, cmd->state = SCST_CMD_STATE_TGT_PRE_EXEC; if (cmd->set_sn_on_restart_cmd) scst_cmd_set_sn(cmd); +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + if (cmd->op_flags & SCST_TEST_IO_IN_SIRQ_ALLOWED) + break; +#endif /* Small context optimization */ if ((pref_context == SCST_CONTEXT_TASKLET) || (pref_context == SCST_CONTEXT_DIRECT_ATOMIC) || ((pref_context == SCST_CONTEXT_SAME) && - scst_cmd_atomic(cmd))) { - if (cmd->data_direction & SCST_DATA_WRITE) { - if (!test_bit(SCST_TGT_DEV_AFTER_RESTART_WR_ATOMIC, - &cmd->tgt_dev->tgt_dev_flags)) - pref_context = SCST_CONTEXT_THREAD; - } else { - if (!test_bit(SCST_TGT_DEV_AFTER_RESTART_OTH_ATOMIC, - &cmd->tgt_dev->tgt_dev_flags)) - pref_context = SCST_CONTEXT_THREAD; - } - } + scst_cmd_atomic(cmd))) + pref_context = SCST_CONTEXT_THREAD; break; case SCST_PREPROCESS_STATUS_ERROR_SENSE_SET: @@ -1276,15 +1275,17 @@ void scst_rx_data(struct scst_cmd *cmd, int status, #endif cmd->state = SCST_CMD_STATE_TGT_PRE_EXEC; +#ifdef CONFIG_SCST_TEST_IO_IN_SIRQ + if (cmd->op_flags & SCST_TEST_IO_IN_SIRQ_ALLOWED) + break; +#endif + /* Small context optimization */ if ((pref_context == SCST_CONTEXT_TASKLET) || (pref_context == SCST_CONTEXT_DIRECT_ATOMIC) || ((pref_context == SCST_CONTEXT_SAME) && - scst_cmd_atomic(cmd))) { - if (!test_bit(SCST_TGT_DEV_AFTER_RX_DATA_ATOMIC, - &cmd->tgt_dev->tgt_dev_flags)) - pref_context = SCST_CONTEXT_THREAD; - } + scst_cmd_atomic(cmd))) + pref_context = SCST_CONTEXT_THREAD; break; case SCST_RX_STATUS_ERROR_SENSE_SET: @@ -1344,13 +1345,6 @@ static int scst_tgt_pre_exec(struct scst_cmd *cmd) SCST_LOAD_SENSE(scst_sense_hardw_error)); scst_set_cmd_abnormal_done_state(cmd); break; - case SCST_PREPROCESS_STATUS_NEED_THREAD: - TRACE_DBG("Target driver's %s pre_exec() requested " - "thread context, rescheduling", - cmd->tgtt->name); - res = SCST_CMD_STATE_RES_NEED_THREAD; - cmd->state = SCST_CMD_STATE_TGT_PRE_EXEC; - break; default: sBUG(); break; @@ -1501,6 +1495,8 @@ static void scst_cmd_done_local(struct scst_cmd *cmd, int next_state, { TRACE_ENTRY(); + EXTRACHECKS_BUG_ON(cmd->pr_abort_counter != NULL); + scst_set_exec_time(cmd); TRACE(TRACE_SCSI, "cmd %p, status %x, msg_status %x, host_status %x, " @@ -1560,11 +1556,6 @@ static int scst_report_luns_local(struct scst_cmd *cmd) TRACE_ENTRY(); - if (scst_cmd_atomic(cmd)) { - res = SCST_EXEC_NEED_THREAD; - goto out; - } - rc = scst_check_local_events(cmd); if (unlikely(rc != 0)) goto out_done; @@ -1699,7 +1690,6 @@ out_done: /* Report the result */ cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); -out: TRACE_EXIT_RES(res); return res; @@ -1824,16 +1814,10 @@ static int scst_pre_select(struct scst_cmd *cmd) TRACE_ENTRY(); - if (scst_cmd_atomic(cmd)) { - res = SCST_EXEC_NEED_THREAD; - goto out; - } - scst_block_dev_cmd(cmd, 1); /* Check for local events will be done when cmd will be executed */ -out: TRACE_EXIT_RES(res); return res; } @@ -1846,11 +1830,6 @@ static int scst_reserve_local(struct scst_cmd *cmd) TRACE_ENTRY(); - if (scst_cmd_atomic(cmd)) { - res = SCST_EXEC_NEED_THREAD; - goto out; - } - if ((cmd->cdb[0] == RESERVE_10) && (cmd->cdb[2] & SCST_RES_3RDPTY)) { PRINT_ERROR("RESERVE_10: 3rdPty RESERVE not implemented " "(lun=%lld)", (long long unsigned int)cmd->lun); @@ -1861,13 +1840,37 @@ static int scst_reserve_local(struct scst_cmd *cmd) dev = cmd->dev; - if (dev->tst == SCST_CONTR_MODE_ONE_TASK_SET) - scst_block_dev_cmd(cmd, 1); + /* + * There's no need to block this device, even for + * SCST_CONTR_MODE_ONE_TASK_SET, or anyhow else protect reservations + * changes, because: + * + * 1. The reservation changes are (rather) atomic, i.e., in contrast + * to persistent reservations, don't have any invalid intermediate + * states during being changed. + * + * 2. It's a duty of initiators to ensure order of regular commands + * around the reservation command either by ORDERED attribute, or by + * queue draining, or etc. For case of SCST_CONTR_MODE_ONE_TASK_SET + * there are no target drivers which can ensure even for ORDERED + * comamnds order of their delivery, so, because initiators know + * it, also there's no point to do any extra protection actions. + */ rc = scst_check_local_events(cmd); if (unlikely(rc != 0)) goto out_done; + if (!list_empty(&dev->dev_registrants_list)) { + if (scst_pr_crh_case(cmd)) + goto out_completed; + else { + scst_set_cmd_error_status(cmd, + SAM_STAT_RESERVATION_CONFLICT); + goto out_done; + } + } + spin_lock_bh(&dev->dev_lock); if (test_bit(SCST_TGT_DEV_RESERVED, &cmd->tgt_dev->tgt_dev_flags)) { @@ -1890,6 +1893,9 @@ out: TRACE_EXIT_RES(res); return res; +out_completed: + cmd->completed = 1; + out_done: /* Report the result */ cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); @@ -1905,20 +1911,27 @@ static int scst_release_local(struct scst_cmd *cmd) TRACE_ENTRY(); - if (scst_cmd_atomic(cmd)) { - res = SCST_EXEC_NEED_THREAD; - goto out; - } - dev = cmd->dev; - if (dev->tst == SCST_CONTR_MODE_ONE_TASK_SET) - scst_block_dev_cmd(cmd, 1); + /* + * See comment in scst_reserve_local() why no dev blocking or any + * other protection is needed here. + */ rc = scst_check_local_events(cmd); if (unlikely(rc != 0)) goto out_done; + if (!list_empty(&dev->dev_registrants_list)) { + if (scst_pr_crh_case(cmd)) + goto out_completed; + else { + scst_set_cmd_error_status(cmd, + SAM_STAT_RESERVATION_CONFLICT); + goto out_done; + } + } + spin_lock_bh(&dev->dev_lock); /* @@ -1952,6 +1965,9 @@ out: TRACE_EXIT_RES(res); return res; +out_completed: + cmd->completed = 1; + out_done: res = SCST_EXEC_COMPLETED; /* Report the result */ @@ -1973,6 +1989,273 @@ out_done: * * On call no locks, no IRQ or IRQ-disabled context allowed. */ +static int scst_persistent_reserve_in_local(struct scst_cmd *cmd) +{ + int rc; + struct scst_device *dev; + struct scst_tgt_dev *tgt_dev; + struct scst_session *session; + int action; + uint8_t *buffer; + int buffer_size; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(scst_cmd_atomic(cmd)); + + dev = cmd->dev; + tgt_dev = cmd->tgt_dev; + session = cmd->sess; + + rc = scst_check_local_events(cmd); + if (unlikely(rc != 0)) + goto out_done; + + if (unlikely(dev->not_pr_supporting_tgt_devs_num != 0)) { + PRINT_WARNING("Persistent Reservation command %x refused for " + "device %s, because the device has not supporting PR " + "transports connected", cmd->cdb[0], dev->virt_name); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + goto out_done; + } + + if (dev->dev_reserved) { + TRACE_PR("PR command rejected, because device %s holds regular " + "reservation", dev->virt_name); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out_done; + } + + if (dev->scsi_dev != NULL) { + PRINT_WARNING("PR commands for pass-through devices not " + "supported (device %s)", dev->virt_name); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + goto out_done; + } + + buffer_size = scst_get_full_buf(cmd, &buffer); + if (unlikely(buffer_size <= 0)) { + if (buffer_size < 0) + scst_set_busy(cmd); + goto out_done; + } + + scst_pr_write_lock(dev); + + /* We can be aborted by another PR command while waiting for the lock */ + if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) { + TRACE_MGMT_DBG("ABORTED set, aborting cmd %p", cmd); + goto out_unlock; + } + + action = cmd->cdb[1] & 0x1f; + + TRACE(TRACE_SCSI, "PR action %x for '%s' (LUN %llx) from '%s'", action, + dev->virt_name, tgt_dev->lun, session->initiator_name); + + switch (action) { + case PR_READ_KEYS: + scst_pr_read_keys(cmd, buffer, buffer_size); + break; + case PR_READ_RESERVATION: + scst_pr_read_reservation(cmd, buffer, buffer_size); + break; + case PR_REPORT_CAPS: + scst_pr_report_caps(cmd, buffer, buffer_size); + break; + case PR_READ_FULL_STATUS: + scst_pr_read_full_status(cmd, buffer, buffer_size); + break; + default: + PRINT_ERROR("Unsupported action %x", action); + scst_pr_write_unlock(dev); + goto out_err; + } + +out_complete: + cmd->completed = 1; + +out_unlock: + scst_pr_write_unlock(dev); + + scst_put_full_buf(cmd, buffer); + +out_done: + cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME); + + TRACE_EXIT_RES(SCST_EXEC_COMPLETED); + return SCST_EXEC_COMPLETED; + +out_err: + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out_complete; +} + +/* No locks, no IRQ or IRQ-disabled context allowed */ +static int scst_persistent_reserve_out_local(struct scst_cmd *cmd) +{ + int res = SCST_EXEC_COMPLETED; + int rc; + struct scst_device *dev; + struct scst_tgt_dev *tgt_dev; + struct scst_session *session; + int action; + uint8_t *buffer; + int buffer_size; + bool aborted = false; + + TRACE_ENTRY(); + + EXTRACHECKS_BUG_ON(scst_cmd_atomic(cmd)); + + dev = cmd->dev; + tgt_dev = cmd->tgt_dev; + session = cmd->sess; + + rc = scst_check_local_events(cmd); + if (unlikely(rc != 0)) + goto out_done; + + if (unlikely(dev->not_pr_supporting_tgt_devs_num != 0)) { + PRINT_WARNING("Persistent Reservation command %x refused for " + "device %s, because the device has not supporting PR " + "transports connected", cmd->cdb[0], dev->virt_name); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_opcode)); + goto out_done; + } + + action = cmd->cdb[1] & 0x1f; + + TRACE(TRACE_SCSI, "PR action %x for '%s' (LUN %llx) from '%s'", action, + dev->virt_name, tgt_dev->lun, session->initiator_name); + + if (dev->dev_reserved) { + TRACE_PR("PR command rejected, because device %s holds regular " + "reservation", dev->virt_name); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out_done; + } + + /* + * Check if tgt_dev already registered. Also by this check we make + * sure that table "PERSISTENT RESERVE OUT service actions that are + * allowed in the presence of various reservations" is honored. + * REGISTER AND MOVE and RESERVE will be additionally checked for + * conflicts later. + */ + if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_IGNORE) && + (tgt_dev->registrant == NULL)) { + TRACE_PR("'%s' not registered", cmd->sess->initiator_name); + scst_set_cmd_error_status(cmd, SAM_STAT_RESERVATION_CONFLICT); + goto out_done; + } + + buffer_size = scst_get_full_buf(cmd, &buffer); + if (unlikely(buffer_size <= 0)) { + if (buffer_size < 0) + scst_set_busy(cmd); + goto out_done; + } + + /* Check scope */ + if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_IGNORE) && + (action != PR_CLEAR) && ((cmd->cdb[2] & 0x0f) >> 4) != SCOPE_LU) { + TRACE_PR("Scope must be SCOPE_LU for action %x", action); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out_put_full_buf; + } + + /* Check SPEC_I_PT (PR_REGISTER_AND_MOVE has another format) */ + if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_MOVE) && + ((buffer[20] >> 3) & 0x01)) { + TRACE_PR("SPEC_I_PT must be zero for action %x", action); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE( + scst_sense_invalid_field_in_cdb)); + goto out_put_full_buf; + } + + /* Check ALL_TG_PT (PR_REGISTER_AND_MOVE has another format) */ + if ((action != PR_REGISTER) && (action != PR_REGISTER_AND_IGNORE) && + (action != PR_REGISTER_AND_MOVE) && ((buffer[20] >> 2) & 0x01)) { + TRACE_PR("ALL_TG_PT must be zero for action %x", action); + scst_set_cmd_error(cmd, SCST_LOAD_SENSE( + scst_sense_invalid_field_in_cdb)); + goto out_put_full_buf; + } + + scst_pr_write_lock(dev); + + /* We can be aborted by another PR command while waiting for the lock */ + aborted = test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags); + if (unlikely(aborted)) { + TRACE_MGMT_DBG("ABORTED set, aborting cmd %p", cmd); + goto out_unlock; + } + + switch (action) { + case PR_REGISTER: + scst_pr_register(cmd, buffer, buffer_size); + break; + case PR_RESERVE: + scst_pr_reserve(cmd, buffer, buffer_size); + break; + case PR_RELEASE: + scst_pr_release(cmd, buffer, buffer_size); + break; + case PR_CLEAR: + scst_pr_clear(cmd, buffer, buffer_size); + break; + case PR_PREEMPT: + scst_pr_preempt(cmd, buffer, buffer_size); + break; + case PR_PREEMPT_AND_ABORT: + scst_pr_preempt_and_abort(cmd, buffer, buffer_size); + break; + case PR_REGISTER_AND_IGNORE: + scst_pr_register_and_ignore(cmd, buffer, buffer_size); + break; + case PR_REGISTER_AND_MOVE: + scst_pr_register_and_move(cmd, buffer, buffer_size); + break; + default: + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb)); + goto out_unlock; + } + +#ifndef CONFIG_SCST_PROC + if (cmd->status == SAM_STAT_GOOD) + scst_pr_sync_device_file(tgt_dev, cmd); +#endif + + if ((dev->handler->pr_cmds_notifications) && + (cmd->status == SAM_STAT_GOOD)) /* sync file may change status */ + res = SCST_EXEC_NOT_COMPLETED; + +out_unlock: + scst_pr_write_unlock(dev); + +out_put_full_buf: + scst_put_full_buf(cmd, buffer); + +out_done: + if (SCST_EXEC_COMPLETED == res) { + if (!aborted) + cmd->completed = 1; + cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, + SCST_CONTEXT_SAME); + } + + TRACE_EXIT_RES(res); + return res; +} + +/* No locks, no IRQ or IRQ-disabled context allowed */ int scst_check_local_events(struct scst_cmd *cmd) { int res, rc; @@ -1988,11 +2271,6 @@ int scst_check_local_events(struct scst_cmd *cmd) if (unlikely(dev->dev_double_ua_possible)) cmd->double_ua_possible = 1; - if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) { - TRACE_MGMT_DBG("ABORTED set, aborting cmd %p", cmd); - goto out_uncomplete; - } - /* Reserve check before Unit Attention */ if (unlikely(test_bit(SCST_TGT_DEV_RESERVED, &tgt_dev->tgt_dev_flags))) { @@ -2003,6 +2281,23 @@ int scst_check_local_events(struct scst_cmd *cmd) } } + if (dev->pr_is_set) { + if (unlikely(!scst_pr_is_cmd_allowed(cmd))) { + scst_set_cmd_error_status(cmd, + SAM_STAT_RESERVATION_CONFLICT); + goto out_complete; + } + } + + /* + * Let's check for ABORTED after scst_pr_is_cmd_allowed(), because + * we might sleep for a while there. + */ + if (unlikely(test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags))) { + TRACE_MGMT_DBG("ABORTED set, aborting cmd %p", cmd); + goto out_uncomplete; + } + /* If we had internal bus reset, set the command error unit attention */ if ((dev->scsi_dev != NULL) && unlikely(dev->scsi_dev->was_reset)) { @@ -2140,7 +2435,6 @@ static int scst_do_real_exec(struct scst_cmd *cmd) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) int rc; #endif - bool atomic = scst_cmd_atomic(cmd); struct scst_device *dev = cmd->dev; struct scst_dev_type *handler = dev->handler; struct io_context *old_ctx = NULL; @@ -2148,23 +2442,11 @@ static int scst_do_real_exec(struct scst_cmd *cmd) TRACE_ENTRY(); - if (!atomic) - ctx_changed = scst_set_io_context(cmd, &old_ctx); + ctx_changed = scst_set_io_context(cmd, &old_ctx); cmd->state = SCST_CMD_STATE_REAL_EXECUTING; if (handler->exec) { - if (unlikely(!dev->handler->exec_atomic && atomic)) { - /* - * It shouldn't be because of the SCST_TGT_DEV_AFTER_* - * optimization. - */ - TRACE_DBG("Dev handler %s exec() needs thread " - "context, rescheduling", dev->handler->name); - res = SCST_EXEC_NEED_THREAD; - goto out_restore; - } - TRACE_DBG("Calling dev handler %s exec(%p)", handler->name, cmd); TRACE_BUFF_FLAG(TRACE_SND_TOP, "Execing: ", cmd->cdb, @@ -2176,8 +2458,6 @@ static int scst_do_real_exec(struct scst_cmd *cmd) if (res == SCST_EXEC_COMPLETED) goto out_complete; - else if (res == SCST_EXEC_NEED_THREAD) - goto out_restore; scst_set_exec_time(cmd); @@ -2197,28 +2477,13 @@ static int scst_do_real_exec(struct scst_cmd *cmd) if (unlikely(res != 0)) goto out_done; -#ifndef CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ - if (unlikely(atomic)) { - TRACE_DBG("Pass-through exec() can not be called in atomic " - "context, rescheduling to the thread (handler %s)", - handler->name); - res = SCST_EXEC_NEED_THREAD; - goto out_restore; - } -#endif - scst_set_cur_start(cmd); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) if (unlikely(scst_alloc_request(cmd) != 0)) { - if (atomic) { - res = SCST_EXEC_NEED_THREAD; - goto out_restore; - } else { - PRINT_INFO("%s", "Unable to allocate request, " - "sending BUSY status"); - goto out_busy; - } + PRINT_INFO("%s", "Unable to allocate request, sending BUSY " + "status"); + goto out_busy; } scst_do_req(cmd->scsi_req, (void *)cmd->cdb, @@ -2230,37 +2495,25 @@ static int scst_do_real_exec(struct scst_cmd *cmd) rc = scst_exec_req(dev->scsi_dev, cmd->cdb, cmd->cdb_len, cmd->data_direction, cmd->sg, cmd->bufflen, cmd->sg_cnt, cmd->timeout, cmd->retries, cmd, scst_cmd_done, - atomic ? GFP_ATOMIC : GFP_KERNEL); + GFP_KERNEL); #else rc = scst_scsi_exec_async(cmd, scst_cmd_done); #endif if (unlikely(rc != 0)) { - if (atomic) { - res = SCST_EXEC_NEED_THREAD; - goto out_restore; - } else { - PRINT_ERROR("scst pass-through exec failed: %x", rc); - goto out_error; - } + PRINT_ERROR("scst pass-through exec failed: %x", rc); + goto out_error; } #endif out_complete: res = SCST_EXEC_COMPLETED; -out_reset_ctx: if (ctx_changed) scst_reset_io_context(cmd->tgt_dev, old_ctx); TRACE_EXIT(); return res; -out_restore: - scst_set_exec_time(cmd); - /* Restore the state */ - cmd->state = SCST_CMD_STATE_REAL_EXEC; - goto out_reset_ctx; - out_error: scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_hardw_error)); goto out_done; @@ -2286,7 +2539,6 @@ static inline int scst_real_exec(struct scst_cmd *cmd) BUILD_BUG_ON(SCST_CMD_STATE_RES_CONT_SAME != SCST_EXEC_NOT_COMPLETED); BUILD_BUG_ON(SCST_CMD_STATE_RES_CONT_NEXT != SCST_EXEC_COMPLETED); - BUILD_BUG_ON(SCST_CMD_STATE_RES_NEED_THREAD != SCST_EXEC_NEED_THREAD); __scst_cmd_get(cmd); @@ -2298,7 +2550,7 @@ static inline int scst_real_exec(struct scst_cmd *cmd) generic_unplug_device( cmd->dev->scsi_dev->request_queue); } else - sBUG_ON(res != SCST_EXEC_NEED_THREAD); + sBUG(); __scst_cmd_put(cmd); @@ -2346,6 +2598,12 @@ static int scst_do_local_exec(struct scst_cmd *cmd) case RELEASE_10: res = scst_release_local(cmd); break; + case PERSISTENT_RESERVE_IN: + res = scst_persistent_reserve_in_local(cmd); + break; + case PERSISTENT_RESERVE_OUT: + res = scst_persistent_reserve_out_local(cmd); + break; case REPORT_LUNS: res = scst_report_luns_local(cmd); break; @@ -2376,7 +2634,6 @@ static int scst_local_exec(struct scst_cmd *cmd) BUILD_BUG_ON(SCST_CMD_STATE_RES_CONT_SAME != SCST_EXEC_NOT_COMPLETED); BUILD_BUG_ON(SCST_CMD_STATE_RES_CONT_NEXT != SCST_EXEC_COMPLETED); - BUILD_BUG_ON(SCST_CMD_STATE_RES_NEED_THREAD != SCST_EXEC_NEED_THREAD); __scst_cmd_get(cmd); @@ -2386,7 +2643,7 @@ static int scst_local_exec(struct scst_cmd *cmd) else if (res == SCST_EXEC_COMPLETED) scst_post_exec_sn(cmd, true); else - sBUG_ON(res != SCST_EXEC_NEED_THREAD); + sBUG(); __scst_cmd_put(cmd); @@ -2430,13 +2687,7 @@ static int scst_exec(struct scst_cmd **active_cmd) rc = scst_do_local_exec(cmd); if (likely(rc == SCST_EXEC_NOT_COMPLETED)) /* Nothing to do */; - else if (rc == SCST_EXEC_NEED_THREAD) { - TRACE_DBG("%s", "scst_do_local_exec() requested " - "thread context, rescheduling"); - scst_dec_on_dev_cmd(cmd); - res = SCST_CMD_STATE_RES_NEED_THREAD; - break; - } else { + else { sBUG_ON(rc != SCST_EXEC_COMPLETED); goto done; } @@ -2444,16 +2695,7 @@ static int scst_exec(struct scst_cmd **active_cmd) cmd->state = SCST_CMD_STATE_REAL_EXEC; rc = scst_do_real_exec(cmd); - if (likely(rc == SCST_EXEC_COMPLETED)) - /* Nothing to do */; - else if (rc == SCST_EXEC_NEED_THREAD) { - TRACE_DBG("scst_real_exec() requested thread " - "context, rescheduling (cmd %p)", cmd); - scst_dec_on_dev_cmd(cmd); - res = SCST_CMD_STATE_RES_NEED_THREAD; - break; - } else - sBUG(); + sBUG_ON(rc != SCST_EXEC_COMPLETED); done: count++; @@ -2840,11 +3082,11 @@ out: static int scst_mode_select_checks(struct scst_cmd *cmd) { int res = SCST_CMD_STATE_RES_CONT_SAME; - int atomic = scst_cmd_atomic(cmd); TRACE_ENTRY(); if (likely(scsi_status_is_good(cmd->status))) { + int atomic = scst_cmd_atomic(cmd); if (unlikely((cmd->cdb[0] == MODE_SELECT) || (cmd->cdb[0] == MODE_SELECT_10) || (cmd->cdb[0] == LOG_SELECT))) { @@ -2897,6 +3139,7 @@ static int scst_mode_select_checks(struct scst_cmd *cmd) scst_analyze_sense(cmd->sense, cmd->sense_valid_len, SCST_SENSE_ASC_VALID, 0, 0x2F, 0))) { + int atomic = scst_cmd_atomic(cmd); if (atomic) { TRACE_DBG("Possible parameters changed UA %x: " "thread context required", cmd->sense[12]); @@ -3085,9 +3328,8 @@ static int scst_pre_xmit_response(struct scst_cmd *cmd) scst_store_sense(cmd); if (unlikely(test_bit(SCST_CMD_NO_RESP, &cmd->cmd_flags))) { - TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu)," - " skipping", - cmd, (long long unsigned int)cmd->tag); + TRACE_MGMT_DBG("Flag NO_RESP set for cmd %p (tag %llu), " + "skipping", cmd, (long long unsigned int)cmd->tag); cmd->state = SCST_CMD_STATE_FINISHED; res = SCST_CMD_STATE_RES_CONT_SAME; goto out; @@ -4087,7 +4329,7 @@ out: /* No locks */ void scst_done_cmd_mgmt(struct scst_cmd *cmd) { - struct scst_mgmt_cmd_stub *mstb; + struct scst_mgmt_cmd_stub *mstb, *t; bool wake = 0; unsigned long flags; @@ -4098,7 +4340,7 @@ void scst_done_cmd_mgmt(struct scst_cmd *cmd) spin_lock_irqsave(&scst_mcmd_lock, flags); - list_for_each_entry(mstb, &cmd->mgmt_cmd_list, + list_for_each_entry_safe(mstb, t, &cmd->mgmt_cmd_list, cmd_mgmt_cmd_list_entry) { struct scst_mgmt_cmd *mcmd; @@ -4110,22 +4352,30 @@ void scst_done_cmd_mgmt(struct scst_cmd *cmd) mcmd, mcmd->cmd_done_wait_count); mcmd->cmd_done_wait_count--; + + sBUG_ON(mcmd->cmd_done_wait_count < 0); + if (mcmd->cmd_done_wait_count > 0) { TRACE_MGMT_DBG("cmd_done_wait_count(%d) not 0, " "skipping", mcmd->cmd_done_wait_count); - continue; + goto check_free; } - if (mcmd->completed) { - sBUG_ON(mcmd->affected_cmds_done_called); - mcmd->completed = 0; - mcmd->state = SCST_MCMD_STATE_POST_AFFECTED_CMDS_DONE; + if (mcmd->state == SCST_MCMD_STATE_WAITING_AFFECTED_CMDS_DONE) { + mcmd->state = SCST_MCMD_STATE_AFFECTED_CMDS_DONE; TRACE_MGMT_DBG("Adding mgmt cmd %p to active mgmt cmd " "list", mcmd); list_add_tail(&mcmd->mgmt_cmd_list_entry, &scst_active_mgmt_cmd_list); wake = 1; } + +check_free: + if (!mstb->finish_counted) { + TRACE_DBG("Releasing mstb %p", mstb); + list_del(&mstb->cmd_mgmt_cmd_list_entry); + mempool_free(mstb, scst_mgmt_stub_mempool); + } } spin_unlock_irqrestore(&scst_mcmd_lock, flags); @@ -4138,18 +4388,27 @@ void scst_done_cmd_mgmt(struct scst_cmd *cmd) } /* Called under scst_mcmd_lock and IRQs disabled */ -static int __scst_dec_finish_wait_count(struct scst_mgmt_cmd *mcmd, bool *wake) +static void __scst_dec_finish_wait_count(struct scst_mgmt_cmd *mcmd, bool *wake) { TRACE_ENTRY(); mcmd->cmd_finish_wait_count--; + + sBUG_ON(mcmd->cmd_finish_wait_count < 0); + if (mcmd->cmd_finish_wait_count > 0) { TRACE_MGMT_DBG("cmd_finish_wait_count(%d) not 0, " "skipping", mcmd->cmd_finish_wait_count); goto out; } - if (mcmd->completed) { + if (mcmd->cmd_done_wait_count > 0) { + TRACE_MGMT_DBG("cmd_done_wait_count(%d) not 0, " + "skipping", mcmd->cmd_done_wait_count); + goto out; + } + + if (mcmd->state == SCST_MCMD_STATE_WAITING_AFFECTED_CMDS_FINISHED) { mcmd->state = SCST_MCMD_STATE_DONE; TRACE_MGMT_DBG("Adding mgmt cmd %p to active mgmt cmd " "list", mcmd); @@ -4159,8 +4418,8 @@ static int __scst_dec_finish_wait_count(struct scst_mgmt_cmd *mcmd, bool *wake) } out: - TRACE_EXIT_RES(mcmd->cmd_finish_wait_count); - return mcmd->cmd_finish_wait_count; + TRACE_EXIT(); + return; } /** @@ -4242,20 +4501,19 @@ static void scst_finish_cmd_mgmt(struct scst_cmd *cmd) cmd_mgmt_cmd_list_entry) { struct scst_mgmt_cmd *mcmd = mstb->mcmd; - TRACE_MGMT_DBG("mcmd %p, mcmd->cmd_finish_wait_count %d", - mcmd, mcmd->cmd_finish_wait_count); + TRACE_MGMT_DBG("mcmd %p, mcmd->cmd_finish_wait_count %d", mcmd, + mcmd->cmd_finish_wait_count); - list_del(&mstb->cmd_mgmt_cmd_list_entry); - mempool_free(mstb, scst_mgmt_stub_mempool); + sBUG_ON(!mstb->finish_counted); if (cmd->completed) mcmd->completed_cmd_count++; - if (__scst_dec_finish_wait_count(mcmd, &wake) > 0) { - TRACE_MGMT_DBG("cmd_finish_wait_count(%d) not 0, " - "skipping", mcmd->cmd_finish_wait_count); - continue; - } + __scst_dec_finish_wait_count(mcmd, &wake); + + TRACE_DBG("Releasing mstb %p", mstb); + list_del(&mstb->cmd_mgmt_cmd_list_entry); + mempool_free(mstb, scst_mgmt_stub_mempool); } spin_unlock_irqrestore(&scst_mcmd_lock, flags); @@ -4304,7 +4562,7 @@ static inline int scst_is_strict_mgmt_fn(int mgmt_fn) /* Might be called under sess_list_lock and IRQ off + BHs also off */ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, - int other_ini, int call_dev_task_mgmt_fn) + bool other_ini, bool call_dev_task_mgmt_fn) { unsigned long flags; static DEFINE_SPINLOCK(other_ini_lock); @@ -4376,14 +4634,9 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, } memset(mstb, 0, sizeof(*mstb)); - mstb->mcmd = mcmd; + TRACE_DBG("mstb %p, mcmd %p", mstb, mcmd); - /* - * cmd can't die here or sess_list_lock already taken and - * cmd is in the sess list - */ - list_add_tail(&mstb->cmd_mgmt_cmd_list_entry, - &cmd->mgmt_cmd_list); + mstb->mcmd = mcmd; /* * Delay the response until the command's finish in order to @@ -4396,23 +4649,45 @@ void scst_abort_cmd(struct scst_cmd *cmd, struct scst_mgmt_cmd *mcmd, * command actually gets executed *after* new commands sent * after this TM command completed. */ - TRACE_MGMT_DBG("cmd %p (tag %llu, sn %u) being " - "executed/xmitted (state %d, op %x, proc time %ld " - "sec., timeout %d sec.), deferring ABORT...", cmd, - (long long unsigned int)cmd->tag, cmd->sn, cmd->state, - cmd->cdb[0], (long)(jiffies - cmd->start_time) / HZ, - cmd->timeout / HZ); - - mcmd->cmd_finish_wait_count++; if (cmd->sent_for_exec && !cmd->done) { - TRACE_MGMT_DBG("cmd %p (tag %llu) is being executed " - "and not done yet", cmd, - (long long unsigned int)cmd->tag); + TRACE_MGMT_DBG("cmd %p (tag %llu) is being executed", + cmd, (long long unsigned int)cmd->tag); mstb->done_counted = 1; mcmd->cmd_done_wait_count++; } + + /* + * We don't have to wait the command's status delivery finish + * to other initiators + it can affect MPIO failover. + */ + if (!other_ini) { + mstb->finish_counted = 1; + mcmd->cmd_finish_wait_count++; + } + + if (mstb->done_counted || mstb->finish_counted) { + TRACE_MGMT_DBG("cmd %p (tag %llu, sn %u) being " + "executed/xmitted (state %d, op %x, proc time " + "%ld sec., timeout %d sec.), deferring ABORT " + "(cmd_done_wait_count %d, cmd_finish_wait_count " + "%d)", cmd, (long long unsigned int)cmd->tag, + cmd->sn, cmd->state, cmd->cdb[0], + (long)(jiffies - cmd->start_time) / HZ, + cmd->timeout / HZ, mcmd->cmd_done_wait_count, + mcmd->cmd_finish_wait_count); + /* + * cmd can't die here or sess_list_lock already taken + * and cmd is in the sess list + */ + list_add_tail(&mstb->cmd_mgmt_cmd_list_entry, + &cmd->mgmt_cmd_list); + } else { + /* We don't need to wait for this cmd */ + mempool_free(mstb, scst_mgmt_stub_mempool); + } } + unlock: spin_unlock_irqrestore(&scst_mcmd_lock, flags); @@ -4422,35 +4697,58 @@ unlock: return; } -/* No locks */ +/* No locks. Returns 0, if mcmd should be processed further. */ static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd) { int res; spin_lock_irq(&scst_mcmd_lock); - if (mcmd->cmd_finish_wait_count == 0) { - if (!mcmd->affected_cmds_done_called) - mcmd->state = SCST_MCMD_STATE_POST_AFFECTED_CMDS_DONE; - else + switch (mcmd->state) { + case SCST_MCMD_STATE_INIT: + case SCST_MCMD_STATE_EXEC: + if (mcmd->cmd_done_wait_count == 0) { + mcmd->state = SCST_MCMD_STATE_AFFECTED_CMDS_DONE; + res = 0; + } else { + TRACE_MGMT_DBG("cmd_done_wait_count(%d) not 0, " + "preparing to wait", mcmd->cmd_done_wait_count); + mcmd->state = SCST_MCMD_STATE_WAITING_AFFECTED_CMDS_DONE; + res = -1; + } + break; + + case SCST_MCMD_STATE_AFFECTED_CMDS_DONE: + if (mcmd->cmd_finish_wait_count == 0) { mcmd->state = SCST_MCMD_STATE_DONE; + res = 0; + } else { + TRACE_MGMT_DBG("cmd_finish_wait_count(%d) not 0, " + "preparing to wait", + mcmd->cmd_finish_wait_count); + mcmd->state = SCST_MCMD_STATE_WAITING_AFFECTED_CMDS_FINISHED; + res = -1; + } + break; + + case SCST_MCMD_STATE_DONE: + mcmd->state = SCST_MCMD_STATE_FINISHED; res = 0; - } else if ((mcmd->cmd_done_wait_count == 0) && - (!mcmd->affected_cmds_done_called)) { - mcmd->state = SCST_MCMD_STATE_POST_AFFECTED_CMDS_DONE; - res = 0; - goto out_unlock; - } else { - TRACE_MGMT_DBG("cmd_finish_wait_count(%d) not 0, preparing to " - "wait", mcmd->cmd_finish_wait_count); - mcmd->state = SCST_MCMD_STATE_EXECUTING; - res = -1; + break; + + default: + PRINT_CRIT_ERROR("Wrong mcmd %p state %d (fn %d, " + "cmd_finish_wait_count %d, cmd_done_wait_count %d)", + mcmd, mcmd->state, mcmd->fn, + mcmd->cmd_finish_wait_count, mcmd->cmd_done_wait_count); + spin_unlock_irq(&scst_mcmd_lock); + sBUG(); + goto out; } - mcmd->completed = 1; - -out_unlock: spin_unlock_irq(&scst_mcmd_lock); + +out: return res; } @@ -4528,14 +4826,24 @@ static void __scst_abort_task_set(struct scst_mgmt_cmd *mcmd, { struct scst_cmd *cmd; struct scst_session *sess = tgt_dev->sess; + bool other_ini; TRACE_ENTRY(); + if ((mcmd->fn == SCST_PR_ABORT_ALL) && + (mcmd->origin_pr_cmd->sess != sess)) + other_ini = true; + else + other_ini = false; + spin_lock_irq(&sess->sess_list_lock); TRACE_DBG("Searching in sess cmd list (sess=%p)", sess); list_for_each_entry(cmd, &sess->sess_cmd_list, sess_cmd_list_entry) { + if ((mcmd->fn == SCST_PR_ABORT_ALL) && + (mcmd->origin_pr_cmd == cmd)) + continue; if ((cmd->tgt_dev == tgt_dev) || ((cmd->tgt_dev == NULL) && (cmd->lun == tgt_dev->lun))) { @@ -4545,7 +4853,7 @@ static void __scst_abort_task_set(struct scst_mgmt_cmd *mcmd, (mcmd->cmd_sn == cmd->tgt_sn)) continue; } - scst_abort_cmd(cmd, mcmd, 0, 0); + scst_abort_cmd(cmd, mcmd, other_ini, 0); } } spin_unlock_irq(&sess->sess_list_lock); @@ -4565,7 +4873,10 @@ static int scst_abort_task_set(struct scst_mgmt_cmd *mcmd) __scst_abort_task_set(mcmd, tgt_dev); - tm_dbg_task_mgmt(mcmd->mcmd_tgt_dev->dev, "ABORT TASK SET", 0); + if (atomic_dec_and_test(&mcmd->origin_pr_cmd->pr_abort_counter->pr_aborting_cnt)) + complete_all(&mcmd->origin_pr_cmd->pr_abort_counter->pr_aborting_cmpl); + + tm_dbg_task_mgmt(mcmd->mcmd_tgt_dev->dev, "ABORT TASK SET/PR ABORT", 0); scst_unblock_aborted_cmds(0); @@ -4712,38 +5023,16 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd) "for tag %llu not found", (long long unsigned int)mcmd->tag); mcmd->status = SCST_MGMT_STATUS_TASK_NOT_EXIST; - mcmd->state = SCST_MCMD_STATE_DONE; spin_unlock_irq(&sess->sess_list_lock); + res = scst_set_mcmd_next_state(mcmd); goto out; } __scst_cmd_get(cmd); spin_unlock_irq(&sess->sess_list_lock); - TRACE_MGMT_DBG("Cmd %p for tag %llu (sn %d, set %d, " - "queue_type %x) found, aborting it", - cmd, (long long unsigned int)mcmd->tag, - cmd->sn, cmd->sn_set, cmd->queue_type); + TRACE_DBG("Cmd to abort %p for tag %llu found", + cmd, (long long unsigned int)mcmd->tag); mcmd->cmd_to_abort = cmd; - if (mcmd->lun_set && (mcmd->lun != cmd->lun)) { - PRINT_ERROR("ABORT TASK: LUN mismatch: mcmd LUN %llx, " - "cmd LUN %llx, cmd tag %llu", - (long long unsigned int)mcmd->lun, - (long long unsigned int)cmd->lun, - (long long unsigned int)mcmd->tag); - mcmd->status = SCST_MGMT_STATUS_REJECTED; - } else if (mcmd->cmd_sn_set && - (scst_sn_before(mcmd->cmd_sn, cmd->tgt_sn) || - (mcmd->cmd_sn == cmd->tgt_sn))) { - PRINT_ERROR("ABORT TASK: SN mismatch: mcmd SN %x, " - "cmd SN %x, cmd tag %llu", mcmd->cmd_sn, - cmd->tgt_sn, (long long unsigned int)mcmd->tag); - mcmd->status = SCST_MGMT_STATUS_REJECTED; - } else { - scst_abort_cmd(cmd, mcmd, 0, 1); - scst_unblock_aborted_cmds(0); - } - res = scst_set_mcmd_next_state(mcmd); - mcmd->cmd_to_abort = NULL; /* just in case */ - __scst_cmd_put(cmd); + mcmd->state = SCST_MCMD_STATE_EXEC; break; } @@ -4753,21 +5042,22 @@ static int scst_mgmt_cmd_init(struct scst_mgmt_cmd *mcmd) case SCST_NEXUS_LOSS: case SCST_ABORT_ALL_TASKS: case SCST_UNREG_SESS_TM: - mcmd->state = SCST_MCMD_STATE_READY; + mcmd->state = SCST_MCMD_STATE_EXEC; break; case SCST_ABORT_TASK_SET: case SCST_CLEAR_ACA: case SCST_CLEAR_TASK_SET: case SCST_LUN_RESET: + case SCST_PR_ABORT_ALL: rc = scst_mgmt_translate_lun(mcmd); if (rc == 0) - mcmd->state = SCST_MCMD_STATE_READY; + mcmd->state = SCST_MCMD_STATE_EXEC; else if (rc < 0) { PRINT_ERROR("Corresponding device for LUN %lld not " "found", (long long unsigned int)mcmd->lun); mcmd->status = SCST_MGMT_STATUS_LUN_NOT_EXIST; - mcmd->state = SCST_MCMD_STATE_DONE; + res = scst_set_mcmd_next_state(mcmd); } else res = rc; break; @@ -5103,6 +5393,46 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd, return res; } +static int scst_abort_task(struct scst_mgmt_cmd *mcmd) +{ + int res; + struct scst_cmd *cmd = mcmd->cmd_to_abort; + + TRACE_ENTRY(); + + TRACE_MGMT_DBG("Abortind task (cmd %p, sn %d, set %d, tag %llu, " + "queue_type %x)", cmd, cmd->sn, cmd->sn_set, + (long long unsigned int)mcmd->tag, cmd->queue_type); + + if (mcmd->lun_set && (mcmd->lun != cmd->lun)) { + PRINT_ERROR("ABORT TASK: LUN mismatch: mcmd LUN %llx, " + "cmd LUN %llx, cmd tag %llu", + (long long unsigned int)mcmd->lun, + (long long unsigned int)cmd->lun, + (long long unsigned int)mcmd->tag); + mcmd->status = SCST_MGMT_STATUS_REJECTED; + } else if (mcmd->cmd_sn_set && + (scst_sn_before(mcmd->cmd_sn, cmd->tgt_sn) || + (mcmd->cmd_sn == cmd->tgt_sn))) { + PRINT_ERROR("ABORT TASK: SN mismatch: mcmd SN %x, " + "cmd SN %x, cmd tag %llu", mcmd->cmd_sn, + cmd->tgt_sn, (long long unsigned int)mcmd->tag); + mcmd->status = SCST_MGMT_STATUS_REJECTED; + } else { + scst_abort_cmd(cmd, mcmd, 0, 1); + scst_unblock_aborted_cmds(0); + } + + res = scst_set_mcmd_next_state(mcmd); + + mcmd->cmd_to_abort = NULL; /* just in case */ + + __scst_cmd_put(cmd); + + TRACE_EXIT_RES(res); + return res; +} + /* Returns 0 if the command processing should be continued, <0 otherwise */ static int scst_mgmt_cmd_exec(struct scst_mgmt_cmd *mcmd) { @@ -5113,7 +5443,12 @@ static int scst_mgmt_cmd_exec(struct scst_mgmt_cmd *mcmd) mcmd->status = SCST_MGMT_STATUS_SUCCESS; switch (mcmd->fn) { + case SCST_ABORT_TASK: + res = scst_abort_task(mcmd); + break; + case SCST_ABORT_TASK_SET: + case SCST_PR_ABORT_ALL: res = scst_abort_task_set(mcmd); break; @@ -5169,7 +5504,7 @@ out: return res; out_done: - mcmd->state = SCST_MCMD_STATE_DONE; + res = scst_set_mcmd_next_state(mcmd); goto out; } @@ -5178,7 +5513,8 @@ static void scst_call_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd) struct scst_session *sess = mcmd->sess; if ((sess->tgt->tgtt->task_mgmt_affected_cmds_done != NULL) && - (mcmd->fn != SCST_UNREG_SESS_TM)) { + (mcmd->fn != SCST_UNREG_SESS_TM) && + (mcmd->fn != SCST_PR_ABORT_ALL)) { TRACE_DBG("Calling target %s task_mgmt_affected_cmds_done(%p)", sess->tgt->tgtt->name, sess); sess->tgt->tgtt->task_mgmt_affected_cmds_done(mcmd); @@ -5211,8 +5547,6 @@ static int scst_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd) scst_call_task_mgmt_affected_cmds_done(mcmd); - mcmd->affected_cmds_done_called = 1; - res = scst_set_mcmd_next_state(mcmd); TRACE_EXIT_RES(res); @@ -5233,13 +5567,12 @@ static void scst_mgmt_cmd_send_done(struct scst_mgmt_cmd *mcmd) TRACE(TRACE_MINOR_AND_MGMT_DBG, "TM command fn %d finished, " "status %x", mcmd->fn, mcmd->status); - if (!mcmd->affected_cmds_done_called) { - /* It might happen in case of errors */ - scst_call_task_mgmt_affected_cmds_done(mcmd); - } - - if ((sess->tgt->tgtt->task_mgmt_fn_done != NULL) && - (mcmd->fn != SCST_UNREG_SESS_TM)) { + if (mcmd->fn == SCST_PR_ABORT_ALL) { + mcmd->origin_pr_cmd->scst_cmd_done(mcmd->origin_pr_cmd, + SCST_CMD_STATE_DEFAULT, + SCST_CONTEXT_THREAD); + } else if ((sess->tgt->tgtt->task_mgmt_fn_done != NULL) && + (mcmd->fn != SCST_UNREG_SESS_TM)) { TRACE_DBG("Calling target %s task_mgmt_fn_done(%p)", sess->tgt->tgtt->name, sess); sess->tgt->tgtt->task_mgmt_fn_done(mcmd); @@ -5288,6 +5621,11 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd) TRACE_ENTRY(); + /* + * We are in the TM thread and mcmd->state guaranteed to not be + * changed behind us. + */ + TRACE_DBG("mcmd %p, state %d", mcmd, mcmd->state); while (1) { @@ -5298,12 +5636,12 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd) goto out; break; - case SCST_MCMD_STATE_READY: + case SCST_MCMD_STATE_EXEC: if (scst_mgmt_cmd_exec(mcmd)) goto out; break; - case SCST_MCMD_STATE_POST_AFFECTED_CMDS_DONE: + case SCST_MCMD_STATE_AFFECTED_CMDS_DONE: if (scst_mgmt_affected_cmds_done(mcmd)) goto out; break; @@ -5312,19 +5650,20 @@ static int scst_process_mgmt_cmd(struct scst_mgmt_cmd *mcmd) scst_mgmt_cmd_send_done(mcmd); break; - default: - PRINT_ERROR("Unknown state %d of management command", - mcmd->state); - res = -1; - /* go through */ case SCST_MCMD_STATE_FINISHED: scst_free_mgmt_cmd(mcmd); + /* mcmd is dead */ goto out; -#ifdef CONFIG_SCST_EXTRACHECKS - case SCST_MCMD_STATE_EXECUTING: + default: + PRINT_CRIT_ERROR("Wrong mcmd %p state %d (fn %d, " + "cmd_finish_wait_count %d, cmd_done_wait_count " + "%d)", mcmd, mcmd->state, mcmd->fn, + mcmd->cmd_finish_wait_count, + mcmd->cmd_done_wait_count); sBUG(); -#endif + res = -1; + goto out; } } @@ -5439,6 +5778,11 @@ static struct scst_mgmt_cmd *scst_pre_rx_mgmt_cmd(struct scst_session mcmd->state = SCST_MCMD_STATE_INIT; mcmd->tgt_priv = tgt_priv; + if (fn == SCST_PR_ABORT_ALL) { + atomic_inc(&mcmd->origin_pr_cmd->pr_abort_counter->pr_abort_pending_cnt); + atomic_inc(&mcmd->origin_pr_cmd->pr_abort_counter->pr_aborting_cnt); + } + out: TRACE_EXIT(); return mcmd; @@ -5822,13 +6166,29 @@ static int scst_init_session(struct scst_session *sess) TRACE_DBG("Adding sess %p to tgt->sess_list", sess); list_add_tail(&sess->sess_list_entry, &sess->tgt->sess_list); + if (sess->tgt->tgtt->get_initiator_port_transport_id != NULL) { + res = sess->tgt->tgtt->get_initiator_port_transport_id(sess, + &sess->transport_id); + if (res != 0) { + PRINT_ERROR("Unable to make initiator %s port " + "transport id", sess->initiator_name); + goto failed; + } + TRACE_PR("sess %p (ini %s), transport id %s/%d", sess, + sess->initiator_name, + debug_transport_id_to_initiator_name( + sess->transport_id), sess->tgt->rel_tgt_id); + } + res = scst_sess_alloc_tgt_devs(sess); /* Let's always create session's sysfs to simplify error recovery */ + rc = scst_create_sess_sysfs(sess); if (res == 0) res = rc; +failed: mutex_unlock(&scst_mutex); if (sess->init_result_fn) { @@ -5926,7 +6286,7 @@ restart: * inside result_fn(), it will NOT be called automatically. */ struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic, - const char *initiator_name, void *data, + const char *initiator_name, void *tgt_priv, void *result_fn_data, void (*result_fn) (struct scst_session *sess, void *data, int result)) { struct scst_session *sess; @@ -5940,11 +6300,13 @@ struct scst_session *scst_register_session(struct scst_tgt *tgt, int atomic, if (sess == NULL) goto out; + scst_sess_set_tgt_priv(sess, tgt_priv); + scst_sess_get(sess); /* one for registered session */ scst_sess_get(sess); /* one held until sess is inited */ if (atomic) { - sess->reg_sess_data = data; + sess->reg_sess_data = result_fn_data; sess->init_result_fn = result_fn; spin_lock_irqsave(&scst_mgmt_lock, flags); TRACE_DBG("Adding sess %p to scst_sess_init_list", sess); diff --git a/scst_local/scst_local.c b/scst_local/scst_local.c index 795610f45..ba508751f 100644 --- a/scst_local/scst_local.c +++ b/scst_local/scst_local.c @@ -644,7 +644,7 @@ static void scst_local_release_adapter(struct device *dev) if (scst_lcl_host->session[i]) scst_unregister_session( scst_lcl_host->session[i], TRUE, NULL); - scst_unregister(scst_lcl_host->target); + scst_unregister_target(scst_lcl_host->target); kfree(scst_lcl_host); } @@ -683,7 +683,8 @@ static int scst_local_add_adapter(void) * Register a target with SCST and add a session */ sprintf(name, "scstlcltgt%d", scst_local_add_host); - scst_lcl_host->target = scst_register(&scst_local_targ_tmpl, name); + scst_lcl_host->target = scst_register_target(&scst_local_targ_tmpl, + name); if (!scst_lcl_host) { printk(KERN_WARNING "scst_register_target failed:\n"); error = -1; @@ -697,7 +698,7 @@ static int scst_local_add_adapter(void) sprintf(name, "scstlclhst%d:%d", scst_local_add_host, i); scst_lcl_host->session[i] = scst_register_session( scst_lcl_host->target, - 0, name, NULL, NULL); + 0, name, NULL, NULL, NULL); if (!scst_lcl_host->session[i]) { printk(KERN_WARNING "scst_register_session failed:\n"); error = -1; @@ -732,7 +733,7 @@ unregister_session: TRUE, NULL); } unregister_target: - scst_unregister(scst_lcl_host->target); + scst_unregister_target(scst_lcl_host->target); cleanup: kfree(scst_lcl_host); TRACE_EXIT(); diff --git a/srpt/src/ib_srpt.c b/srpt/src/ib_srpt.c index ee3370702..d6109d72d 100644 --- a/srpt/src/ib_srpt.c +++ b/srpt/src/ib_srpt.c @@ -2137,7 +2137,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, BUG_ON(!sdev->scst_tgt); ch->scst_sess = scst_register_session(sdev->scst_tgt, 0, ch->sess_name, - NULL, NULL); + ch, NULL, NULL); if (!ch->scst_sess) { rej->reason = cpu_to_be32(SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); TRACE_DBG("%s", "Failed to create scst sess"); @@ -2147,8 +2147,6 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, TRACE_DBG("Establish connection sess=%p name=%s cm_id=%p", ch->scst_sess, ch->sess_name, ch->cm_id); - scst_sess_set_tgt_priv(ch->scst_sess, ch); - /* create srp_login_response */ rsp->opcode = SRP_LOGIN_RSP; rsp->tag = req->tag; @@ -2775,11 +2773,7 @@ static int srpt_xmit_response(struct scst_cmd *scmnd) int resp_len; enum srpt_command_state prev_state; - if (unlikely(scst_cmd_atomic(scmnd))) { - TRACE_DBG("%s", "Switching to thread context."); - ret = SCST_TGT_RES_NEED_THREAD_CTX; - goto out; - } + EXTRACHECKS_BUG_ON(scst_cmd_atomic(scst_cmd)); ioctx = scst_cmd_get_tgt_priv(scmnd); BUG_ON(!ioctx); @@ -2971,8 +2965,8 @@ static int srpt_detect(struct scst_tgt_template *tp) /** * srpt_release() - SCST release callback function. * - * Callback function called by the SCST core from scst_unregister() to free up - * the resources associated with device scst_tgt. + * Callback function called by the SCST core from scst_unregister_target() to + * free up the resources associated with device scst_tgt. */ static int srpt_release(struct scst_tgt *scst_tgt) { @@ -3157,7 +3151,7 @@ static void srpt_add_one(struct ib_device *device) if (!sdev) return; - sdev->scst_tgt = scst_register(&srpt_template, NULL); + sdev->scst_tgt = scst_register_target(&srpt_template, NULL); if (!sdev->scst_tgt) { PRINT_ERROR("SCST registration failed for %s.", sdev->device->name); @@ -3306,7 +3300,7 @@ err_dev: device_unregister(&sdev->dev); #endif unregister_tgt: - scst_unregister(sdev->scst_tgt); + scst_unregister_target(sdev->scst_tgt); free_dev: kfree(sdev); @@ -3366,7 +3360,7 @@ static void srpt_remove_one(struct ib_device *device) * such that no new SRP_LOGIN_REQ information units can arrive while * destroying the SCST target. */ - scst_unregister(sdev->scst_tgt); + scst_unregister_target(sdev->scst_tgt); sdev->scst_tgt = NULL; srpt_free_ioctx_ring(sdev, sdev->ioctx_ring, diff --git a/usr/fileio/common.c b/usr/fileio/common.c index 316cf8102..4817cb205 100644 --- a/usr/fileio/common.c +++ b/usr/fileio/common.c @@ -646,6 +646,7 @@ static int do_tm(struct vdisk_cmd *vcmd) { struct scst_user_get_cmd *cmd = vcmd->cmd; struct scst_user_reply_cmd *reply = vcmd->reply; + struct vdisk_dev *dev = vcmd->dev; int res = 0; TRACE_ENTRY(); @@ -659,6 +660,16 @@ static int do_tm(struct vdisk_cmd *vcmd) cmd->tm_cmd.fn, cmd->tm_cmd.sess_h, cmd->tm_cmd.cmd_h_to_abort); + switch (cmd->tm_cmd.fn) { + case SCST_LUN_RESET: + case SCST_TARGET_RESET: + case SCST_PR_ABORT_ALL: + pthread_mutex_lock(&dev->dev_mutex); + dev->prevent_allow_medium_removal = 0; + pthread_mutex_unlock(&dev->dev_mutex); + break; + } + memset(reply, 0, sizeof(*reply)); reply->cmd_h = cmd->cmd_h; reply->subcode = cmd->subcode; @@ -1618,15 +1629,7 @@ static void exec_prevent_allow_medium_removal(struct vdisk_cmd *vcmd) TRACE_DBG("PERSIST/PREVENT 0x%02x", cmd->cdb[4]); pthread_mutex_lock(&dev->dev_mutex); - if (dev->type == TYPE_ROM) - dev->prevent_allow_medium_removal = - cmd->cdb[4] & 0x01 ? 1 : 0; - else { - PRINT_ERROR("%s", "Prevent allow medium removal for " - "non-CDROM device"); - set_cmd_error(vcmd, - SCST_LOAD_SENSE(scst_sense_invalid_opcode)); - } + dev->prevent_allow_medium_removal = cmd->cdb[4] & 0x01 ? 1 : 0; pthread_mutex_unlock(&dev->dev_mutex); return;