diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-24 10:22:09 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-24 10:22:09 -0700 |
commit | c6668726d2c2c581e6c417448c472c994d026f5f (patch) | |
tree | ccbc5a73b9dfe09a065cb5d8627aa2297e730ec0 /drivers/target | |
parent | 06b45f2aa703837163496f5db6a53575665cc6b4 (diff) | |
parent | 68d4cef3bab3fb9bb0dbac690ba35a96cb5a16d9 (diff) | |
download | linux-c6668726d2c2c581e6c417448c472c994d026f5f.tar.gz linux-c6668726d2c2c581e6c417448c472c994d026f5f.tar.bz2 linux-c6668726d2c2c581e6c417448c472c994d026f5f.zip |
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending
Pull SCSI target updates from Nicholas Bellinger:
"Lots of activity in target land the last months.
The highlights include:
- Convert fabric drivers tree-wide to target_register_template() (hch
+ bart)
- iser-target hardening fixes + v1.0 improvements (sagi)
- Convert iscsi_thread_set usage to kthread.h + kill
iscsi_target_tq.c (sagi + nab)
- Add support for T10-PI WRITE_STRIP + READ_INSERT operation (mkp +
sagi + nab)
- DIF fixes for CONFIG_DEBUG_SG=y + UNMAP file emulation (akinobu +
sagi + mkp)
- Extended TCMU ABI v2 for future BIDI + DIF support (andy + ilias)
- Fix COMPARE_AND_WRITE handling for NO_ALLLOC drivers (hch + nab)
Thanks to everyone who contributed this round with new features,
bug-reports, fixes, cleanups and improvements.
Looking forward, it's currently shaping up to be a busy v4.2 as well"
* 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending: (69 commits)
target: Put TCMU under a new config option
target: Version 2 of TCMU ABI
target: fix tcm_mod_builder.py
target/file: Fix UNMAP with DIF protection support
target/file: Fix SG table for prot_buf initialization
target/file: Fix BUG() when CONFIG_DEBUG_SG=y and DIF protection enabled
target: Make core_tmr_abort_task() skip TMFs
target/sbc: Update sbc_dif_generate pr_debug output
target/sbc: Make internal DIF emulation honor ->prot_checks
target/sbc: Return INVALID_CDB_FIELD if DIF + sess_prot_type disabled
target: Ensure sess_prot_type is saved across session restart
target/rd: Don't pass incomplete scatterlist entries to sbc_dif_verify_*
target: Remove the unused flag SCF_ACK_KREF
target: Fix two sparse warnings
target: Fix COMPARE_AND_WRITE with SG_TO_MEM_NOALLOC handling
target: simplify the target template registration API
target: simplify target_xcopy_init_pt_lun
target: remove the unused SCF_CMD_XCOPY_PASSTHROUGH flag
target/rd: reduce code duplication in rd_execute_rw()
tcm_loop: fixup tpgt string to integer conversion
...
Diffstat (limited to 'drivers/target')
33 files changed, 990 insertions, 1524 deletions
diff --git a/drivers/target/Kconfig b/drivers/target/Kconfig index 81d44c477a5b..257361280510 100644 --- a/drivers/target/Kconfig +++ b/drivers/target/Kconfig @@ -31,12 +31,13 @@ config TCM_PSCSI Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered passthrough access to Linux/SCSI device -config TCM_USER +config TCM_USER2 tristate "TCM/USER Subsystem Plugin for Linux" depends on UIO && NET help Say Y here to enable the TCM/USER subsystem plugin for a userspace - process to handle requests + process to handle requests. This is version 2 of the ABI; version 1 + is obsolete. source "drivers/target/loopback/Kconfig" source "drivers/target/tcm_fc/Kconfig" diff --git a/drivers/target/Makefile b/drivers/target/Makefile index bbb4a7d638ef..e619c0266a79 100644 --- a/drivers/target/Makefile +++ b/drivers/target/Makefile @@ -22,7 +22,7 @@ obj-$(CONFIG_TARGET_CORE) += target_core_mod.o obj-$(CONFIG_TCM_IBLOCK) += target_core_iblock.o obj-$(CONFIG_TCM_FILEIO) += target_core_file.o obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o -obj-$(CONFIG_TCM_USER) += target_core_user.o +obj-$(CONFIG_TCM_USER2) += target_core_user.o # Fabric modules obj-$(CONFIG_LOOPBACK_TARGET) += loopback/ diff --git a/drivers/target/iscsi/Makefile b/drivers/target/iscsi/Makefile index 13a92403fe3e..0f43be9c3453 100644 --- a/drivers/target/iscsi/Makefile +++ b/drivers/target/iscsi/Makefile @@ -1,6 +1,5 @@ iscsi_target_mod-y += iscsi_target_parameters.o \ iscsi_target_seq_pdu_list.o \ - iscsi_target_tq.o \ iscsi_target_auth.o \ iscsi_target_datain_values.o \ iscsi_target_device.o \ diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 77d64251af40..34871a628b11 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -33,8 +33,6 @@ #include <target/iscsi/iscsi_target_core.h> #include "iscsi_target_parameters.h" #include "iscsi_target_seq_pdu_list.h" -#include "iscsi_target_tq.h" -#include "iscsi_target_configfs.h" #include "iscsi_target_datain_values.h" #include "iscsi_target_erl0.h" #include "iscsi_target_erl1.h" @@ -537,7 +535,7 @@ static struct iscsit_transport iscsi_target_transport = { static int __init iscsi_target_init_module(void) { - int ret = 0; + int ret = 0, size; pr_debug("iSCSI-Target "ISCSIT_VERSION"\n"); @@ -546,24 +544,21 @@ static int __init iscsi_target_init_module(void) pr_err("Unable to allocate memory for iscsit_global\n"); return -1; } + spin_lock_init(&iscsit_global->ts_bitmap_lock); mutex_init(&auth_id_lock); spin_lock_init(&sess_idr_lock); idr_init(&tiqn_idr); idr_init(&sess_idr); - ret = iscsi_target_register_configfs(); - if (ret < 0) + ret = target_register_template(&iscsi_ops); + if (ret) goto out; - ret = iscsi_thread_set_init(); - if (ret < 0) + size = BITS_TO_LONGS(ISCSIT_BITMAP_BITS) * sizeof(long); + iscsit_global->ts_bitmap = vzalloc(size); + if (!iscsit_global->ts_bitmap) { + pr_err("Unable to allocate iscsit_global->ts_bitmap\n"); goto configfs_out; - - if (iscsi_allocate_thread_sets(TARGET_THREAD_SET_COUNT) != - TARGET_THREAD_SET_COUNT) { - pr_err("iscsi_allocate_thread_sets() returned" - " unexpected value!\n"); - goto ts_out1; } lio_qr_cache = kmem_cache_create("lio_qr_cache", @@ -572,7 +567,7 @@ static int __init iscsi_target_init_module(void) if (!lio_qr_cache) { pr_err("nable to kmem_cache_create() for" " lio_qr_cache\n"); - goto ts_out2; + goto bitmap_out; } lio_dr_cache = kmem_cache_create("lio_dr_cache", @@ -617,12 +612,13 @@ dr_out: kmem_cache_destroy(lio_dr_cache); qr_out: kmem_cache_destroy(lio_qr_cache); -ts_out2: - iscsi_deallocate_thread_sets(); -ts_out1: - iscsi_thread_set_free(); +bitmap_out: + vfree(iscsit_global->ts_bitmap); configfs_out: - iscsi_target_deregister_configfs(); + /* XXX: this probably wants it to be it's own unwind step.. */ + if (iscsit_global->discovery_tpg) + iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1); + target_unregister_template(&iscsi_ops); out: kfree(iscsit_global); return -ENOMEM; @@ -630,8 +626,6 @@ out: static void __exit iscsi_target_cleanup_module(void) { - iscsi_deallocate_thread_sets(); - iscsi_thread_set_free(); iscsit_release_discovery_tpg(); iscsit_unregister_transport(&iscsi_target_transport); kmem_cache_destroy(lio_qr_cache); @@ -639,8 +633,15 @@ static void __exit iscsi_target_cleanup_module(void) kmem_cache_destroy(lio_ooo_cache); kmem_cache_destroy(lio_r2t_cache); - iscsi_target_deregister_configfs(); + /* + * Shutdown discovery sessions and disable discovery TPG + */ + if (iscsit_global->discovery_tpg) + iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1); + target_unregister_template(&iscsi_ops); + + vfree(iscsit_global->ts_bitmap); kfree(iscsit_global); } @@ -990,7 +991,7 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, /* * Initialize struct se_cmd descriptor from target_core_mod infrastructure */ - transport_init_se_cmd(&cmd->se_cmd, &lio_target_fabric_configfs->tf_ops, + transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops, conn->sess->se_sess, be32_to_cpu(hdr->data_length), cmd->data_direction, sam_task_attr, cmd->sense_buffer + 2); @@ -1805,8 +1806,7 @@ iscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, u8 tcm_function; int ret; - transport_init_se_cmd(&cmd->se_cmd, - &lio_target_fabric_configfs->tf_ops, + transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops, conn->sess->se_sess, 0, DMA_NONE, TCM_SIMPLE_TAG, cmd->sense_buffer + 2); @@ -2155,7 +2155,6 @@ reject: cmd->text_in_ptr = NULL; return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf); } -EXPORT_SYMBOL(iscsit_handle_text_cmd); int iscsit_logout_closesession(struct iscsi_cmd *cmd, struct iscsi_conn *conn) { @@ -3715,17 +3714,16 @@ static int iscsit_send_reject( void iscsit_thread_get_cpumask(struct iscsi_conn *conn) { - struct iscsi_thread_set *ts = conn->thread_set; int ord, cpu; /* - * thread_id is assigned from iscsit_global->ts_bitmap from - * within iscsi_thread_set.c:iscsi_allocate_thread_sets() + * bitmap_id is assigned from iscsit_global->ts_bitmap from + * within iscsit_start_kthreads() * - * Here we use thread_id to determine which CPU that this - * iSCSI connection's iscsi_thread_set will be scheduled to + * Here we use bitmap_id to determine which CPU that this + * iSCSI connection's RX/TX threads will be scheduled to * execute upon. */ - ord = ts->thread_id % cpumask_weight(cpu_online_mask); + ord = conn->bitmap_id % cpumask_weight(cpu_online_mask); for_each_online_cpu(cpu) { if (ord-- == 0) { cpumask_set_cpu(cpu, conn->conn_cpumask); @@ -3914,7 +3912,7 @@ check_rsp_state: switch (state) { case ISTATE_SEND_LOGOUTRSP: if (!iscsit_logout_post_handler(cmd, conn)) - goto restart; + return -ECONNRESET; /* fall through */ case ISTATE_SEND_STATUS: case ISTATE_SEND_ASYNCMSG: @@ -3942,8 +3940,6 @@ check_rsp_state: err: return -1; -restart: - return -EAGAIN; } static int iscsit_handle_response_queue(struct iscsi_conn *conn) @@ -3970,21 +3966,13 @@ static int iscsit_handle_response_queue(struct iscsi_conn *conn) int iscsi_target_tx_thread(void *arg) { int ret = 0; - struct iscsi_conn *conn; - struct iscsi_thread_set *ts = arg; + struct iscsi_conn *conn = arg; /* * Allow ourselves to be interrupted by SIGINT so that a * connection recovery / failure event can be triggered externally. */ allow_signal(SIGINT); -restart: - conn = iscsi_tx_thread_pre_handler(ts); - if (!conn) - goto out; - - ret = 0; - while (!kthread_should_stop()) { /* * Ensure that both TX and RX per connection kthreads @@ -3993,11 +3981,9 @@ restart: iscsit_thread_check_cpumask(conn, current, 1); wait_event_interruptible(conn->queues_wq, - !iscsit_conn_all_queues_empty(conn) || - ts->status == ISCSI_THREAD_SET_RESET); + !iscsit_conn_all_queues_empty(conn)); - if ((ts->status == ISCSI_THREAD_SET_RESET) || - signal_pending(current)) + if (signal_pending(current)) goto transport_err; get_immediate: @@ -4008,15 +3994,14 @@ get_immediate: ret = iscsit_handle_response_queue(conn); if (ret == 1) goto get_immediate; - else if (ret == -EAGAIN) - goto restart; + else if (ret == -ECONNRESET) + goto out; else if (ret < 0) goto transport_err; } transport_err: iscsit_take_action_for_connection_exit(conn); - goto restart; out: return 0; } @@ -4111,8 +4096,7 @@ int iscsi_target_rx_thread(void *arg) int ret; u8 buffer[ISCSI_HDR_LEN], opcode; u32 checksum = 0, digest = 0; - struct iscsi_conn *conn = NULL; - struct iscsi_thread_set *ts = arg; + struct iscsi_conn *conn = arg; struct kvec iov; /* * Allow ourselves to be interrupted by SIGINT so that a @@ -4120,11 +4104,6 @@ int iscsi_target_rx_thread(void *arg) */ allow_signal(SIGINT); -restart: - conn = iscsi_rx_thread_pre_handler(ts); - if (!conn) - goto out; - if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) { struct completion comp; int rc; @@ -4134,7 +4113,7 @@ restart: if (rc < 0) goto transport_err; - goto out; + goto transport_err; } while (!kthread_should_stop()) { @@ -4210,8 +4189,6 @@ transport_err: if (!signal_pending(current)) atomic_set(&conn->transport_failed, 1); iscsit_take_action_for_connection_exit(conn); - goto restart; -out: return 0; } @@ -4273,7 +4250,24 @@ int iscsit_close_connection( if (conn->conn_transport->transport_type == ISCSI_TCP) complete(&conn->conn_logout_comp); - iscsi_release_thread_set(conn); + if (!strcmp(current->comm, ISCSI_RX_THREAD_NAME)) { + if (conn->tx_thread && + cmpxchg(&conn->tx_thread_active, true, false)) { + send_sig(SIGINT, conn->tx_thread, 1); + kthread_stop(conn->tx_thread); + } + } else if (!strcmp(current->comm, ISCSI_TX_THREAD_NAME)) { + if (conn->rx_thread && + cmpxchg(&conn->rx_thread_active, true, false)) { + send_sig(SIGINT, conn->rx_thread, 1); + kthread_stop(conn->rx_thread); + } + } + + spin_lock(&iscsit_global->ts_bitmap_lock); + bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id, + get_order(1)); + spin_unlock(&iscsit_global->ts_bitmap_lock); iscsit_stop_timers_for_cmds(conn); iscsit_stop_nopin_response_timer(conn); @@ -4383,8 +4377,6 @@ int iscsit_close_connection( iscsit_put_transport(conn->conn_transport); - conn->thread_set = NULL; - pr_debug("Moving to TARG_CONN_STATE_FREE.\n"); conn->conn_state = TARG_CONN_STATE_FREE; kfree(conn); @@ -4551,15 +4543,13 @@ static void iscsit_logout_post_handler_closesession( struct iscsi_conn *conn) { struct iscsi_session *sess = conn->sess; - - iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD); - iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD); + int sleep = cmpxchg(&conn->tx_thread_active, true, false); atomic_set(&conn->conn_logout_remove, 0); complete(&conn->conn_logout_comp); iscsit_dec_conn_usage_count(conn); - iscsit_stop_session(sess, 1, 1); + iscsit_stop_session(sess, sleep, sleep); iscsit_dec_session_usage_count(sess); target_put_session(sess->se_sess); } @@ -4567,13 +4557,12 @@ static void iscsit_logout_post_handler_closesession( static void iscsit_logout_post_handler_samecid( struct iscsi_conn *conn) { - iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD); - iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD); + int sleep = cmpxchg(&conn->tx_thread_active, true, false); atomic_set(&conn->conn_logout_remove, 0); complete(&conn->conn_logout_comp); - iscsit_cause_connection_reinstatement(conn, 1); + iscsit_cause_connection_reinstatement(conn, sleep); iscsit_dec_conn_usage_count(conn); } diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h index e936d56fb523..7d0f9c00d9c2 100644 --- a/drivers/target/iscsi/iscsi_target.h +++ b/drivers/target/iscsi/iscsi_target.h @@ -35,7 +35,7 @@ extern void iscsit_stop_session(struct iscsi_session *, int, int); extern int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *, int); extern struct iscsit_global *iscsit_global; -extern struct target_fabric_configfs *lio_target_fabric_configfs; +extern const struct target_core_fabric_ops iscsi_ops; extern struct kmem_cache *lio_dr_cache; extern struct kmem_cache *lio_ooo_cache; diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 48384b675e62..469fce44ebad 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -37,9 +37,6 @@ #include "iscsi_target_util.h" #include "iscsi_target.h" #include <target/iscsi/iscsi_target_stat.h> -#include "iscsi_target_configfs.h" - -struct target_fabric_configfs *lio_target_fabric_configfs; struct lio_target_configfs_attribute { struct configfs_attribute attr; @@ -1052,6 +1049,11 @@ TPG_ATTR(default_erl, S_IRUGO | S_IWUSR); */ DEF_TPG_ATTRIB(t10_pi); TPG_ATTR(t10_pi, S_IRUGO | S_IWUSR); +/* + * Define iscsi_tpg_attrib_s_fabric_prot_type + */ +DEF_TPG_ATTRIB(fabric_prot_type); +TPG_ATTR(fabric_prot_type, S_IRUGO | S_IWUSR); static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_authentication.attr, @@ -1065,6 +1067,7 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_demo_mode_discovery.attr, &iscsi_tpg_attrib_default_erl.attr, &iscsi_tpg_attrib_t10_pi.attr, + &iscsi_tpg_attrib_fabric_prot_type.attr, NULL, }; @@ -1410,8 +1413,18 @@ out: TF_TPG_BASE_ATTR(lio_target, enable, S_IRUGO | S_IWUSR); +static ssize_t lio_target_tpg_show_dynamic_sessions( + struct se_portal_group *se_tpg, + char *page) +{ + return target_show_dynamic_sessions(se_tpg, page); +} + +TF_TPG_BASE_ATTR_RO(lio_target, dynamic_sessions); + static struct configfs_attribute *lio_target_tpg_attrs[] = { &lio_target_tpg_enable.attr, + &lio_target_tpg_dynamic_sessions.attr, NULL, }; @@ -1450,10 +1463,8 @@ static struct se_portal_group *lio_target_tiqn_addtpg( if (!tpg) return NULL; - ret = core_tpg_register( - &lio_target_fabric_configfs->tf_ops, - wwn, &tpg->tpg_se_tpg, tpg, - TRANSPORT_TPG_TYPE_NORMAL); + ret = core_tpg_register(&iscsi_ops, wwn, &tpg->tpg_se_tpg, + tpg, TRANSPORT_TPG_TYPE_NORMAL); if (ret < 0) return NULL; @@ -1872,6 +1883,20 @@ static int lio_tpg_check_prod_mode_write_protect( return tpg->tpg_attrib.prod_mode_write_protect; } +static int lio_tpg_check_prot_fabric_only( + struct se_portal_group *se_tpg) +{ + struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; + /* + * Only report fabric_prot_type if t10_pi has also been enabled + * for incoming ib_isert sessions. + */ + if (!tpg->tpg_attrib.t10_pi) + return 0; + + return tpg->tpg_attrib.fabric_prot_type; +} + static void lio_tpg_release_fabric_acl( struct se_portal_group *se_tpg, struct se_node_acl *se_acl) @@ -1953,115 +1978,60 @@ static void lio_release_cmd(struct se_cmd *se_cmd) iscsit_release_cmd(cmd); } -/* End functions for target_core_fabric_ops */ - -int iscsi_target_register_configfs(void) -{ - struct target_fabric_configfs *fabric; - int ret; - - lio_target_fabric_configfs = NULL; - fabric = target_fabric_configfs_init(THIS_MODULE, "iscsi"); - if (IS_ERR(fabric)) { - pr_err("target_fabric_configfs_init() for" - " LIO-Target failed!\n"); - return PTR_ERR(fabric); - } - /* - * Setup the fabric API of function pointers used by target_core_mod.. - */ - fabric->tf_ops.get_fabric_name = &iscsi_get_fabric_name; - fabric->tf_ops.get_fabric_proto_ident = &iscsi_get_fabric_proto_ident; - fabric->tf_ops.tpg_get_wwn = &lio_tpg_get_endpoint_wwn; - fabric->tf_ops.tpg_get_tag = &lio_tpg_get_tag; - fabric->tf_ops.tpg_get_default_depth = &lio_tpg_get_default_depth; - fabric->tf_ops.tpg_get_pr_transport_id = &iscsi_get_pr_transport_id; - fabric->tf_ops.tpg_get_pr_transport_id_len = - &iscsi_get_pr_transport_id_len; - fabric->tf_ops.tpg_parse_pr_out_transport_id = - &iscsi_parse_pr_out_transport_id; - fabric->tf_ops.tpg_check_demo_mode = &lio_tpg_check_demo_mode; - fabric->tf_ops.tpg_check_demo_mode_cache = - &lio_tpg_check_demo_mode_cache; - fabric->tf_ops.tpg_check_demo_mode_write_protect = - &lio_tpg_check_demo_mode_write_protect; - fabric->tf_ops.tpg_check_prod_mode_write_protect = - &lio_tpg_check_prod_mode_write_protect; - fabric->tf_ops.tpg_alloc_fabric_acl = &lio_tpg_alloc_fabric_acl; - fabric->tf_ops.tpg_release_fabric_acl = &lio_tpg_release_fabric_acl; - fabric->tf_ops.tpg_get_inst_index = &lio_tpg_get_inst_index; - fabric->tf_ops.check_stop_free = &lio_check_stop_free, - fabric->tf_ops.release_cmd = &lio_release_cmd; - fabric->tf_ops.shutdown_session = &lio_tpg_shutdown_session; - fabric->tf_ops.close_session = &lio_tpg_close_session; - fabric->tf_ops.sess_get_index = &lio_sess_get_index; - fabric->tf_ops.sess_get_initiator_sid = &lio_sess_get_initiator_sid; - fabric->tf_ops.write_pending = &lio_write_pending; - fabric->tf_ops.write_pending_status = &lio_write_pending_status; - fabric->tf_ops.set_default_node_attributes = - &lio_set_default_node_attributes; - fabric->tf_ops.get_task_tag = &iscsi_get_task_tag; - fabric->tf_ops.get_cmd_state = &iscsi_get_cmd_state; - fabric->tf_ops.queue_data_in = &lio_queue_data_in; - fabric->tf_ops.queue_status = &lio_queue_status; - fabric->tf_ops.queue_tm_rsp = &lio_queue_tm_rsp; - fabric->tf_ops.aborted_task = &lio_aborted_task; - /* - * Setup function pointers for generic logic in target_core_fabric_configfs.c - */ - fabric->tf_ops.fabric_make_wwn = &lio_target_call_coreaddtiqn; - fabric->tf_ops.fabric_drop_wwn = &lio_target_call_coredeltiqn; - fabric->tf_ops.fabric_make_tpg = &lio_target_tiqn_addtpg; - fabric->tf_ops.fabric_drop_tpg = &lio_target_tiqn_deltpg; - fabric->tf_ops.fabric_post_link = NULL; - fabric->tf_ops.fabric_pre_unlink = NULL; - fabric->tf_ops.fabric_make_np = &lio_target_call_addnptotpg; - fabric->tf_ops.fabric_drop_np = &lio_target_call_delnpfromtpg; - fabric->tf_ops.fabric_make_nodeacl = &lio_target_make_nodeacl; - fabric->tf_ops.fabric_drop_nodeacl = &lio_target_drop_nodeacl; - /* - * Setup default attribute lists for various fabric->tf_cit_tmpl - * sturct config_item_type's - */ - fabric->tf_cit_tmpl.tfc_discovery_cit.ct_attrs = lio_target_discovery_auth_attrs; - fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = lio_target_wwn_attrs; - fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = lio_target_tpg_attrs; - fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = lio_target_tpg_attrib_attrs; - fabric->tf_cit_tmpl.tfc_tpg_auth_cit.ct_attrs = lio_target_tpg_auth_attrs; - fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = lio_target_tpg_param_attrs; - fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = lio_target_portal_attrs; - fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = lio_target_initiator_attrs; - fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = lio_target_nacl_attrib_attrs; - fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = lio_target_nacl_auth_attrs; - fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = lio_target_nacl_param_attrs; - - ret = target_fabric_configfs_register(fabric); - if (ret < 0) { - pr_err("target_fabric_configfs_register() for" - " LIO-Target failed!\n"); - target_fabric_configfs_free(fabric); - return ret; - } - - lio_target_fabric_configfs = fabric; - pr_debug("LIO_TARGET[0] - Set fabric ->" - " lio_target_fabric_configfs\n"); - return 0; -} - - -void iscsi_target_deregister_configfs(void) -{ - if (!lio_target_fabric_configfs) - return; - /* - * Shutdown discovery sessions and disable discovery TPG - */ - if (iscsit_global->discovery_tpg) - iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1); - - target_fabric_configfs_deregister(lio_target_fabric_configfs); - lio_target_fabric_configfs = NULL; - pr_debug("LIO_TARGET[0] - Cleared" - " lio_target_fabric_configfs\n"); -} +const struct target_core_fabric_ops iscsi_ops = { + .module = THIS_MODULE, + .name = "iscsi", + .get_fabric_name = iscsi_get_fabric_name, + .get_fabric_proto_ident = iscsi_get_fabric_proto_ident, + .tpg_get_wwn = lio_tpg_get_endpoint_wwn, + .tpg_get_tag = lio_tpg_get_tag, + .tpg_get_default_depth = lio_tpg_get_default_depth, + .tpg_get_pr_transport_id = iscsi_get_pr_transport_id, + .tpg_get_pr_transport_id_len = iscsi_get_pr_transport_id_len, + .tpg_parse_pr_out_transport_id = iscsi_parse_pr_out_transport_id, + .tpg_check_demo_mode = lio_tpg_check_demo_mode, + .tpg_check_demo_mode_cache = lio_tpg_check_demo_mode_cache, + .tpg_check_demo_mode_write_protect = + lio_tpg_check_demo_mode_write_protect, + .tpg_check_prod_mode_write_protect = + lio_tpg_check_prod_mode_write_protect, + .tpg_check_prot_fabric_only = &lio_tpg_check_prot_fabric_only, + .tpg_alloc_fabric_acl = lio_tpg_alloc_fabric_acl, + .tpg_release_fabric_acl = lio_tpg_release_fabric_acl, + .tpg_get_inst_index = lio_tpg_get_inst_index, + .check_stop_free = lio_check_stop_free, + .release_cmd = lio_release_cmd, + .shutdown_session = lio_tpg_shutdown_session, + .close_session = lio_tpg_close_session, + .sess_get_index = lio_sess_get_index, + .sess_get_initiator_sid = lio_sess_get_initiator_sid, + .write_pending = lio_write_pending, + .write_pending_status = lio_write_pending_status, + .set_default_node_attributes = lio_set_default_node_attributes, + .get_task_tag = iscsi_get_task_tag, + .get_cmd_state = iscsi_get_cmd_state, + .queue_data_in = lio_queue_data_in, + .queue_status = lio_queue_status, + .queue_tm_rsp = lio_queue_tm_rsp, + .aborted_task = lio_aborted_task, + .fabric_make_wwn = lio_target_call_coreaddtiqn, + .fabric_drop_wwn = lio_target_call_coredeltiqn, + .fabric_make_tpg = lio_target_tiqn_addtpg, + .fabric_drop_tpg = lio_target_tiqn_deltpg, + .fabric_make_np = lio_target_call_addnptotpg, + .fabric_drop_np = lio_target_call_delnpfromtpg, + .fabric_make_nodeacl = lio_target_make_nodeacl, + .fabric_drop_nodeacl = lio_target_drop_nodeacl, + + .tfc_discovery_attrs = lio_target_discovery_auth_attrs, + .tfc_wwn_attrs = lio_target_wwn_attrs, + .tfc_tpg_base_attrs = lio_target_tpg_attrs, + .tfc_tpg_attrib_attrs = lio_target_tpg_attrib_attrs, + .tfc_tpg_auth_attrs = lio_target_tpg_auth_attrs, + .tfc_tpg_param_attrs = lio_target_tpg_param_attrs, + .tfc_tpg_np_base_attrs = lio_target_portal_attrs, + .tfc_tpg_nacl_base_attrs = lio_target_initiator_attrs, + .tfc_tpg_nacl_attrib_attrs = lio_target_nacl_attrib_attrs, + .tfc_tpg_nacl_auth_attrs = lio_target_nacl_auth_attrs, + .tfc_tpg_nacl_param_attrs = lio_target_nacl_param_attrs, +}; diff --git a/drivers/target/iscsi/iscsi_target_configfs.h b/drivers/target/iscsi/iscsi_target_configfs.h deleted file mode 100644 index 8cd5a63c4edc..000000000000 --- a/drivers/target/iscsi/iscsi_target_configfs.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef ISCSI_TARGET_CONFIGFS_H -#define ISCSI_TARGET_CONFIGFS_H - -extern int iscsi_target_register_configfs(void); -extern void iscsi_target_deregister_configfs(void); - -#endif /* ISCSI_TARGET_CONFIGFS_H */ diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c index bdd8731a4daa..959a14c9dd5d 100644 --- a/drivers/target/iscsi/iscsi_target_erl0.c +++ b/drivers/target/iscsi/iscsi_target_erl0.c @@ -23,7 +23,6 @@ #include <target/iscsi/iscsi_target_core.h> #include "iscsi_target_seq_pdu_list.h" -#include "iscsi_target_tq.h" #include "iscsi_target_erl0.h" #include "iscsi_target_erl1.h" #include "iscsi_target_erl2.h" @@ -860,7 +859,10 @@ void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *conn) } spin_unlock_bh(&conn->state_lock); - iscsi_thread_set_force_reinstatement(conn); + if (conn->tx_thread && conn->tx_thread_active) + send_sig(SIGINT, conn->tx_thread, 1); + if (conn->rx_thread && conn->rx_thread_active) + send_sig(SIGINT, conn->rx_thread, 1); sleep: wait_for_completion(&conn->conn_wait_rcfr_comp); @@ -885,10 +887,10 @@ void iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep) return; } - if (iscsi_thread_set_force_reinstatement(conn) < 0) { - spin_unlock_bh(&conn->state_lock); - return; - } + if (conn->tx_thread && conn->tx_thread_active) + send_sig(SIGINT, conn->tx_thread, 1); + if (conn->rx_thread && conn->rx_thread_active) + send_sig(SIGINT, conn->rx_thread, 1); atomic_set(&conn->connection_reinstatement, 1); if (!sleep) { diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 153fb66ac1b8..8ce94ff744e6 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -26,7 +26,6 @@ #include <target/iscsi/iscsi_target_core.h> #include <target/iscsi/iscsi_target_stat.h> -#include "iscsi_target_tq.h" #include "iscsi_target_device.h" #include "iscsi_target_nego.h" #include "iscsi_target_erl0.h" @@ -699,6 +698,51 @@ static void iscsi_post_login_start_timers(struct iscsi_conn *conn) iscsit_start_nopin_timer(conn); } +static int iscsit_start_kthreads(struct iscsi_conn *conn) +{ + int ret = 0; + + spin_lock(&iscsit_global->ts_bitmap_lock); + conn->bitmap_id = bitmap_find_free_region(iscsit_global->ts_bitmap, + ISCSIT_BITMAP_BITS, get_order(1)); + spin_unlock(&iscsit_global->ts_bitmap_lock); + + if (conn->bitmap_id < 0) { + pr_err("bitmap_find_free_region() failed for" + " iscsit_start_kthreads()\n"); + return -ENOMEM; + } + + conn->tx_thread = kthread_run(iscsi_target_tx_thread, conn, + "%s", ISCSI_TX_THREAD_NAME); + if (IS_ERR(conn->tx_thread)) { + pr_err("Unable to start iscsi_target_tx_thread\n"); + ret = PTR_ERR(conn->tx_thread); + goto out_bitmap; + } + conn->tx_thread_active = true; + + conn->rx_thread = kthread_run(iscsi_target_rx_thread, conn, + "%s", ISCSI_RX_THREAD_NAME); + if (IS_ERR(conn->rx_thread)) { + pr_err("Unable to start iscsi_target_rx_thread\n"); + ret = PTR_ERR(conn->rx_thread); + goto out_tx; + } + conn->rx_thread_active = true; + + return 0; +out_tx: + kthread_stop(conn->tx_thread); + conn->tx_thread_active = false; +out_bitmap: + spin_lock(&iscsit_global->ts_bitmap_lock); + bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id, + get_order(1)); + spin_unlock(&iscsit_global->ts_bitmap_lock); + return ret; +} + int iscsi_post_login_handler( struct iscsi_np *np, struct iscsi_conn *conn, @@ -709,7 +753,7 @@ int iscsi_post_login_handler( struct se_session *se_sess = sess->se_sess; struct iscsi_portal_group *tpg = sess->tpg; struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; - struct iscsi_thread_set *ts; + int rc; iscsit_inc_conn_usage_count(conn); @@ -724,7 +768,6 @@ int iscsi_post_login_handler( /* * SCSI Initiator -> SCSI Target Port Mapping */ - ts = iscsi_get_thread_set(); if (!zero_tsih) { iscsi_set_session_parameters(sess->sess_ops, conn->param_list, 0); @@ -751,9 +794,11 @@ int iscsi_post_login_handler( sess->sess_ops->InitiatorName); spin_unlock_bh(&sess->conn_lock); - iscsi_post_login_start_timers(conn); + rc = iscsit_start_kthreads(conn); + if (rc) + return rc; - iscsi_activate_thread_set(conn, ts); + iscsi_post_login_start_timers(conn); /* * Determine CPU mask to ensure connection's RX and TX kthreads * are scheduled on the same CPU. @@ -810,8 +855,11 @@ int iscsi_post_login_handler( " iSCSI Target Portal Group: %hu\n", tpg->nsessions, tpg->tpgt); spin_unlock_bh(&se_tpg->session_lock); + rc = iscsit_start_kthreads(conn); + if (rc) + return rc; + iscsi_post_login_start_timers(conn); - iscsi_activate_thread_set(conn, ts); /* * Determine CPU mask to ensure connection's RX and TX kthreads * are scheduled on the same CPU. diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index bdd127c0e3ae..e8a240818353 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -68,10 +68,8 @@ int iscsit_load_discovery_tpg(void) return -1; } - ret = core_tpg_register( - &lio_target_fabric_configfs->tf_ops, - NULL, &tpg->tpg_se_tpg, tpg, - TRANSPORT_TPG_TYPE_DISCOVERY); + ret = core_tpg_register(&iscsi_ops, NULL, &tpg->tpg_se_tpg, + tpg, TRANSPORT_TPG_TYPE_DISCOVERY); if (ret < 0) { kfree(tpg); return -1; @@ -228,6 +226,7 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg) a->demo_mode_discovery = TA_DEMO_MODE_DISCOVERY; a->default_erl = TA_DEFAULT_ERL; a->t10_pi = TA_DEFAULT_T10_PI; + a->fabric_prot_type = TA_DEFAULT_FABRIC_PROT_TYPE; } int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg) @@ -878,3 +877,21 @@ int iscsit_ta_t10_pi( return 0; } + +int iscsit_ta_fabric_prot_type( + struct iscsi_portal_group *tpg, + u32 prot_type) +{ + struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; + + if ((prot_type != 0) && (prot_type != 1) && (prot_type != 3)) { + pr_err("Illegal value for fabric_prot_type: %u\n", prot_type); + return -EINVAL; + } + + a->fabric_prot_type = prot_type; + pr_debug("iSCSI_TPG[%hu] - T10 Fabric Protection Type: %u\n", + tpg->tpgt, prot_type); + + return 0; +} diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h index e7265337bc43..95ff5bdecd71 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.h +++ b/drivers/target/iscsi/iscsi_target_tpg.h @@ -39,5 +39,6 @@ extern int iscsit_ta_prod_mode_write_protect(struct iscsi_portal_group *, u32); extern int iscsit_ta_demo_mode_discovery(struct iscsi_portal_group *, u32); extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32); extern int iscsit_ta_t10_pi(struct iscsi_portal_group *, u32); +extern int iscsit_ta_fabric_prot_type(struct iscsi_portal_group *, u32); #endif /* ISCSI_TARGET_TPG_H */ diff --git a/drivers/target/iscsi/iscsi_target_tq.c b/drivers/target/iscsi/iscsi_target_tq.c deleted file mode 100644 index 26aa50996473..000000000000 --- a/drivers/target/iscsi/iscsi_target_tq.c +++ /dev/null @@ -1,495 +0,0 @@ -/******************************************************************************* - * This file contains the iSCSI Login Thread and Thread Queue functions. - * - * (c) Copyright 2007-2013 Datera, Inc. - * - * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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 <linux/kthread.h> -#include <linux/list.h> -#include <linux/bitmap.h> - -#include <target/iscsi/iscsi_target_core.h> -#include "iscsi_target_tq.h" -#include "iscsi_target.h" - -static LIST_HEAD(inactive_ts_list); -static DEFINE_SPINLOCK(inactive_ts_lock); -static DEFINE_SPINLOCK(ts_bitmap_lock); - -static void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *ts) -{ - if (!list_empty(&ts->ts_list)) { - WARN_ON(1); - return; - } - spin_lock(&inactive_ts_lock); - list_add_tail(&ts->ts_list, &inactive_ts_list); - iscsit_global->inactive_ts++; - spin_unlock(&inactive_ts_lock); -} - -static struct iscsi_thread_set *iscsi_get_ts_from_inactive_list(void) -{ - struct iscsi_thread_set *ts; - - spin_lock(&inactive_ts_lock); - if (list_empty(&inactive_ts_list)) { - spin_unlock(&inactive_ts_lock); - return NULL; - } - - ts = list_first_entry(&inactive_ts_list, struct iscsi_thread_set, ts_list); - - list_del_init(&ts->ts_list); - iscsit_global->inactive_ts--; - spin_unlock(&inactive_ts_lock); - - return ts; -} - -int iscsi_allocate_thread_sets(u32 thread_pair_count) -{ - int allocated_thread_pair_count = 0, i, thread_id; - struct iscsi_thread_set *ts = NULL; - - for (i = 0; i < thread_pair_count; i++) { - ts = kzalloc(sizeof(struct iscsi_thread_set), GFP_KERNEL); - if (!ts) { - pr_err("Unable to allocate memory for" - " thread set.\n"); - return allocated_thread_pair_count; - } - /* - * Locate the next available regision in the thread_set_bitmap - */ - spin_lock(&ts_bitmap_lock); - thread_id = bitmap_find_free_region(iscsit_global->ts_bitmap, - iscsit_global->ts_bitmap_count, get_order(1)); - spin_unlock(&ts_bitmap_lock); - if (thread_id < 0) { - pr_err("bitmap_find_free_region() failed for" - " thread_set_bitmap\n"); - kfree(ts); - return allocated_thread_pair_count; - } - - ts->thread_id = thread_id; - ts->status = ISCSI_THREAD_SET_FREE; - INIT_LIST_HEAD(&ts->ts_list); - spin_lock_init(&ts->ts_state_lock); - init_completion(&ts->rx_restart_comp); - init_completion(&ts->tx_restart_comp); - init_completion(&ts->rx_start_comp); - init_completion(&ts->tx_start_comp); - sema_init(&ts->ts_activate_sem, 0); - - ts->create_threads = 1; - ts->tx_thread = kthread_run(iscsi_target_tx_thread, ts, "%s", - ISCSI_TX_THREAD_NAME); - if (IS_ERR(ts->tx_thread)) { - dump_stack(); - pr_err("Unable to start iscsi_target_tx_thread\n"); - break; - } - - ts->rx_thread = kthread_run(iscsi_target_rx_thread, ts, "%s", - ISCSI_RX_THREAD_NAME); - if (IS_ERR(ts->rx_thread)) { - kthread_stop(ts->tx_thread); - pr_err("Unable to start iscsi_target_rx_thread\n"); - break; - } - ts->create_threads = 0; - - iscsi_add_ts_to_inactive_list(ts); - allocated_thread_pair_count++; - } - - pr_debug("Spawned %d thread set(s) (%d total threads).\n", - allocated_thread_pair_count, allocated_thread_pair_count * 2); - return allocated_thread_pair_count; -} - -static void iscsi_deallocate_thread_one(struct iscsi_thread_set *ts) -{ - spin_lock_bh(&ts->ts_state_lock); - ts->status = ISCSI_THREAD_SET_DIE; - - if (ts->rx_thread) { - complete(&ts->rx_start_comp); - spin_unlock_bh(&ts->ts_state_lock); - kthread_stop(ts->rx_thread); - spin_lock_bh(&ts->ts_state_lock); - } - if (ts->tx_thread) { - complete(&ts->tx_start_comp); - spin_unlock_bh(&ts->ts_state_lock); - kthread_stop(ts->tx_thread); - spin_lock_bh(&ts->ts_state_lock); - } - spin_unlock_bh(&ts->ts_state_lock); - /* - * Release this thread_id in the thread_set_bitmap - */ - spin_lock(&ts_bitmap_lock); - bitmap_release_region(iscsit_global->ts_bitmap, - ts->thread_id, get_order(1)); - spin_unlock(&ts_bitmap_lock); - - kfree(ts); -} - -void iscsi_deallocate_thread_sets(void) -{ - struct iscsi_thread_set *ts = NULL; - u32 released_count = 0; - - while ((ts = iscsi_get_ts_from_inactive_list())) { - - iscsi_deallocate_thread_one(ts); - released_count++; - } - - if (released_count) - pr_debug("Stopped %d thread set(s) (%d total threads)." - "\n", released_count, released_count * 2); -} - -static void iscsi_deallocate_extra_thread_sets(void) -{ - u32 orig_count, released_count = 0; - struct iscsi_thread_set *ts = NULL; - - orig_count = TARGET_THREAD_SET_COUNT; - - while ((iscsit_global->inactive_ts + 1) > orig_count) { - ts = iscsi_get_ts_from_inactive_list(); - if (!ts) - break; - - iscsi_deallocate_thread_one(ts); - released_count++; - } - - if (released_count) - pr_debug("Stopped %d thread set(s) (%d total threads)." - "\n", released_count, released_count * 2); -} - -void iscsi_activate_thread_set(struct iscsi_conn *conn, struct iscsi_thread_set *ts) -{ - spin_lock_bh(&ts->ts_state_lock); - conn->thread_set = ts; - ts->conn = conn; - ts->status = ISCSI_THREAD_SET_ACTIVE; - spin_unlock_bh(&ts->ts_state_lock); - - complete(&ts->rx_start_comp); - complete(&ts->tx_start_comp); - - down(&ts->ts_activate_sem); -} - -struct iscsi_thread_set *iscsi_get_thread_set(void) -{ - struct iscsi_thread_set *ts; - -get_set: - ts = iscsi_get_ts_from_inactive_list(); - if (!ts) { - iscsi_allocate_thread_sets(1); - goto get_set; - } - - ts->delay_inactive = 1; - ts->signal_sent = 0; - ts->thread_count = 2; - init_completion(&ts->rx_restart_comp); - init_completion(&ts->tx_restart_comp); - sema_init(&ts->ts_activate_sem, 0); - - return ts; -} - -void iscsi_set_thread_clear(struct iscsi_conn *conn, u8 thread_clear) -{ - struct iscsi_thread_set *ts = NULL; - - if (!conn->thread_set) { - pr_err("struct iscsi_conn->thread_set is NULL\n"); - return; - } - ts = conn->thread_set; - - spin_lock_bh(&ts->ts_state_lock); - ts->thread_clear &= ~thread_clear; - - if ((thread_clear & ISCSI_CLEAR_RX_THREAD) && - (ts->blocked_threads & ISCSI_BLOCK_RX_THREAD)) - complete(&ts->rx_restart_comp); - else if ((thread_clear & ISCSI_CLEAR_TX_THREAD) && - (ts->blocked_threads & ISCSI_BLOCK_TX_THREAD)) - complete(&ts->tx_restart_comp); - spin_unlock_bh(&ts->ts_state_lock); -} - -void iscsi_set_thread_set_signal(struct iscsi_conn *conn, u8 signal_sent) -{ - struct iscsi_thread_set *ts = NULL; - - if (!conn->thread_set) { - pr_err("struct iscsi_conn->thread_set is NULL\n"); - return; - } - ts = conn->thread_set; - - spin_lock_bh(&ts->ts_state_lock); - ts->signal_sent |= signal_sent; - spin_unlock_bh(&ts->ts_state_lock); -} - -int iscsi_release_thread_set(struct iscsi_conn *conn) -{ - int thread_called = 0; - struct iscsi_thread_set *ts = NULL; - - if (!conn || !conn->thread_set) { - pr_err("connection or thread set pointer is NULL\n"); - BUG(); - } - ts = conn->thread_set; - - spin_lock_bh(&ts->ts_state_lock); - ts->status = ISCSI_THREAD_SET_RESET; - - if (!strncmp(current->comm, ISCSI_RX_THREAD_NAME, - strlen(ISCSI_RX_THREAD_NAME))) - thread_called = ISCSI_RX_THREAD; - else if (!strncmp(current->comm, ISCSI_TX_THREAD_NAME, - strlen(ISCSI_TX_THREAD_NAME))) - thread_called = ISCSI_TX_THREAD; - - if (ts->rx_thread && (thread_called == ISCSI_TX_THREAD) && - (ts->thread_clear & ISCSI_CLEAR_RX_THREAD)) { - - if (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD)) { - send_sig(SIGINT, ts->rx_thread, 1); - ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD; - } - ts->blocked_threads |= ISCSI_BLOCK_RX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); - wait_for_completion(&ts->rx_restart_comp); - spin_lock_bh(&ts->ts_state_lock); - ts->blocked_threads &= ~ISCSI_BLOCK_RX_THREAD; - } - if (ts->tx_thread && (thread_called == ISCSI_RX_THREAD) && - (ts->thread_clear & ISCSI_CLEAR_TX_THREAD)) { - - if (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD)) { - send_sig(SIGINT, ts->tx_thread, 1); - ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD; - } - ts->blocked_threads |= ISCSI_BLOCK_TX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); - wait_for_completion(&ts->tx_restart_comp); - spin_lock_bh(&ts->ts_state_lock); - ts->blocked_threads &= ~ISCSI_BLOCK_TX_THREAD; - } - - ts->conn = NULL; - ts->status = ISCSI_THREAD_SET_FREE; - spin_unlock_bh(&ts->ts_state_lock); - - return 0; -} - -int iscsi_thread_set_force_reinstatement(struct iscsi_conn *conn) -{ - struct iscsi_thread_set *ts; - - if (!conn->thread_set) - return -1; - ts = conn->thread_set; - - spin_lock_bh(&ts->ts_state_lock); - if (ts->status != ISCSI_THREAD_SET_ACTIVE) { - spin_unlock_bh(&ts->ts_state_lock); - return -1; - } - - if (ts->tx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD))) { - send_sig(SIGINT, ts->tx_thread, 1); - ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD; - } - if (ts->rx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD))) { - send_sig(SIGINT, ts->rx_thread, 1); - ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD; - } - spin_unlock_bh(&ts->ts_state_lock); - - return 0; -} - -static void iscsi_check_to_add_additional_sets(void) -{ - int thread_sets_add; - - spin_lock(&inactive_ts_lock); - thread_sets_add = iscsit_global->inactive_ts; - spin_unlock(&inactive_ts_lock); - if (thread_sets_add == 1) - iscsi_allocate_thread_sets(1); -} - -static int iscsi_signal_thread_pre_handler(struct iscsi_thread_set *ts) -{ - spin_lock_bh(&ts->ts_state_lock); - if (ts->status == ISCSI_THREAD_SET_DIE || kthread_should_stop() || - signal_pending(current)) { - spin_unlock_bh(&ts->ts_state_lock); - return -1; - } - spin_unlock_bh(&ts->ts_state_lock); - - return 0; -} - -struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *ts) -{ - int ret; - - spin_lock_bh(&ts->ts_state_lock); - if (ts->create_threads) { - spin_unlock_bh(&ts->ts_state_lock); - goto sleep; - } - - if (ts->status != ISCSI_THREAD_SET_DIE) - flush_signals(current); - - if (ts->delay_inactive && (--ts->thread_count == 0)) { - spin_unlock_bh(&ts->ts_state_lock); - - if (!iscsit_global->in_shutdown) - iscsi_deallocate_extra_thread_sets(); - - iscsi_add_ts_to_inactive_list(ts); - spin_lock_bh(&ts->ts_state_lock); - } - - if ((ts->status == ISCSI_THREAD_SET_RESET) && - (ts->thread_clear & ISCSI_CLEAR_RX_THREAD)) - complete(&ts->rx_restart_comp); - - ts->thread_clear &= ~ISCSI_CLEAR_RX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); -sleep: - ret = wait_for_completion_interruptible(&ts->rx_start_comp); - if (ret != 0) - return NULL; - - if (iscsi_signal_thread_pre_handler(ts) < 0) - return NULL; - - iscsi_check_to_add_additional_sets(); - - spin_lock_bh(&ts->ts_state_lock); - if (!ts->conn) { - pr_err("struct iscsi_thread_set->conn is NULL for" - " RX thread_id: %s/%d\n", current->comm, current->pid); - spin_unlock_bh(&ts->ts_state_lock); - return NULL; - } - ts->thread_clear |= ISCSI_CLEAR_RX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); - - up(&ts->ts_activate_sem); - - return ts->conn; -} - -struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *ts) -{ - int ret; - - spin_lock_bh(&ts->ts_state_lock); - if (ts->create_threads) { - spin_unlock_bh(&ts->ts_state_lock); - goto sleep; - } - - if (ts->status != ISCSI_THREAD_SET_DIE) - flush_signals(current); - - if (ts->delay_inactive && (--ts->thread_count == 0)) { - spin_unlock_bh(&ts->ts_state_lock); - - if (!iscsit_global->in_shutdown) - iscsi_deallocate_extra_thread_sets(); - - iscsi_add_ts_to_inactive_list(ts); - spin_lock_bh(&ts->ts_state_lock); - } - if ((ts->status == ISCSI_THREAD_SET_RESET) && - (ts->thread_clear & ISCSI_CLEAR_TX_THREAD)) - complete(&ts->tx_restart_comp); - - ts->thread_clear &= ~ISCSI_CLEAR_TX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); -sleep: - ret = wait_for_completion_interruptible(&ts->tx_start_comp); - if (ret != 0) - return NULL; - - if (iscsi_signal_thread_pre_handler(ts) < 0) - return NULL; - - iscsi_check_to_add_additional_sets(); - - spin_lock_bh(&ts->ts_state_lock); - if (!ts->conn) { - pr_err("struct iscsi_thread_set->conn is NULL for" - " TX thread_id: %s/%d\n", current->comm, current->pid); - spin_unlock_bh(&ts->ts_state_lock); - return NULL; - } - ts->thread_clear |= ISCSI_CLEAR_TX_THREAD; - spin_unlock_bh(&ts->ts_state_lock); - - up(&ts->ts_activate_sem); - - return ts->conn; -} - -int iscsi_thread_set_init(void) -{ - int size; - - iscsit_global->ts_bitmap_count = ISCSI_TS_BITMAP_BITS; - - size = BITS_TO_LONGS(iscsit_global->ts_bitmap_count) * sizeof(long); - iscsit_global->ts_bitmap = kzalloc(size, GFP_KERNEL); - if (!iscsit_global->ts_bitmap) { - pr_err("Unable to allocate iscsit_global->ts_bitmap\n"); - return -ENOMEM; - } - - return 0; -} - -void iscsi_thread_set_free(void) -{ - kfree(iscsit_global->ts_bitmap); -} diff --git a/drivers/target/iscsi/iscsi_target_tq.h b/drivers/target/iscsi/iscsi_target_tq.h deleted file mode 100644 index cc1eede5ab3a..000000000000 --- a/drivers/target/iscsi/iscsi_target_tq.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef ISCSI_THREAD_QUEUE_H -#define ISCSI_THREAD_QUEUE_H - -/* - * Defines for thread sets. - */ -extern int iscsi_thread_set_force_reinstatement(struct iscsi_conn *); -extern int iscsi_allocate_thread_sets(u32); -extern void iscsi_deallocate_thread_sets(void); -extern void iscsi_activate_thread_set(struct iscsi_conn *, struct iscsi_thread_set *); -extern struct iscsi_thread_set *iscsi_get_thread_set(void); -extern void iscsi_set_thread_clear(struct iscsi_conn *, u8); -extern void iscsi_set_thread_set_signal(struct iscsi_conn *, u8); -extern int iscsi_release_thread_set(struct iscsi_conn *); -extern struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *); -extern struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *); -extern int iscsi_thread_set_init(void); -extern void iscsi_thread_set_free(void); - -extern int iscsi_target_tx_thread(void *); -extern int iscsi_target_rx_thread(void *); - -#define TARGET_THREAD_SET_COUNT 4 - -#define ISCSI_RX_THREAD 1 -#define ISCSI_TX_THREAD 2 -#define ISCSI_RX_THREAD_NAME "iscsi_trx" -#define ISCSI_TX_THREAD_NAME "iscsi_ttx" -#define ISCSI_BLOCK_RX_THREAD 0x1 -#define ISCSI_BLOCK_TX_THREAD 0x2 -#define ISCSI_CLEAR_RX_THREAD 0x1 -#define ISCSI_CLEAR_TX_THREAD 0x2 -#define ISCSI_SIGNAL_RX_THREAD 0x1 -#define ISCSI_SIGNAL_TX_THREAD 0x2 - -/* struct iscsi_thread_set->status */ -#define ISCSI_THREAD_SET_FREE 1 -#define ISCSI_THREAD_SET_ACTIVE 2 -#define ISCSI_THREAD_SET_DIE 3 -#define ISCSI_THREAD_SET_RESET 4 -#define ISCSI_THREAD_SET_DEALLOCATE_THREADS 5 - -/* By default allow a maximum of 32K iSCSI connections */ -#define ISCSI_TS_BITMAP_BITS 32768 - -struct iscsi_thread_set { - /* flags used for blocking and restarting sets */ - int blocked_threads; - /* flag for creating threads */ - int create_threads; - /* flag for delaying readding to inactive list */ - int delay_inactive; - /* status for thread set */ - int status; - /* which threads have had signals sent */ - int signal_sent; - /* flag for which threads exited first */ - int thread_clear; - /* Active threads in the thread set */ - int thread_count; - /* Unique thread ID */ - u32 thread_id; - /* pointer to connection if set is active */ - struct iscsi_conn *conn; - /* used for controlling ts state accesses */ - spinlock_t ts_state_lock; - /* used for restarting thread queue */ - struct completion rx_restart_comp; - /* used for restarting thread queue */ - struct completion tx_restart_comp; - /* used for normal unused blocking */ - struct completion rx_start_comp; - /* used for normal unused blocking */ - struct completion tx_start_comp; - /* OS descriptor for rx thread */ - struct task_struct *rx_thread; - /* OS descriptor for tx thread */ - struct task_struct *tx_thread; - /* struct iscsi_thread_set in list list head*/ - struct list_head ts_list; - struct semaphore ts_activate_sem; -}; - -#endif /*** ISCSI_THREAD_QUEUE_H ***/ diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 390df8ed72b2..b18edda3e8af 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -33,7 +33,6 @@ #include "iscsi_target_erl1.h" #include "iscsi_target_erl2.h" #include "iscsi_target_tpg.h" -#include "iscsi_target_tq.h" #include "iscsi_target_util.h" #include "iscsi_target.h" diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index c36bd7c29136..51f0c895c6a5 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -41,8 +41,7 @@ #define to_tcm_loop_hba(hba) container_of(hba, struct tcm_loop_hba, dev) -/* Local pointer to allocated TCM configfs fabric module */ -static struct target_fabric_configfs *tcm_loop_fabric_configfs; +static const struct target_core_fabric_ops loop_ops; static struct workqueue_struct *tcm_loop_workqueue; static struct kmem_cache *tcm_loop_cmd_cache; @@ -108,7 +107,7 @@ static struct device_driver tcm_loop_driverfs = { /* * Used with root_device_register() in tcm_loop_alloc_core_bus() below */ -struct device *tcm_loop_primary; +static struct device *tcm_loop_primary; static void tcm_loop_submission_work(struct work_struct *work) { @@ -697,6 +696,13 @@ static int tcm_loop_check_prod_mode_write_protect(struct se_portal_group *se_tpg return 0; } +static int tcm_loop_check_prot_fabric_only(struct se_portal_group *se_tpg) +{ + struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, + tl_se_tpg); + return tl_tpg->tl_fabric_prot_type; +} + static struct se_node_acl *tcm_loop_tpg_alloc_fabric_acl( struct se_portal_group *se_tpg) { @@ -912,6 +918,46 @@ static void tcm_loop_port_unlink( /* End items for tcm_loop_port_cit */ +static ssize_t tcm_loop_tpg_attrib_show_fabric_prot_type( + struct se_portal_group *se_tpg, + char *page) +{ + struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, + tl_se_tpg); + + return sprintf(page, "%d\n", tl_tpg->tl_fabric_prot_type); +} + +static ssize_t tcm_loop_tpg_attrib_store_fabric_prot_type( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, + tl_se_tpg); + unsigned long val; + int ret = kstrtoul(page, 0, &val); + + if (ret) { + pr_err("kstrtoul() returned %d for fabric_prot_type\n", ret); + return ret; + } + if (val != 0 && val != 1 && val != 3) { + pr_err("Invalid qla2xxx fabric_prot_type: %lu\n", val); + return -EINVAL; + } + tl_tpg->tl_fabric_prot_type = val; + + return count; +} + +TF_TPG_ATTRIB_ATTR(tcm_loop, fabric_prot_type, S_IRUGO | S_IWUSR); + +static struct configfs_attribute *tcm_loop_tpg_attrib_attrs[] = { + &tcm_loop_tpg_attrib_fabric_prot_type.attr, + NULL, +}; + /* Start items for tcm_loop_nexus_cit */ static int tcm_loop_make_nexus( @@ -937,7 +983,8 @@ static int tcm_loop_make_nexus( /* * Initialize the struct se_session pointer */ - tl_nexus->se_sess = transport_init_session(TARGET_PROT_ALL); + tl_nexus->se_sess = transport_init_session( + TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS); if (IS_ERR(tl_nexus->se_sess)) { ret = PTR_ERR(tl_nexus->se_sess); goto out; @@ -1165,21 +1212,19 @@ static struct se_portal_group *tcm_loop_make_naa_tpg( struct tcm_loop_hba *tl_hba = container_of(wwn, struct tcm_loop_hba, tl_hba_wwn); struct tcm_loop_tpg *tl_tpg; - char *tpgt_str, *end_ptr; int ret; - unsigned short int tpgt; + unsigned long tpgt; - tpgt_str = strstr(name, "tpgt_"); - if (!tpgt_str) { + if (strstr(name, "tpgt_") != name) { pr_err("Unable to locate \"tpgt_#\" directory" " group\n"); return ERR_PTR(-EINVAL); } - tpgt_str += 5; /* Skip ahead of "tpgt_" */ - tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0); + if (kstrtoul(name+5, 10, &tpgt)) + return ERR_PTR(-EINVAL); if (tpgt >= TL_TPGS_PER_HBA) { - pr_err("Passed tpgt: %hu exceeds TL_TPGS_PER_HBA:" + pr_err("Passed tpgt: %lu exceeds TL_TPGS_PER_HBA:" " %u\n", tpgt, TL_TPGS_PER_HBA); return ERR_PTR(-EINVAL); } @@ -1189,14 +1234,13 @@ static struct se_portal_group *tcm_loop_make_naa_tpg( /* * Register the tl_tpg as a emulated SAS TCM Target Endpoint */ - ret = core_tpg_register(&tcm_loop_fabric_configfs->tf_ops, - wwn, &tl_tpg->tl_se_tpg, tl_tpg, + ret = core_tpg_register(&loop_ops, wwn, &tl_tpg->tl_se_tpg, tl_tpg, TRANSPORT_TPG_TYPE_NORMAL); if (ret < 0) return ERR_PTR(-ENOMEM); pr_debug("TCM_Loop_ConfigFS: Allocated Emulated %s" - " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba), + " Target Port %s,t,0x%04lx\n", tcm_loop_dump_proto_id(tl_hba), config_item_name(&wwn->wwn_group.cg_item), tpgt); return &tl_tpg->tl_se_tpg; @@ -1338,127 +1382,51 @@ static struct configfs_attribute *tcm_loop_wwn_attrs[] = { /* End items for tcm_loop_cit */ -static int tcm_loop_register_configfs(void) -{ - struct target_fabric_configfs *fabric; - int ret; - /* - * Set the TCM Loop HBA counter to zero - */ - tcm_loop_hba_no_cnt = 0; - /* - * Register the top level struct config_item_type with TCM core - */ - fabric = target_fabric_configfs_init(THIS_MODULE, "loopback"); - if (IS_ERR(fabric)) { - pr_err("tcm_loop_register_configfs() failed!\n"); - return PTR_ERR(fabric); - } - /* - * Setup the fabric API of function pointers used by target_core_mod - */ - fabric->tf_ops.get_fabric_name = &tcm_loop_get_fabric_name; - fabric->tf_ops.get_fabric_proto_ident = &tcm_loop_get_fabric_proto_ident; - fabric->tf_ops.tpg_get_wwn = &tcm_loop_get_endpoint_wwn; - fabric->tf_ops.tpg_get_tag = &tcm_loop_get_tag; - fabric->tf_ops.tpg_get_default_depth = &tcm_loop_get_default_depth; - fabric->tf_ops.tpg_get_pr_transport_id = &tcm_loop_get_pr_transport_id; - fabric->tf_ops.tpg_get_pr_transport_id_len = - &tcm_loop_get_pr_transport_id_len; - fabric->tf_ops.tpg_parse_pr_out_transport_id = - &tcm_loop_parse_pr_out_transport_id; - fabric->tf_ops.tpg_check_demo_mode = &tcm_loop_check_demo_mode; - fabric->tf_ops.tpg_check_demo_mode_cache = - &tcm_loop_check_demo_mode_cache; - fabric->tf_ops.tpg_check_demo_mode_write_protect = - &tcm_loop_check_demo_mode_write_protect; - fabric->tf_ops.tpg_check_prod_mode_write_protect = - &tcm_loop_check_prod_mode_write_protect; - /* - * The TCM loopback fabric module runs in demo-mode to a local - * virtual SCSI device, so fabric dependent initator ACLs are - * not required. - */ - fabric->tf_ops.tpg_alloc_fabric_acl = &tcm_loop_tpg_alloc_fabric_acl; - fabric->tf_ops.tpg_release_fabric_acl = - &tcm_loop_tpg_release_fabric_acl; - fabric->tf_ops.tpg_get_inst_index = &tcm_loop_get_inst_index; - /* - * Used for setting up remaining TCM resources in process context - */ - fabric->tf_ops.check_stop_free = &tcm_loop_check_stop_free; - fabric->tf_ops.release_cmd = &tcm_loop_release_cmd; - fabric->tf_ops.shutdown_session = &tcm_loop_shutdown_session; - fabric->tf_ops.close_session = &tcm_loop_close_session; - fabric->tf_ops.sess_get_index = &tcm_loop_sess_get_index; - fabric->tf_ops.sess_get_initiator_sid = NULL; - fabric->tf_ops.write_pending = &tcm_loop_write_pending; - fabric->tf_ops.write_pending_status = &tcm_loop_write_pending_status; - /* - * Not used for TCM loopback - */ - fabric->tf_ops.set_default_node_attributes = - &tcm_loop_set_default_node_attributes; - fabric->tf_ops.get_task_tag = &tcm_loop_get_task_tag; - fabric->tf_ops.get_cmd_state = &tcm_loop_get_cmd_state; - fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in; - fabric->tf_ops.queue_status = &tcm_loop_queue_status; - fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp; - fabric->tf_ops.aborted_task = &tcm_loop_aborted_task; - - /* - * Setup function pointers for generic logic in target_core_fabric_configfs.c - */ - fabric->tf_ops.fabric_make_wwn = &tcm_loop_make_scsi_hba; - fabric->tf_ops.fabric_drop_wwn = &tcm_loop_drop_scsi_hba; - fabric->tf_ops.fabric_make_tpg = &tcm_loop_make_naa_tpg; - fabric->tf_ops.fabric_drop_tpg = &tcm_loop_drop_naa_tpg; - /* - * fabric_post_link() and fabric_pre_unlink() are used for - * registration and release of TCM Loop Virtual SCSI LUNs. - */ - fabric->tf_ops.fabric_post_link = &tcm_loop_port_link; - fabric->tf_ops.fabric_pre_unlink = &tcm_loop_port_unlink; - fabric->tf_ops.fabric_make_np = NULL; - fabric->tf_ops.fabric_drop_np = NULL; - /* - * Setup default attribute lists for various fabric->tf_cit_tmpl - */ - fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs; - fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs; - fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; - /* - * Once fabric->tf_ops has been setup, now register the fabric for - * use within TCM - */ - ret = target_fabric_configfs_register(fabric); - if (ret < 0) { - pr_err("target_fabric_configfs_register() for" - " TCM_Loop failed!\n"); - target_fabric_configfs_free(fabric); - return -1; - } - /* - * Setup our local pointer to *fabric. - */ - tcm_loop_fabric_configfs = fabric; - pr_debug("TCM_LOOP[0] - Set fabric ->" - " tcm_loop_fabric_configfs\n"); - return 0; -} - -static void tcm_loop_deregister_configfs(void) -{ - if (!tcm_loop_fabric_configfs) - return; - - target_fabric_configfs_deregister(tcm_loop_fabric_configfs); - tcm_loop_fabric_configfs = NULL; - pr_debug("TCM_LOOP[0] - Cleared" - " tcm_loop_fabric_configfs\n"); -} +static const struct target_core_fabric_ops loop_ops = { + .module = THIS_MODULE, + .name = "loopback", + .get_fabric_name = tcm_loop_get_fabric_name, + .get_fabric_proto_ident = tcm_loop_get_fabric_proto_ident, + .tpg_get_wwn = tcm_loop_get_endpoint_wwn, + .tpg_get_tag = tcm_loop_get_tag, + .tpg_get_default_depth = tcm_loop_get_default_depth, + .tpg_get_pr_transport_id = tcm_loop_get_pr_transport_id, + .tpg_get_pr_transport_id_len = tcm_loop_get_pr_transport_id_len, + .tpg_parse_pr_out_transport_id = tcm_loop_parse_pr_out_transport_id, + .tpg_check_demo_mode = tcm_loop_check_demo_mode, + .tpg_check_demo_mode_cache = tcm_loop_check_demo_mode_cache, + .tpg_check_demo_mode_write_protect = + tcm_loop_check_demo_mode_write_protect, + .tpg_check_prod_mode_write_protect = + tcm_loop_check_prod_mode_write_protect, + .tpg_check_prot_fabric_only = tcm_loop_check_prot_fabric_only, + .tpg_alloc_fabric_acl = tcm_loop_tpg_alloc_fabric_acl, + .tpg_release_fabric_acl = tcm_loop_tpg_release_fabric_acl, + .tpg_get_inst_index = tcm_loop_get_inst_index, + .check_stop_free = tcm_loop_check_stop_free, + .release_cmd = tcm_loop_release_cmd, + .shutdown_session = tcm_loop_shutdown_session, + .close_session = tcm_loop_close_session, + .sess_get_index = tcm_loop_sess_get_index, + .write_pending = tcm_loop_write_pending, + .write_pending_status = tcm_loop_write_pending_status, + .set_default_node_attributes = tcm_loop_set_default_node_attributes, + .get_task_tag = tcm_loop_get_task_tag, + .get_cmd_state = tcm_loop_get_cmd_state, + .queue_data_in = tcm_loop_queue_data_in, + .queue_status = tcm_loop_queue_status, + .queue_tm_rsp = tcm_loop_queue_tm_rsp, + .aborted_task = tcm_loop_aborted_task, + .fabric_make_wwn = tcm_loop_make_scsi_hba, + .fabric_drop_wwn = tcm_loop_drop_scsi_hba, + .fabric_make_tpg = tcm_loop_make_naa_tpg, + .fabric_drop_tpg = tcm_loop_drop_naa_tpg, + .fabric_post_link = tcm_loop_port_link, + .fabric_pre_unlink = tcm_loop_port_unlink, + .tfc_wwn_attrs = tcm_loop_wwn_attrs, + .tfc_tpg_base_attrs = tcm_loop_tpg_attrs, + .tfc_tpg_attrib_attrs = tcm_loop_tpg_attrib_attrs, +}; static int __init tcm_loop_fabric_init(void) { @@ -1482,7 +1450,7 @@ static int __init tcm_loop_fabric_init(void) if (ret) goto out_destroy_cache; - ret = tcm_loop_register_configfs(); + ret = target_register_template(&loop_ops); if (ret) goto out_release_core_bus; @@ -1500,7 +1468,7 @@ out: static void __exit tcm_loop_fabric_exit(void) { - tcm_loop_deregister_configfs(); + target_unregister_template(&loop_ops); tcm_loop_release_core_bus(); kmem_cache_destroy(tcm_loop_cmd_cache); destroy_workqueue(tcm_loop_workqueue); diff --git a/drivers/target/loopback/tcm_loop.h b/drivers/target/loopback/tcm_loop.h index 6ae49f272ba6..1e72ff77cac9 100644 --- a/drivers/target/loopback/tcm_loop.h +++ b/drivers/target/loopback/tcm_loop.h @@ -43,6 +43,7 @@ struct tcm_loop_nacl { struct tcm_loop_tpg { unsigned short tl_tpgt; unsigned short tl_transport_status; + enum target_prot_type tl_fabric_prot_type; atomic_t tl_tpg_port_count; struct se_portal_group tl_se_tpg; struct tcm_loop_hba *tl_hba; diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c index 9512af6a8114..18b0f9703ff2 100644 --- a/drivers/target/sbp/sbp_target.c +++ b/drivers/target/sbp/sbp_target.c @@ -42,8 +42,7 @@ #include "sbp_target.h" -/* Local pointer to allocated TCM configfs fabric module */ -static struct target_fabric_configfs *sbp_fabric_configfs; +static const struct target_core_fabric_ops sbp_ops; /* FireWire address region for management and command block address handlers */ static const struct fw_address_region sbp_register_region = { @@ -2215,8 +2214,7 @@ static struct se_portal_group *sbp_make_tpg( goto out_free_tpg; } - ret = core_tpg_register(&sbp_fabric_configfs->tf_ops, wwn, - &tpg->se_tpg, (void *)tpg, + ret = core_tpg_register(&sbp_ops, wwn, &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL); if (ret < 0) goto out_unreg_mgt_agt; @@ -2503,7 +2501,9 @@ static struct configfs_attribute *sbp_tpg_attrib_attrs[] = { NULL, }; -static struct target_core_fabric_ops sbp_ops = { +static const struct target_core_fabric_ops sbp_ops = { + .module = THIS_MODULE, + .name = "sbp", .get_fabric_name = sbp_get_fabric_name, .get_fabric_proto_ident = sbp_get_fabric_proto_ident, .tpg_get_wwn = sbp_get_fabric_wwn, @@ -2544,68 +2544,20 @@ static struct target_core_fabric_ops sbp_ops = { .fabric_drop_np = NULL, .fabric_make_nodeacl = sbp_make_nodeacl, .fabric_drop_nodeacl = sbp_drop_nodeacl, -}; - -static int sbp_register_configfs(void) -{ - struct target_fabric_configfs *fabric; - int ret; - - fabric = target_fabric_configfs_init(THIS_MODULE, "sbp"); - if (IS_ERR(fabric)) { - pr_err("target_fabric_configfs_init() failed\n"); - return PTR_ERR(fabric); - } - - fabric->tf_ops = sbp_ops; - - /* - * Setup default attribute lists for various fabric->tf_cit_tmpl - */ - fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = sbp_wwn_attrs; - fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = sbp_tpg_base_attrs; - fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = sbp_tpg_attrib_attrs; - fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL; - - ret = target_fabric_configfs_register(fabric); - if (ret < 0) { - pr_err("target_fabric_configfs_register() failed for SBP\n"); - return ret; - } - sbp_fabric_configfs = fabric; - - return 0; -}; - -static void sbp_deregister_configfs(void) -{ - if (!sbp_fabric_configfs) - return; - - target_fabric_configfs_deregister(sbp_fabric_configfs); - sbp_fabric_configfs = NULL; + .tfc_wwn_attrs = sbp_wwn_attrs, + .tfc_tpg_base_attrs = sbp_tpg_base_attrs, + .tfc_tpg_attrib_attrs = sbp_tpg_attrib_attrs, }; static int __init sbp_init(void) { - int ret; - - ret = sbp_register_configfs(); - if (ret < 0) - return ret; - - return 0; + return target_register_template(&sbp_ops); }; static void __exit sbp_exit(void) { - sbp_deregister_configfs(); + target_unregister_template(&sbp_ops); }; MODULE_DESCRIPTION("FireWire SBP fabric driver"); diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 75d89adfccc0..ddaf76a4ac2a 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -142,8 +142,8 @@ static struct config_group *target_core_register_fabric( tf = target_core_get_fabric(name); if (!tf) { - pr_err("target_core_register_fabric() trying autoload for %s\n", - name); + pr_debug("target_core_register_fabric() trying autoload for %s\n", + name); /* * Below are some hardcoded request_module() calls to automatically @@ -165,8 +165,8 @@ static struct config_group *target_core_register_fabric( */ ret = request_module("iscsi_target_mod"); if (ret < 0) { - pr_err("request_module() failed for" - " iscsi_target_mod.ko: %d\n", ret); + pr_debug("request_module() failed for" + " iscsi_target_mod.ko: %d\n", ret); return ERR_PTR(-EINVAL); } } else if (!strncmp(name, "loopback", 8)) { @@ -178,8 +178,8 @@ static struct config_group *target_core_register_fabric( */ ret = request_module("tcm_loop"); if (ret < 0) { - pr_err("request_module() failed for" - " tcm_loop.ko: %d\n", ret); + pr_debug("request_module() failed for" + " tcm_loop.ko: %d\n", ret); return ERR_PTR(-EINVAL); } } @@ -188,8 +188,8 @@ static struct config_group *target_core_register_fabric( } if (!tf) { - pr_err("target_core_get_fabric() failed for %s\n", - name); + pr_debug("target_core_get_fabric() failed for %s\n", + name); return ERR_PTR(-EINVAL); } pr_debug("Target_Core_ConfigFS: REGISTER -> Located fabric:" @@ -300,81 +300,17 @@ struct configfs_subsystem *target_core_subsystem[] = { // Start functions called by external Target Fabrics Modules //############################################################################*/ -/* - * First function called by fabric modules to: - * - * 1) Allocate a struct target_fabric_configfs and save the *fabric_cit pointer. - * 2) Add struct target_fabric_configfs to g_tf_list - * 3) Return struct target_fabric_configfs to fabric module to be passed - * into target_fabric_configfs_register(). - */ -struct target_fabric_configfs *target_fabric_configfs_init( - struct module *fabric_mod, - const char *name) +static int target_fabric_tf_ops_check(const struct target_core_fabric_ops *tfo) { - struct target_fabric_configfs *tf; - - if (!(name)) { - pr_err("Unable to locate passed fabric name\n"); - return ERR_PTR(-EINVAL); + if (!tfo->name) { + pr_err("Missing tfo->name\n"); + return -EINVAL; } - if (strlen(name) >= TARGET_FABRIC_NAME_SIZE) { + if (strlen(tfo->name) >= TARGET_FABRIC_NAME_SIZE) { pr_err("Passed name: %s exceeds TARGET_FABRIC" - "_NAME_SIZE\n", name); - return ERR_PTR(-EINVAL); + "_NAME_SIZE\n", tfo->name); + return -EINVAL; } - - tf = kzalloc(sizeof(struct target_fabric_configfs), GFP_KERNEL); - if (!tf) - return ERR_PTR(-ENOMEM); - - INIT_LIST_HEAD(&tf->tf_list); - atomic_set(&tf->tf_access_cnt, 0); - /* - * Setup the default generic struct config_item_type's (cits) in - * struct target_fabric_configfs->tf_cit_tmpl - */ - tf->tf_module = fabric_mod; - target_fabric_setup_cits(tf); - - tf->tf_subsys = target_core_subsystem[0]; - snprintf(tf->tf_name, TARGET_FABRIC_NAME_SIZE, "%s", name); - - mutex_lock(&g_tf_lock); - list_add_tail(&tf->tf_list, &g_tf_list); - mutex_unlock(&g_tf_lock); - - pr_debug("<<<<<<<<<<<<<<<<<<<<<< BEGIN FABRIC API >>>>>>>>" - ">>>>>>>>>>>>>>\n"); - pr_debug("Initialized struct target_fabric_configfs: %p for" - " %s\n", tf, tf->tf_name); - return tf; -} -EXPORT_SYMBOL(target_fabric_configfs_init); - -/* - * Called by fabric plugins after FAILED target_fabric_configfs_register() call. - */ -void target_fabric_configfs_free( - struct target_fabric_configfs *tf) -{ - mutex_lock(&g_tf_lock); - list_del(&tf->tf_list); - mutex_unlock(&g_tf_lock); - - kfree(tf); -} -EXPORT_SYMBOL(target_fabric_configfs_free); - -/* - * Perform a sanity check of the passed tf->tf_ops before completing - * TCM fabric module registration. - */ -static int target_fabric_tf_ops_check( - struct target_fabric_configfs *tf) -{ - struct target_core_fabric_ops *tfo = &tf->tf_ops; - if (!tfo->get_fabric_name) { pr_err("Missing tfo->get_fabric_name()\n"); return -EINVAL; @@ -508,77 +444,59 @@ static int target_fabric_tf_ops_check( return 0; } -/* - * Called 2nd from fabric module with returned parameter of - * struct target_fabric_configfs * from target_fabric_configfs_init(). - * - * Upon a successful registration, the new fabric's struct config_item is - * return. Also, a pointer to this struct is set in the passed - * struct target_fabric_configfs. - */ -int target_fabric_configfs_register( - struct target_fabric_configfs *tf) +int target_register_template(const struct target_core_fabric_ops *fo) { + struct target_fabric_configfs *tf; int ret; + ret = target_fabric_tf_ops_check(fo); + if (ret) + return ret; + + tf = kzalloc(sizeof(struct target_fabric_configfs), GFP_KERNEL); if (!tf) { - pr_err("Unable to locate target_fabric_configfs" - " pointer\n"); - return -EINVAL; - } - if (!tf->tf_subsys) { - pr_err("Unable to target struct config_subsystem" - " pointer\n"); - return -EINVAL; + pr_err("%s: could not allocate memory!\n", __func__); + return -ENOMEM; } - ret = target_fabric_tf_ops_check(tf); - if (ret < 0) - return ret; - pr_debug("<<<<<<<<<<<<<<<<<<<<<< END FABRIC API >>>>>>>>>>>>" - ">>>>>>>>>>\n"); + INIT_LIST_HEAD(&tf->tf_list); + atomic_set(&tf->tf_access_cnt, 0); + + /* + * Setup the default generic struct config_item_type's (cits) in + * struct target_fabric_configfs->tf_cit_tmpl + */ + tf->tf_module = fo->module; + tf->tf_subsys = target_core_subsystem[0]; + snprintf(tf->tf_name, TARGET_FABRIC_NAME_SIZE, "%s", fo->name); + + tf->tf_ops = *fo; + target_fabric_setup_cits(tf); + + mutex_lock(&g_tf_lock); + list_add_tail(&tf->tf_list, &g_tf_list); + mutex_unlock(&g_tf_lock); + return 0; } -EXPORT_SYMBOL(target_fabric_configfs_register); +EXPORT_SYMBOL(target_register_template); -void target_fabric_configfs_deregister( - struct target_fabric_configfs *tf) +void target_unregister_template(const struct target_core_fabric_ops *fo) { - struct configfs_subsystem *su; + struct target_fabric_configfs *t; - if (!tf) { - pr_err("Unable to locate passed target_fabric_" - "configfs\n"); - return; - } - su = tf->tf_subsys; - if (!su) { - pr_err("Unable to locate passed tf->tf_subsys" - " pointer\n"); - return; - } - pr_debug("<<<<<<<<<<<<<<<<<<<<<< BEGIN FABRIC API >>>>>>>>>>" - ">>>>>>>>>>>>\n"); mutex_lock(&g_tf_lock); - if (atomic_read(&tf->tf_access_cnt)) { - mutex_unlock(&g_tf_lock); - pr_err("Non zero tf->tf_access_cnt for fabric %s\n", - tf->tf_name); - BUG(); + list_for_each_entry(t, &g_tf_list, tf_list) { + if (!strcmp(t->tf_name, fo->name)) { + BUG_ON(atomic_read(&t->tf_access_cnt)); + list_del(&t->tf_list); + kfree(t); + break; + } } - list_del(&tf->tf_list); mutex_unlock(&g_tf_lock); - - pr_debug("Target_Core_ConfigFS: DEREGISTER -> Releasing tf:" - " %s\n", tf->tf_name); - tf->tf_module = NULL; - tf->tf_subsys = NULL; - kfree(tf); - - pr_debug("<<<<<<<<<<<<<<<<<<<<<< END FABRIC API >>>>>>>>>>>>>>>>>" - ">>>>>\n"); } -EXPORT_SYMBOL(target_fabric_configfs_deregister); +EXPORT_SYMBOL(target_unregister_template); /*############################################################################## // Stop functions called by external Target Fabrics Modules @@ -945,7 +863,7 @@ static ssize_t target_core_dev_pr_show_attr_res_pr_holder_tg_port( struct se_lun *lun; struct se_portal_group *se_tpg; struct t10_pr_registration *pr_reg; - struct target_core_fabric_ops *tfo; + const struct target_core_fabric_ops *tfo; ssize_t len = 0; spin_lock(&dev->dev_reservation_lock); @@ -979,7 +897,7 @@ SE_DEV_PR_ATTR_RO(res_pr_holder_tg_port); static ssize_t target_core_dev_pr_show_attr_res_pr_registered_i_pts( struct se_device *dev, char *page) { - struct target_core_fabric_ops *tfo; + const struct target_core_fabric_ops *tfo; struct t10_pr_registration *pr_reg; unsigned char buf[384]; char i_buf[PR_REG_ISID_ID_LEN]; diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 0c3f90130b7d..1f7886bb16bf 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -56,6 +56,20 @@ static void target_fabric_setup_##_name##_cit(struct target_fabric_configfs *tf) pr_debug("Setup generic %s\n", __stringify(_name)); \ } +#define TF_CIT_SETUP_DRV(_name, _item_ops, _group_ops) \ +static void target_fabric_setup_##_name##_cit(struct target_fabric_configfs *tf) \ +{ \ + struct target_fabric_configfs_template *tfc = &tf->tf_cit_tmpl; \ + struct config_item_type *cit = &tfc->tfc_##_name##_cit; \ + struct configfs_attribute **attrs = tf->tf_ops.tfc_##_name##_attrs; \ + \ + cit->ct_item_ops = _item_ops; \ + cit->ct_group_ops = _group_ops; \ + cit->ct_attrs = attrs; \ + cit->ct_owner = tf->tf_module; \ + pr_debug("Setup generic %s\n", __stringify(_name)); \ +} + /* Start of tfc_tpg_mappedlun_cit */ static int target_fabric_mappedlun_link( @@ -278,7 +292,7 @@ static struct configfs_item_operations target_fabric_nacl_attrib_item_ops = { .store_attribute = target_fabric_nacl_attrib_attr_store, }; -TF_CIT_SETUP(tpg_nacl_attrib, &target_fabric_nacl_attrib_item_ops, NULL, NULL); +TF_CIT_SETUP_DRV(tpg_nacl_attrib, &target_fabric_nacl_attrib_item_ops, NULL); /* End of tfc_tpg_nacl_attrib_cit */ @@ -291,7 +305,7 @@ static struct configfs_item_operations target_fabric_nacl_auth_item_ops = { .store_attribute = target_fabric_nacl_auth_attr_store, }; -TF_CIT_SETUP(tpg_nacl_auth, &target_fabric_nacl_auth_item_ops, NULL, NULL); +TF_CIT_SETUP_DRV(tpg_nacl_auth, &target_fabric_nacl_auth_item_ops, NULL); /* End of tfc_tpg_nacl_auth_cit */ @@ -304,7 +318,7 @@ static struct configfs_item_operations target_fabric_nacl_param_item_ops = { .store_attribute = target_fabric_nacl_param_attr_store, }; -TF_CIT_SETUP(tpg_nacl_param, &target_fabric_nacl_param_item_ops, NULL, NULL); +TF_CIT_SETUP_DRV(tpg_nacl_param, &target_fabric_nacl_param_item_ops, NULL); /* End of tfc_tpg_nacl_param_cit */ @@ -461,8 +475,8 @@ static struct configfs_group_operations target_fabric_nacl_base_group_ops = { .drop_item = target_fabric_drop_mappedlun, }; -TF_CIT_SETUP(tpg_nacl_base, &target_fabric_nacl_base_item_ops, - &target_fabric_nacl_base_group_ops, NULL); +TF_CIT_SETUP_DRV(tpg_nacl_base, &target_fabric_nacl_base_item_ops, + &target_fabric_nacl_base_group_ops); /* End of tfc_tpg_nacl_base_cit */ @@ -570,7 +584,7 @@ static struct configfs_item_operations target_fabric_np_base_item_ops = { .store_attribute = target_fabric_np_base_attr_store, }; -TF_CIT_SETUP(tpg_np_base, &target_fabric_np_base_item_ops, NULL, NULL); +TF_CIT_SETUP_DRV(tpg_np_base, &target_fabric_np_base_item_ops, NULL); /* End of tfc_tpg_np_base_cit */ @@ -966,7 +980,7 @@ static struct configfs_item_operations target_fabric_tpg_attrib_item_ops = { .store_attribute = target_fabric_tpg_attrib_attr_store, }; -TF_CIT_SETUP(tpg_attrib, &target_fabric_tpg_attrib_item_ops, NULL, NULL); +TF_CIT_SETUP_DRV(tpg_attrib, &target_fabric_tpg_attrib_item_ops, NULL); /* End of tfc_tpg_attrib_cit */ @@ -979,7 +993,7 @@ static struct configfs_item_operations target_fabric_tpg_auth_item_ops = { .store_attribute = target_fabric_tpg_auth_attr_store, }; -TF_CIT_SETUP(tpg_auth, &target_fabric_tpg_auth_item_ops, NULL, NULL); +TF_CIT_SETUP_DRV(tpg_auth, &target_fabric_tpg_auth_item_ops, NULL); /* End of tfc_tpg_attrib_cit */ @@ -992,7 +1006,7 @@ static struct configfs_item_operations target_fabric_tpg_param_item_ops = { .store_attribute = target_fabric_tpg_param_attr_store, }; -TF_CIT_SETUP(tpg_param, &target_fabric_tpg_param_item_ops, NULL, NULL); +TF_CIT_SETUP_DRV(tpg_param, &target_fabric_tpg_param_item_ops, NULL); /* End of tfc_tpg_param_cit */ @@ -1018,7 +1032,7 @@ static struct configfs_item_operations target_fabric_tpg_base_item_ops = { .store_attribute = target_fabric_tpg_attr_store, }; -TF_CIT_SETUP(tpg_base, &target_fabric_tpg_base_item_ops, NULL, NULL); +TF_CIT_SETUP_DRV(tpg_base, &target_fabric_tpg_base_item_ops, NULL); /* End of tfc_tpg_base_cit */ @@ -1192,7 +1206,7 @@ static struct configfs_item_operations target_fabric_wwn_item_ops = { .store_attribute = target_fabric_wwn_attr_store, }; -TF_CIT_SETUP(wwn, &target_fabric_wwn_item_ops, &target_fabric_wwn_group_ops, NULL); +TF_CIT_SETUP_DRV(wwn, &target_fabric_wwn_item_ops, &target_fabric_wwn_group_ops); /* End of tfc_wwn_cit */ @@ -1206,7 +1220,7 @@ static struct configfs_item_operations target_fabric_discovery_item_ops = { .store_attribute = target_fabric_discovery_attr_store, }; -TF_CIT_SETUP(discovery, &target_fabric_discovery_item_ops, NULL, NULL); +TF_CIT_SETUP_DRV(discovery, &target_fabric_discovery_item_ops, NULL); /* End of tfc_discovery_cit */ diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 44620fb6bd45..f7e6e51aed36 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -264,40 +264,32 @@ static int fd_do_prot_rw(struct se_cmd *cmd, struct fd_prot *fd_prot, struct se_device *se_dev = cmd->se_dev; struct fd_dev *dev = FD_DEV(se_dev); struct file *prot_fd = dev->fd_prot_file; - struct scatterlist *sg; loff_t pos = (cmd->t_task_lba * se_dev->prot_length); unsigned char *buf; - u32 prot_size, len, size; - int rc, ret = 1, i; + u32 prot_size; + int rc, ret = 1; prot_size = (cmd->data_length / se_dev->dev_attrib.block_size) * se_dev->prot_length; if (!is_write) { - fd_prot->prot_buf = vzalloc(prot_size); + fd_prot->prot_buf = kzalloc(prot_size, GFP_KERNEL); if (!fd_prot->prot_buf) { pr_err("Unable to allocate fd_prot->prot_buf\n"); return -ENOMEM; } buf = fd_prot->prot_buf; - fd_prot->prot_sg_nents = cmd->t_prot_nents; - fd_prot->prot_sg = kzalloc(sizeof(struct scatterlist) * - fd_prot->prot_sg_nents, GFP_KERNEL); + fd_prot->prot_sg_nents = 1; + fd_prot->prot_sg = kzalloc(sizeof(struct scatterlist), + GFP_KERNEL); if (!fd_prot->prot_sg) { pr_err("Unable to allocate fd_prot->prot_sg\n"); - vfree(fd_prot->prot_buf); + kfree(fd_prot->prot_buf); return -ENOMEM; } - size = prot_size; - - for_each_sg(fd_prot->prot_sg, sg, fd_prot->prot_sg_nents, i) { - - len = min_t(u32, PAGE_SIZE, size); - sg_set_buf(sg, buf, len); - size -= len; - buf += len; - } + sg_init_table(fd_prot->prot_sg, fd_prot->prot_sg_nents); + sg_set_buf(fd_prot->prot_sg, buf, prot_size); } if (is_write) { @@ -318,7 +310,7 @@ static int fd_do_prot_rw(struct se_cmd *cmd, struct fd_prot *fd_prot, if (is_write || ret < 0) { kfree(fd_prot->prot_sg); - vfree(fd_prot->prot_buf); + kfree(fd_prot->prot_buf); } return ret; @@ -331,36 +323,33 @@ static int fd_do_rw(struct se_cmd *cmd, struct scatterlist *sgl, struct fd_dev *dev = FD_DEV(se_dev); struct file *fd = dev->fd_file; struct scatterlist *sg; - struct iovec *iov; - mm_segment_t old_fs; + struct iov_iter iter; + struct bio_vec *bvec; + ssize_t len = 0; loff_t pos = (cmd->t_task_lba * se_dev->dev_attrib.block_size); int ret = 0, i; - iov = kzalloc(sizeof(struct iovec) * sgl_nents, GFP_KERNEL); - if (!iov) { + bvec = kcalloc(sgl_nents, sizeof(struct bio_vec), GFP_KERNEL); + if (!bvec) { pr_err("Unable to allocate fd_do_readv iov[]\n"); return -ENOMEM; } for_each_sg(sgl, sg, sgl_nents, i) { - iov[i].iov_len = sg->length; - iov[i].iov_base = kmap(sg_page(sg)) + sg->offset; - } + bvec[i].bv_page = sg_page(sg); + bvec[i].bv_len = sg->length; + bvec[i].bv_offset = sg->offset; - old_fs = get_fs(); - set_fs(get_ds()); + len += sg->length; + } + iov_iter_bvec(&iter, ITER_BVEC, bvec, sgl_nents, len); if (is_write) - ret = vfs_writev(fd, &iov[0], sgl_nents, &pos); + ret = vfs_iter_write(fd, &iter, &pos); else - ret = vfs_readv(fd, &iov[0], sgl_nents, &pos); - - set_fs(old_fs); - - for_each_sg(sgl, sg, sgl_nents, i) - kunmap(sg_page(sg)); + ret = vfs_iter_read(fd, &iter, &pos); - kfree(iov); + kfree(bvec); if (is_write) { if (ret < 0 || ret != cmd->data_length) { @@ -436,59 +425,17 @@ fd_execute_sync_cache(struct se_cmd *cmd) return 0; } -static unsigned char * -fd_setup_write_same_buf(struct se_cmd *cmd, struct scatterlist *sg, - unsigned int len) -{ - struct se_device *se_dev = cmd->se_dev; - unsigned int block_size = se_dev->dev_attrib.block_size; - unsigned int i = 0, end; - unsigned char *buf, *p, *kmap_buf; - - buf = kzalloc(min_t(unsigned int, len, PAGE_SIZE), GFP_KERNEL); - if (!buf) { - pr_err("Unable to allocate fd_execute_write_same buf\n"); - return NULL; - } - - kmap_buf = kmap(sg_page(sg)) + sg->offset; - if (!kmap_buf) { - pr_err("kmap() failed in fd_setup_write_same\n"); - kfree(buf); - return NULL; - } - /* - * Fill local *buf to contain multiple WRITE_SAME blocks up to - * min(len, PAGE_SIZE) - */ - p = buf; - end = min_t(unsigned int, len, PAGE_SIZE); - - while (i < end) { - memcpy(p, kmap_buf, block_size); - - i += block_size; - p += block_size; - } - kunmap(sg_page(sg)); - - return buf; -} - static sense_reason_t fd_execute_write_same(struct se_cmd *cmd) { struct se_device *se_dev = cmd->se_dev; struct fd_dev *fd_dev = FD_DEV(se_dev); - struct file *f = fd_dev->fd_file; - struct scatterlist *sg; - struct iovec *iov; - mm_segment_t old_fs; - sector_t nolb = sbc_get_write_same_sectors(cmd); loff_t pos = cmd->t_task_lba * se_dev->dev_attrib.block_size; - unsigned int len, len_tmp, iov_num; - int i, rc; - unsigned char *buf; + sector_t nolb = sbc_get_write_same_sectors(cmd); + struct iov_iter iter; + struct bio_vec *bvec; + unsigned int len = 0, i; + ssize_t ret; if (!nolb) { target_complete_cmd(cmd, SAM_STAT_GOOD); @@ -499,56 +446,92 @@ fd_execute_write_same(struct se_cmd *cmd) " backends not supported\n"); return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } - sg = &cmd->t_data_sg[0]; if (cmd->t_data_nents > 1 || - sg->length != cmd->se_dev->dev_attrib.block_size) { + cmd->t_data_sg[0].length != cmd->se_dev->dev_attrib.block_size) { pr_err("WRITE_SAME: Illegal SGL t_data_nents: %u length: %u" - " block_size: %u\n", cmd->t_data_nents, sg->length, + " block_size: %u\n", + cmd->t_data_nents, + cmd->t_data_sg[0].length, cmd->se_dev->dev_attrib.block_size); return TCM_INVALID_CDB_FIELD; } - len = len_tmp = nolb * se_dev->dev_attrib.block_size; - iov_num = DIV_ROUND_UP(len, PAGE_SIZE); - - buf = fd_setup_write_same_buf(cmd, sg, len); - if (!buf) + bvec = kcalloc(nolb, sizeof(struct bio_vec), GFP_KERNEL); + if (!bvec) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - iov = vzalloc(sizeof(struct iovec) * iov_num); - if (!iov) { - pr_err("Unable to allocate fd_execute_write_same iovecs\n"); - kfree(buf); + for (i = 0; i < nolb; i++) { + bvec[i].bv_page = sg_page(&cmd->t_data_sg[0]); + bvec[i].bv_len = cmd->t_data_sg[0].length; + bvec[i].bv_offset = cmd->t_data_sg[0].offset; + + len += se_dev->dev_attrib.block_size; + } + + iov_iter_bvec(&iter, ITER_BVEC, bvec, nolb, len); + ret = vfs_iter_write(fd_dev->fd_file, &iter, &pos); + + kfree(bvec); + if (ret < 0 || ret != len) { + pr_err("vfs_iter_write() returned %zd for write same\n", ret); return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } - /* - * Map the single fabric received scatterlist block now populated - * in *buf into each iovec for I/O submission. - */ - for (i = 0; i < iov_num; i++) { - iov[i].iov_base = buf; - iov[i].iov_len = min_t(unsigned int, len_tmp, PAGE_SIZE); - len_tmp -= iov[i].iov_len; + + target_complete_cmd(cmd, SAM_STAT_GOOD); + return 0; +} + +static int +fd_do_prot_fill(struct se_device *se_dev, sector_t lba, sector_t nolb, + void *buf, size_t bufsize) +{ + struct fd_dev *fd_dev = FD_DEV(se_dev); + struct file *prot_fd = fd_dev->fd_prot_file; + sector_t prot_length, prot; + loff_t pos = lba * se_dev->prot_length; + + if (!prot_fd) { + pr_err("Unable to locate fd_dev->fd_prot_file\n"); + return -ENODEV; } - old_fs = get_fs(); - set_fs(get_ds()); - rc = vfs_writev(f, &iov[0], iov_num, &pos); - set_fs(old_fs); + prot_length = nolb * se_dev->prot_length; - vfree(iov); - kfree(buf); + for (prot = 0; prot < prot_length;) { + sector_t len = min_t(sector_t, bufsize, prot_length - prot); + ssize_t ret = kernel_write(prot_fd, buf, len, pos + prot); - if (rc < 0 || rc != len) { - pr_err("vfs_writev() returned %d for write same\n", rc); - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + if (ret != len) { + pr_err("vfs_write to prot file failed: %zd\n", ret); + return ret < 0 ? ret : -ENODEV; + } + prot += ret; } - target_complete_cmd(cmd, SAM_STAT_GOOD); return 0; } +static int +fd_do_prot_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) +{ + void *buf; + int rc; + + buf = (void *)__get_free_page(GFP_KERNEL); + if (!buf) { + pr_err("Unable to allocate FILEIO prot buf\n"); + return -ENOMEM; + } + memset(buf, 0xff, PAGE_SIZE); + + rc = fd_do_prot_fill(cmd->se_dev, lba, nolb, buf, PAGE_SIZE); + + free_page((unsigned long)buf); + + return rc; +} + static sense_reason_t fd_do_unmap(struct se_cmd *cmd, void *priv, sector_t lba, sector_t nolb) { @@ -556,6 +539,12 @@ fd_do_unmap(struct se_cmd *cmd, void *priv, sector_t lba, sector_t nolb) struct inode *inode = file->f_mapping->host; int ret; + if (cmd->se_dev->dev_attrib.pi_prot_type) { + ret = fd_do_prot_unmap(cmd, lba, nolb); + if (ret) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } + if (S_ISBLK(inode->i_mode)) { /* The backend is block device, use discard */ struct block_device *bdev = inode->i_bdev; @@ -595,7 +584,7 @@ fd_execute_write_same_unmap(struct se_cmd *cmd) struct file *file = fd_dev->fd_file; sector_t lba = cmd->t_task_lba; sector_t nolb = sbc_get_write_same_sectors(cmd); - int ret; + sense_reason_t ret; if (!nolb) { target_complete_cmd(cmd, SAM_STAT_GOOD); @@ -643,7 +632,7 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, if (data_direction == DMA_FROM_DEVICE) { memset(&fd_prot, 0, sizeof(struct fd_prot)); - if (cmd->prot_type) { + if (cmd->prot_type && dev->dev_attrib.pi_prot_type) { ret = fd_do_prot_rw(cmd, &fd_prot, false); if (ret < 0) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -651,23 +640,23 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, ret = fd_do_rw(cmd, sgl, sgl_nents, 0); - if (ret > 0 && cmd->prot_type) { + if (ret > 0 && cmd->prot_type && dev->dev_attrib.pi_prot_type) { u32 sectors = cmd->data_length / dev->dev_attrib.block_size; rc = sbc_dif_verify_read(cmd, cmd->t_task_lba, sectors, 0, fd_prot.prot_sg, 0); if (rc) { kfree(fd_prot.prot_sg); - vfree(fd_prot.prot_buf); + kfree(fd_prot.prot_buf); return rc; } kfree(fd_prot.prot_sg); - vfree(fd_prot.prot_buf); + kfree(fd_prot.prot_buf); } } else { memset(&fd_prot, 0, sizeof(struct fd_prot)); - if (cmd->prot_type) { + if (cmd->prot_type && dev->dev_attrib.pi_prot_type) { u32 sectors = cmd->data_length / dev->dev_attrib.block_size; ret = fd_do_prot_rw(cmd, &fd_prot, false); @@ -678,7 +667,7 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, 0, fd_prot.prot_sg, 0); if (rc) { kfree(fd_prot.prot_sg); - vfree(fd_prot.prot_buf); + kfree(fd_prot.prot_buf); return rc; } } @@ -705,7 +694,7 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, vfs_fsync_range(fd_dev->fd_file, start, end, 1); } - if (ret > 0 && cmd->prot_type) { + if (ret > 0 && cmd->prot_type && dev->dev_attrib.pi_prot_type) { ret = fd_do_prot_rw(cmd, &fd_prot, true); if (ret < 0) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; @@ -714,7 +703,7 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, if (ret < 0) { kfree(fd_prot.prot_sg); - vfree(fd_prot.prot_buf); + kfree(fd_prot.prot_buf); return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } @@ -878,48 +867,28 @@ static int fd_init_prot(struct se_device *dev) static int fd_format_prot(struct se_device *dev) { - struct fd_dev *fd_dev = FD_DEV(dev); - struct file *prot_fd = fd_dev->fd_prot_file; - sector_t prot_length, prot; unsigned char *buf; - loff_t pos = 0; int unit_size = FDBD_FORMAT_UNIT_SIZE * dev->dev_attrib.block_size; - int rc, ret = 0, size, len; + int ret; if (!dev->dev_attrib.pi_prot_type) { pr_err("Unable to format_prot while pi_prot_type == 0\n"); return -ENODEV; } - if (!prot_fd) { - pr_err("Unable to locate fd_dev->fd_prot_file\n"); - return -ENODEV; - } buf = vzalloc(unit_size); if (!buf) { pr_err("Unable to allocate FILEIO prot buf\n"); return -ENOMEM; } - prot_length = (dev->transport->get_blocks(dev) + 1) * dev->prot_length; - size = prot_length; pr_debug("Using FILEIO prot_length: %llu\n", - (unsigned long long)prot_length); + (unsigned long long)(dev->transport->get_blocks(dev) + 1) * + dev->prot_length); memset(buf, 0xff, unit_size); - for (prot = 0; prot < prot_length; prot += unit_size) { - len = min(unit_size, size); - rc = kernel_write(prot_fd, buf, len, pos); - if (rc != len) { - pr_err("vfs_write to prot file failed: %d\n", rc); - ret = -ENODEV; - goto out; - } - pos += len; - size -= len; - } - -out: + ret = fd_do_prot_fill(dev, 0, dev->transport->get_blocks(dev) + 1, + buf, unit_size); vfree(buf); return ret; } diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index d4a4b0fb444a..1b7947c2510f 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -444,7 +444,7 @@ iblock_execute_write_same_unmap(struct se_cmd *cmd) struct block_device *bdev = IBLOCK_DEV(cmd->se_dev)->ibd_bd; sector_t lba = cmd->t_task_lba; sector_t nolb = sbc_get_write_same_sectors(cmd); - int ret; + sense_reason_t ret; ret = iblock_do_unmap(cmd, bdev, lba, nolb); if (ret) @@ -774,7 +774,7 @@ iblock_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, sg_num--; } - if (cmd->prot_type) { + if (cmd->prot_type && dev->dev_attrib.pi_prot_type) { int rc = iblock_alloc_bip(cmd, bio_start); if (rc) goto fail_put_bios; diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 60381db90026..874a9bc988d8 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -4,7 +4,13 @@ /* target_core_alua.c */ extern struct t10_alua_lu_gp *default_lu_gp; +/* target_core_configfs.c */ +extern struct configfs_subsystem *target_core_subsystem[]; + /* target_core_device.c */ +extern struct mutex g_device_mutex; +extern struct list_head g_device_list; + struct se_dev_entry *core_get_se_deve_from_rtpi(struct se_node_acl *, u16); int core_free_device_list_for_node(struct se_node_acl *, struct se_portal_group *); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 2de6fb8cee8d..c1aa9655e96e 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -78,6 +78,22 @@ enum preempt_type { static void __core_scsi3_complete_pro_release(struct se_device *, struct se_node_acl *, struct t10_pr_registration *, int, int); +static int is_reservation_holder( + struct t10_pr_registration *pr_res_holder, + struct t10_pr_registration *pr_reg) +{ + int pr_res_type; + + if (pr_res_holder) { + pr_res_type = pr_res_holder->pr_res_type; + + return pr_res_holder == pr_reg || + pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG || + pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG; + } + return 0; +} + static sense_reason_t target_scsi2_reservation_check(struct se_cmd *cmd) { @@ -664,7 +680,7 @@ static struct t10_pr_registration *__core_scsi3_alloc_registration( struct se_dev_entry *deve_tmp; struct se_node_acl *nacl_tmp; struct se_port *port, *port_tmp; - struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo; + const struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo; struct t10_pr_registration *pr_reg, *pr_reg_atp, *pr_reg_tmp, *pr_reg_tmp_safe; int ret; /* @@ -963,7 +979,7 @@ int core_scsi3_check_aptpl_registration( } static void __core_scsi3_dump_registration( - struct target_core_fabric_ops *tfo, + const struct target_core_fabric_ops *tfo, struct se_device *dev, struct se_node_acl *nacl, struct t10_pr_registration *pr_reg, @@ -1004,7 +1020,7 @@ static void __core_scsi3_add_registration( enum register_type register_type, int register_move) { - struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo; + const struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo; struct t10_pr_registration *pr_reg_tmp, *pr_reg_tmp_safe; struct t10_reservation *pr_tmpl = &dev->t10_pr; @@ -1220,8 +1236,10 @@ static void __core_scsi3_free_registration( struct t10_pr_registration *pr_reg, struct list_head *preempt_and_abort_list, int dec_holders) + __releases(&pr_tmpl->registration_lock) + __acquires(&pr_tmpl->registration_lock) { - struct target_core_fabric_ops *tfo = + const struct target_core_fabric_ops *tfo = pr_reg->pr_reg_nacl->se_tpg->se_tpg_tfo; struct t10_reservation *pr_tmpl = &dev->t10_pr; char i_buf[PR_REG_ISID_ID_LEN]; @@ -1445,7 +1463,7 @@ core_scsi3_decode_spec_i_port( struct t10_pr_registration *pr_reg_tmp, *pr_reg_tmp_safe; LIST_HEAD(tid_dest_list); struct pr_transport_id_holder *tidh_new, *tidh, *tidh_tmp; - struct target_core_fabric_ops *tmp_tf_ops; + const struct target_core_fabric_ops *tmp_tf_ops; unsigned char *buf; unsigned char *ptr, *i_str = NULL, proto_ident, tmp_proto_ident; char *iport_ptr = NULL, i_buf[PR_REG_ISID_ID_LEN]; @@ -2287,7 +2305,6 @@ core_scsi3_pro_reserve(struct se_cmd *cmd, int type, int scope, u64 res_key) spin_lock(&dev->dev_reservation_lock); pr_res_holder = dev->dev_pr_res_holder; if (pr_res_holder) { - int pr_res_type = pr_res_holder->pr_res_type; /* * From spc4r17 Section 5.7.9: Reserving: * @@ -2298,9 +2315,7 @@ core_scsi3_pro_reserve(struct se_cmd *cmd, int type, int scope, u64 res_key) * the logical unit, then the command shall be completed with * RESERVATION CONFLICT status. */ - if ((pr_res_holder != pr_reg) && - (pr_res_type != PR_TYPE_WRITE_EXCLUSIVE_ALLREG) && - (pr_res_type != PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)) { + if (!is_reservation_holder(pr_res_holder, pr_reg)) { struct se_node_acl *pr_res_nacl = pr_res_holder->pr_reg_nacl; pr_err("SPC-3 PR: Attempted RESERVE from" " [%s]: %s while reservation already held by" @@ -2409,7 +2424,7 @@ static void __core_scsi3_complete_pro_release( int explicit, int unreg) { - struct target_core_fabric_ops *tfo = se_nacl->se_tpg->se_tpg_tfo; + const struct target_core_fabric_ops *tfo = se_nacl->se_tpg->se_tpg_tfo; char i_buf[PR_REG_ISID_ID_LEN]; int pr_res_type = 0, pr_res_scope = 0; @@ -2477,7 +2492,6 @@ core_scsi3_emulate_pro_release(struct se_cmd *cmd, int type, int scope, struct se_lun *se_lun = cmd->se_lun; struct t10_pr_registration *pr_reg, *pr_reg_p, *pr_res_holder; struct t10_reservation *pr_tmpl = &dev->t10_pr; - int all_reg = 0; sense_reason_t ret = 0; if (!se_sess || !se_lun) { @@ -2514,13 +2528,9 @@ core_scsi3_emulate_pro_release(struct se_cmd *cmd, int type, int scope, spin_unlock(&dev->dev_reservation_lock); goto out_put_pr_reg; } - if ((pr_res_holder->pr_res_type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG) || - (pr_res_holder->pr_res_type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG)) - all_reg = 1; - if ((all_reg == 0) && (pr_res_holder != pr_reg)) { + if (!is_reservation_holder(pr_res_holder, pr_reg)) { /* - * Non 'All Registrants' PR Type cases.. * Release request from a registered I_T nexus that is not a * persistent reservation holder. return GOOD status. */ @@ -2726,7 +2736,7 @@ static void __core_scsi3_complete_pro_preempt( enum preempt_type preempt_type) { struct se_node_acl *nacl = pr_reg->pr_reg_nacl; - struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo; + const struct target_core_fabric_ops *tfo = nacl->se_tpg->se_tpg_tfo; char i_buf[PR_REG_ISID_ID_LEN]; memset(i_buf, 0, PR_REG_ISID_ID_LEN); @@ -3111,7 +3121,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, struct se_node_acl *pr_res_nacl, *pr_reg_nacl, *dest_node_acl = NULL; struct se_port *se_port; struct se_portal_group *se_tpg, *dest_se_tpg = NULL; - struct target_core_fabric_ops *dest_tf_ops = NULL, *tf_ops; + const struct target_core_fabric_ops *dest_tf_ops = NULL, *tf_ops; struct t10_pr_registration *pr_reg, *pr_res_holder, *dest_pr_reg; struct t10_reservation *pr_tmpl = &dev->t10_pr; unsigned char *buf; @@ -3375,7 +3385,7 @@ after_iport_check: * From spc4r17 section 5.7.8 Table 50 -- * Register behaviors for a REGISTER AND MOVE service action */ - if (pr_res_holder != pr_reg) { + if (!is_reservation_holder(pr_res_holder, pr_reg)) { pr_warn("SPC-3 PR REGISTER_AND_MOVE: Calling I_T" " Nexus is not reservation holder\n"); spin_unlock(&dev->dev_reservation_lock); diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index 98e83ac5661b..a263bf5fab8d 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -139,10 +139,22 @@ static int rd_allocate_sgl_table(struct rd_dev *rd_dev, struct rd_dev_sg_table * unsigned char *p; while (total_sg_needed) { + unsigned int chain_entry = 0; + sg_per_table = (total_sg_needed > max_sg_per_table) ? max_sg_per_table : total_sg_needed; - sg = kzalloc(sg_per_table * sizeof(struct scatterlist), +#ifdef CONFIG_ARCH_HAS_SG_CHAIN + + /* + * Reserve extra element for chain entry + */ + if (sg_per_table < total_sg_needed) + chain_entry = 1; + +#endif /* CONFIG_ARCH_HAS_SG_CHAIN */ + + sg = kcalloc(sg_per_table + chain_entry, sizeof(*sg), GFP_KERNEL); if (!sg) { pr_err("Unable to allocate scatterlist array" @@ -150,7 +162,16 @@ static int rd_allocate_sgl_table(struct rd_dev *rd_dev, struct rd_dev_sg_table * return -ENOMEM; } - sg_init_table(sg, sg_per_table); + sg_init_table(sg, sg_per_table + chain_entry); + +#ifdef CONFIG_ARCH_HAS_SG_CHAIN + + if (i > 0) { + sg_chain(sg_table[i - 1].sg_table, + max_sg_per_table + 1, sg); + } + +#endif /* CONFIG_ARCH_HAS_SG_CHAIN */ sg_table[i].sg_table = sg; sg_table[i].rd_sg_count = sg_per_table; @@ -382,6 +403,76 @@ static struct rd_dev_sg_table *rd_get_prot_table(struct rd_dev *rd_dev, u32 page return NULL; } +typedef sense_reason_t (*dif_verify)(struct se_cmd *, sector_t, unsigned int, + unsigned int, struct scatterlist *, int); + +static sense_reason_t rd_do_prot_rw(struct se_cmd *cmd, dif_verify dif_verify) +{ + struct se_device *se_dev = cmd->se_dev; + struct rd_dev *dev = RD_DEV(se_dev); + struct rd_dev_sg_table *prot_table; + bool need_to_release = false; + struct scatterlist *prot_sg; + u32 sectors = cmd->data_length / se_dev->dev_attrib.block_size; + u32 prot_offset, prot_page; + u32 prot_npages __maybe_unused; + u64 tmp; + sense_reason_t rc = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + + tmp = cmd->t_task_lba * se_dev->prot_length; + prot_offset = do_div(tmp, PAGE_SIZE); + prot_page = tmp; + + prot_table = rd_get_prot_table(dev, prot_page); + if (!prot_table) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + + prot_sg = &prot_table->sg_table[prot_page - + prot_table->page_start_offset]; + +#ifndef CONFIG_ARCH_HAS_SG_CHAIN + + prot_npages = DIV_ROUND_UP(prot_offset + sectors * se_dev->prot_length, + PAGE_SIZE); + + /* + * Allocate temporaly contiguous scatterlist entries if prot pages + * straddles multiple scatterlist tables. + */ + if (prot_table->page_end_offset < prot_page + prot_npages - 1) { + int i; + + prot_sg = kcalloc(prot_npages, sizeof(*prot_sg), GFP_KERNEL); + if (!prot_sg) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + + need_to_release = true; + sg_init_table(prot_sg, prot_npages); + + for (i = 0; i < prot_npages; i++) { + if (prot_page + i > prot_table->page_end_offset) { + prot_table = rd_get_prot_table(dev, + prot_page + i); + if (!prot_table) { + kfree(prot_sg); + return rc; + } + sg_unmark_end(&prot_sg[i - 1]); + } + prot_sg[i] = prot_table->sg_table[prot_page + i - + prot_table->page_start_offset]; + } + } + +#endif /* !CONFIG_ARCH_HAS_SG_CHAIN */ + + rc = dif_verify(cmd, cmd->t_task_lba, sectors, 0, prot_sg, prot_offset); + if (need_to_release) + kfree(prot_sg); + + return rc; +} + static sense_reason_t rd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, enum dma_data_direction data_direction) @@ -419,24 +510,9 @@ rd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, data_direction == DMA_FROM_DEVICE ? "Read" : "Write", cmd->t_task_lba, rd_size, rd_page, rd_offset); - if (cmd->prot_type && data_direction == DMA_TO_DEVICE) { - struct rd_dev_sg_table *prot_table; - struct scatterlist *prot_sg; - u32 sectors = cmd->data_length / se_dev->dev_attrib.block_size; - u32 prot_offset, prot_page; - - tmp = cmd->t_task_lba * se_dev->prot_length; - prot_offset = do_div(tmp, PAGE_SIZE); - prot_page = tmp; - - prot_table = rd_get_prot_table(dev, prot_page); - if (!prot_table) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - - prot_sg = &prot_table->sg_table[prot_page - prot_table->page_start_offset]; - - rc = sbc_dif_verify_write(cmd, cmd->t_task_lba, sectors, 0, - prot_sg, prot_offset); + if (cmd->prot_type && se_dev->dev_attrib.pi_prot_type && + data_direction == DMA_TO_DEVICE) { + rc = rd_do_prot_rw(cmd, sbc_dif_verify_write); if (rc) return rc; } @@ -502,24 +578,9 @@ rd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, } sg_miter_stop(&m); - if (cmd->prot_type && data_direction == DMA_FROM_DEVICE) { - struct rd_dev_sg_table *prot_table; - struct scatterlist *prot_sg; - u32 sectors = cmd->data_length / se_dev->dev_attrib.block_size; - u32 prot_offset, prot_page; - - tmp = cmd->t_task_lba * se_dev->prot_length; - prot_offset = do_div(tmp, PAGE_SIZE); - prot_page = tmp; - - prot_table = rd_get_prot_table(dev, prot_page); - if (!prot_table) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - - prot_sg = &prot_table->sg_table[prot_page - prot_table->page_start_offset]; - - rc = sbc_dif_verify_read(cmd, cmd->t_task_lba, sectors, 0, - prot_sg, prot_offset); + if (cmd->prot_type && se_dev->dev_attrib.pi_prot_type && + data_direction == DMA_FROM_DEVICE) { + rc = rd_do_prot_rw(cmd, sbc_dif_verify_read); if (rc) return rc; } diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 3e7297411110..8855781ac653 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -93,6 +93,8 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; struct se_session *sess = cmd->se_sess; + int pi_prot_type = dev->dev_attrib.pi_prot_type; + unsigned char *rbuf; unsigned char buf[32]; unsigned long long blocks = dev->transport->get_blocks(dev); @@ -114,8 +116,15 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd) * Set P_TYPE and PROT_EN bits for DIF support */ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) { - if (dev->dev_attrib.pi_prot_type) - buf[12] = (dev->dev_attrib.pi_prot_type - 1) << 1 | 0x1; + /* + * Only override a device's pi_prot_type if no T10-PI is + * available, and sess_prot_type has been explicitly enabled. + */ + if (!pi_prot_type) + pi_prot_type = sess->sess_prot_type; + + if (pi_prot_type) + buf[12] = (pi_prot_type - 1) << 1 | 0x1; } if (dev->transport->get_lbppbe) @@ -312,7 +321,7 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char *flags, struct sbc_ops *o return 0; } -static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd) +static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success) { unsigned char *buf, *addr; struct scatterlist *sg; @@ -376,7 +385,7 @@ sbc_execute_rw(struct se_cmd *cmd) cmd->data_direction); } -static sense_reason_t compare_and_write_post(struct se_cmd *cmd) +static sense_reason_t compare_and_write_post(struct se_cmd *cmd, bool success) { struct se_device *dev = cmd->se_dev; @@ -399,7 +408,7 @@ static sense_reason_t compare_and_write_post(struct se_cmd *cmd) return TCM_NO_SENSE; } -static sense_reason_t compare_and_write_callback(struct se_cmd *cmd) +static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool success) { struct se_device *dev = cmd->se_dev; struct scatterlist *write_sg = NULL, *sg; @@ -414,11 +423,16 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd) /* * Handle early failure in transport_generic_request_failure(), - * which will not have taken ->caw_mutex yet.. + * which will not have taken ->caw_sem yet.. */ - if (!cmd->t_data_sg || !cmd->t_bidi_data_sg) + if (!success && (!cmd->t_data_sg || !cmd->t_bidi_data_sg)) return TCM_NO_SENSE; /* + * Handle special case for zero-length COMPARE_AND_WRITE + */ + if (!cmd->data_length) + goto out; + /* * Immediately exit + release dev->caw_sem if command has already * been failed with a non-zero SCSI status. */ @@ -581,12 +595,13 @@ sbc_compare_and_write(struct se_cmd *cmd) } static int -sbc_set_prot_op_checks(u8 protect, enum target_prot_type prot_type, +sbc_set_prot_op_checks(u8 protect, bool fabric_prot, enum target_prot_type prot_type, bool is_write, struct se_cmd *cmd) { if (is_write) { - cmd->prot_op = protect ? TARGET_PROT_DOUT_PASS : - TARGET_PROT_DOUT_INSERT; + cmd->prot_op = fabric_prot ? TARGET_PROT_DOUT_STRIP : + protect ? TARGET_PROT_DOUT_PASS : + TARGET_PROT_DOUT_INSERT; switch (protect) { case 0x0: case 0x3: @@ -610,8 +625,9 @@ sbc_set_prot_op_checks(u8 protect, enum target_prot_type prot_type, return -EINVAL; } } else { - cmd->prot_op = protect ? TARGET_PROT_DIN_PASS : - TARGET_PROT_DIN_STRIP; + cmd->prot_op = fabric_prot ? TARGET_PROT_DIN_INSERT : + protect ? TARGET_PROT_DIN_PASS : + TARGET_PROT_DIN_STRIP; switch (protect) { case 0x0: case 0x1: @@ -644,11 +660,15 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb, u32 sectors, bool is_write) { u8 protect = cdb[1] >> 5; + int sp_ops = cmd->se_sess->sup_prot_ops; + int pi_prot_type = dev->dev_attrib.pi_prot_type; + bool fabric_prot = false; if (!cmd->t_prot_sg || !cmd->t_prot_nents) { - if (protect && !dev->dev_attrib.pi_prot_type) { - pr_err("CDB contains protect bit, but device does not" - " advertise PROTECT=1 feature bit\n"); + if (unlikely(protect && + !dev->dev_attrib.pi_prot_type && !cmd->se_sess->sess_prot_type)) { + pr_err("CDB contains protect bit, but device + fabric does" + " not advertise PROTECT=1 feature bit\n"); return TCM_INVALID_CDB_FIELD; } if (cmd->prot_pto) @@ -669,15 +689,32 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb, cmd->reftag_seed = cmd->t_task_lba; break; case TARGET_DIF_TYPE0_PROT: + /* + * See if the fabric supports T10-PI, and the session has been + * configured to allow export PROTECT=1 feature bit with backend + * devices that don't support T10-PI. + */ + fabric_prot = is_write ? + !!(sp_ops & (TARGET_PROT_DOUT_PASS | TARGET_PROT_DOUT_STRIP)) : + !!(sp_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DIN_INSERT)); + + if (fabric_prot && cmd->se_sess->sess_prot_type) { + pi_prot_type = cmd->se_sess->sess_prot_type; + break; + } + if (!protect) + return TCM_NO_SENSE; + /* Fallthrough */ default: - return TCM_NO_SENSE; + pr_err("Unable to determine pi_prot_type for CDB: 0x%02x " + "PROTECT: 0x%02x\n", cdb[0], protect); + return TCM_INVALID_CDB_FIELD; } - if (sbc_set_prot_op_checks(protect, dev->dev_attrib.pi_prot_type, - is_write, cmd)) + if (sbc_set_prot_op_checks(protect, fabric_prot, pi_prot_type, is_write, cmd)) return TCM_INVALID_CDB_FIELD; - cmd->prot_type = dev->dev_attrib.pi_prot_type; + cmd->prot_type = pi_prot_type; cmd->prot_length = dev->prot_length * sectors; /** @@ -1166,14 +1203,16 @@ sbc_dif_generate(struct se_cmd *cmd) sdt = paddr + offset; sdt->guard_tag = cpu_to_be16(crc_t10dif(daddr + j, dev->dev_attrib.block_size)); - if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT) + if (cmd->prot_type == TARGET_DIF_TYPE1_PROT) sdt->ref_tag = cpu_to_be32(sector & 0xffffffff); sdt->app_tag = 0; - pr_debug("DIF WRITE INSERT sector: %llu guard_tag: 0x%04x" + pr_debug("DIF %s INSERT sector: %llu guard_tag: 0x%04x" " app_tag: 0x%04x ref_tag: %u\n", - (unsigned long long)sector, sdt->guard_tag, - sdt->app_tag, be32_to_cpu(sdt->ref_tag)); + (cmd->data_direction == DMA_TO_DEVICE) ? + "WRITE" : "READ", (unsigned long long)sector, + sdt->guard_tag, sdt->app_tag, + be32_to_cpu(sdt->ref_tag)); sector++; offset += sizeof(struct se_dif_v1_tuple); @@ -1185,12 +1224,16 @@ sbc_dif_generate(struct se_cmd *cmd) } static sense_reason_t -sbc_dif_v1_verify(struct se_device *dev, struct se_dif_v1_tuple *sdt, +sbc_dif_v1_verify(struct se_cmd *cmd, struct se_dif_v1_tuple *sdt, const void *p, sector_t sector, unsigned int ei_lba) { + struct se_device *dev = cmd->se_dev; int block_size = dev->dev_attrib.block_size; __be16 csum; + if (!(cmd->prot_checks & TARGET_DIF_CHECK_GUARD)) + goto check_ref; + csum = cpu_to_be16(crc_t10dif(p, block_size)); if (sdt->guard_tag != csum) { @@ -1200,7 +1243,11 @@ sbc_dif_v1_verify(struct se_device *dev, struct se_dif_v1_tuple *sdt, return TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED; } - if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT && +check_ref: + if (!(cmd->prot_checks & TARGET_DIF_CHECK_REFTAG)) + return 0; + + if (cmd->prot_type == TARGET_DIF_TYPE1_PROT && be32_to_cpu(sdt->ref_tag) != (sector & 0xffffffff)) { pr_err("DIFv1 Type 1 reference failed on sector: %llu tag: 0x%08x" " sector MSB: 0x%08x\n", (unsigned long long)sector, @@ -1208,7 +1255,7 @@ sbc_dif_v1_verify(struct se_device *dev, struct se_dif_v1_tuple *sdt, return TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED; } - if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE2_PROT && + if (cmd->prot_type == TARGET_DIF_TYPE2_PROT && be32_to_cpu(sdt->ref_tag) != ei_lba) { pr_err("DIFv1 Type 2 reference failed on sector: %llu tag: 0x%08x" " ei_lba: 0x%08x\n", (unsigned long long)sector, @@ -1229,6 +1276,9 @@ sbc_dif_copy_prot(struct se_cmd *cmd, unsigned int sectors, bool read, unsigned int i, len, left; unsigned int offset = sg_off; + if (!sg) + return; + left = sectors * dev->prot_length; for_each_sg(cmd->t_prot_sg, psg, cmd->t_prot_nents, i) { @@ -1292,7 +1342,7 @@ sbc_dif_verify_write(struct se_cmd *cmd, sector_t start, unsigned int sectors, (unsigned long long)sector, sdt->guard_tag, sdt->app_tag, be32_to_cpu(sdt->ref_tag)); - rc = sbc_dif_v1_verify(dev, sdt, daddr + j, sector, + rc = sbc_dif_v1_verify(cmd, sdt, daddr + j, sector, ei_lba); if (rc) { kunmap_atomic(paddr); @@ -1309,6 +1359,9 @@ sbc_dif_verify_write(struct se_cmd *cmd, sector_t start, unsigned int sectors, kunmap_atomic(paddr); kunmap_atomic(daddr); } + if (!sg) + return 0; + sbc_dif_copy_prot(cmd, sectors, false, sg, sg_off); return 0; @@ -1353,7 +1406,7 @@ __sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors, continue; } - rc = sbc_dif_v1_verify(dev, sdt, daddr + j, sector, + rc = sbc_dif_v1_verify(cmd, sdt, daddr + j, sector, ei_lba); if (rc) { kunmap_atomic(paddr); diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 6c8bd6bc175c..7912aa124385 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -103,10 +103,12 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf) buf[5] |= 0x8; /* * Set Protection (PROTECT) bit when DIF has been enabled on the - * device, and the transport supports VERIFY + PASS. + * device, and the fabric supports VERIFY + PASS. Also report + * PROTECT=1 if sess_prot_type has been configured to allow T10-PI + * to unprotected devices. */ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) { - if (dev->dev_attrib.pi_prot_type) + if (dev->dev_attrib.pi_prot_type || cmd->se_sess->sess_prot_type) buf[5] |= 0x1; } @@ -467,9 +469,11 @@ spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf) * only for TYPE3 protection. */ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) { - if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT) + if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT || + cmd->se_sess->sess_prot_type == TARGET_DIF_TYPE1_PROT) buf[4] = 0x5; - else if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE3_PROT) + else if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE3_PROT || + cmd->se_sess->sess_prot_type == TARGET_DIF_TYPE3_PROT) buf[4] = 0x4; } @@ -861,7 +865,7 @@ static int spc_modesense_control(struct se_cmd *cmd, u8 pc, u8 *p) * TAG field. */ if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) { - if (dev->dev_attrib.pi_prot_type) + if (dev->dev_attrib.pi_prot_type || sess->sess_prot_type) p[5] |= 0x80; } @@ -1099,7 +1103,7 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd) unsigned char *buf; unsigned char tbuf[SE_MODE_PAGE_BUF]; int length; - int ret = 0; + sense_reason_t ret = 0; int i; if (!cmd->data_length) { diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index fa5e157db47b..315ec3458eeb 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -125,8 +125,8 @@ void core_tmr_abort_task( if (dev != se_cmd->se_dev) continue; - /* skip se_cmd associated with tmr */ - if (tmr->task_cmd == se_cmd) + /* skip task management functions, including tmr->task_cmd */ + if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) continue; ref_tag = se_cmd->se_tfo->get_task_tag(se_cmd); diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 0696de9553d3..47f064415bf6 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -672,7 +672,7 @@ static int core_tpg_setup_virtual_lun0(struct se_portal_group *se_tpg) } int core_tpg_register( - struct target_core_fabric_ops *tfo, + const struct target_core_fabric_ops *tfo, struct se_wwn *se_wwn, struct se_portal_group *se_tpg, void *tpg_fabric_ptr, diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ac3cbabdbdf0..3fe5cb240b6f 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -322,6 +322,7 @@ void __transport_register_session( struct se_session *se_sess, void *fabric_sess_ptr) { + const struct target_core_fabric_ops *tfo = se_tpg->se_tpg_tfo; unsigned char buf[PR_REG_ISID_LEN]; se_sess->se_tpg = se_tpg; @@ -334,6 +335,21 @@ void __transport_register_session( */ if (se_nacl) { /* + * + * Determine if fabric allows for T10-PI feature bits exposed to + * initiators for device backends with !dev->dev_attrib.pi_prot_type. + * + * If so, then always save prot_type on a per se_node_acl node + * basis and re-instate the previous sess_prot_type to avoid + * disabling PI from below any previously initiator side + * registered LUNs. + */ + if (se_nacl->saved_prot_type) + se_sess->sess_prot_type = se_nacl->saved_prot_type; + else if (tfo->tpg_check_prot_fabric_only) + se_sess->sess_prot_type = se_nacl->saved_prot_type = + tfo->tpg_check_prot_fabric_only(se_tpg); + /* * If the fabric module supports an ISID based TransportID, * save this value in binary from the fabric I_T Nexus now. */ @@ -404,6 +420,30 @@ void target_put_session(struct se_session *se_sess) } EXPORT_SYMBOL(target_put_session); +ssize_t target_show_dynamic_sessions(struct se_portal_group *se_tpg, char *page) +{ + struct se_session *se_sess; + ssize_t len = 0; + + spin_lock_bh(&se_tpg->session_lock); + list_for_each_entry(se_sess, &se_tpg->tpg_sess_list, sess_list) { + if (!se_sess->se_node_acl) + continue; + if (!se_sess->se_node_acl->dynamic_node_acl) + continue; + if (strlen(se_sess->se_node_acl->initiatorname) + 1 + len > PAGE_SIZE) + break; + + len += snprintf(page + len, PAGE_SIZE - len, "%s\n", + se_sess->se_node_acl->initiatorname); + len += 1; /* Include NULL terminator */ + } + spin_unlock_bh(&se_tpg->session_lock); + + return len; +} +EXPORT_SYMBOL(target_show_dynamic_sessions); + static void target_complete_nacl(struct kref *kref) { struct se_node_acl *nacl = container_of(kref, @@ -462,7 +502,7 @@ EXPORT_SYMBOL(transport_free_session); void transport_deregister_session(struct se_session *se_sess) { struct se_portal_group *se_tpg = se_sess->se_tpg; - struct target_core_fabric_ops *se_tfo; + const struct target_core_fabric_ops *se_tfo; struct se_node_acl *se_nacl; unsigned long flags; bool comp_nacl = true; @@ -1118,7 +1158,7 @@ target_cmd_size_check(struct se_cmd *cmd, unsigned int size) */ void transport_init_se_cmd( struct se_cmd *cmd, - struct target_core_fabric_ops *tfo, + const struct target_core_fabric_ops *tfo, struct se_session *se_sess, u32 data_length, int data_direction, @@ -1570,6 +1610,8 @@ EXPORT_SYMBOL(target_submit_tmr); * has completed. */ bool target_stop_cmd(struct se_cmd *cmd, unsigned long *flags) + __releases(&cmd->t_state_lock) + __acquires(&cmd->t_state_lock) { bool was_active = false; @@ -1615,11 +1657,11 @@ void transport_generic_request_failure(struct se_cmd *cmd, transport_complete_task_attr(cmd); /* * Handle special case for COMPARE_AND_WRITE failure, where the - * callback is expected to drop the per device ->caw_mutex. + * callback is expected to drop the per device ->caw_sem. */ if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) && cmd->transport_complete_callback) - cmd->transport_complete_callback(cmd); + cmd->transport_complete_callback(cmd, false); switch (sense_reason) { case TCM_NON_EXISTENT_LUN: @@ -1706,6 +1748,41 @@ void __target_execute_cmd(struct se_cmd *cmd) } } +static int target_write_prot_action(struct se_cmd *cmd) +{ + u32 sectors; + /* + * Perform WRITE_INSERT of PI using software emulation when backend + * device has PI enabled, if the transport has not already generated + * PI using hardware WRITE_INSERT offload. + */ + switch (cmd->prot_op) { + case TARGET_PROT_DOUT_INSERT: + if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DOUT_INSERT)) + sbc_dif_generate(cmd); + break; + case TARGET_PROT_DOUT_STRIP: + if (cmd->se_sess->sup_prot_ops & TARGET_PROT_DOUT_STRIP) + break; + + sectors = cmd->data_length >> ilog2(cmd->se_dev->dev_attrib.block_size); + cmd->pi_err = sbc_dif_verify_write(cmd, cmd->t_task_lba, + sectors, 0, NULL, 0); + if (unlikely(cmd->pi_err)) { + spin_lock_irq(&cmd->t_state_lock); + cmd->transport_state &= ~CMD_T_BUSY|CMD_T_SENT; + spin_unlock_irq(&cmd->t_state_lock); + transport_generic_request_failure(cmd, cmd->pi_err); + return -1; + } + break; + default: + break; + } + + return 0; +} + static bool target_handle_task_attr(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; @@ -1785,15 +1862,9 @@ void target_execute_cmd(struct se_cmd *cmd) cmd->t_state = TRANSPORT_PROCESSING; cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); - /* - * Perform WRITE_INSERT of PI using software emulation when backend - * device has PI enabled, if the transport has not already generated - * PI using hardware WRITE_INSERT offload. - */ - if (cmd->prot_op == TARGET_PROT_DOUT_INSERT) { - if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DOUT_INSERT)) - sbc_dif_generate(cmd); - } + + if (target_write_prot_action(cmd)) + return; if (target_handle_task_attr(cmd)) { spin_lock_irq(&cmd->t_state_lock); @@ -1919,16 +1990,28 @@ static void transport_handle_queue_full( schedule_work(&cmd->se_dev->qf_work_queue); } -static bool target_check_read_strip(struct se_cmd *cmd) +static bool target_read_prot_action(struct se_cmd *cmd) { sense_reason_t rc; - if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DIN_STRIP)) { - rc = sbc_dif_read_strip(cmd); - if (rc) { - cmd->pi_err = rc; - return true; + switch (cmd->prot_op) { + case TARGET_PROT_DIN_STRIP: + if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DIN_STRIP)) { + rc = sbc_dif_read_strip(cmd); + if (rc) { + cmd->pi_err = rc; + return true; + } } + break; + case TARGET_PROT_DIN_INSERT: + if (cmd->se_sess->sup_prot_ops & TARGET_PROT_DIN_INSERT) + break; + + sbc_dif_generate(cmd); + break; + default: + break; } return false; @@ -1975,8 +2058,12 @@ static void target_complete_ok_work(struct work_struct *work) if (cmd->transport_complete_callback) { sense_reason_t rc; - rc = cmd->transport_complete_callback(cmd); + rc = cmd->transport_complete_callback(cmd, true); if (!rc && !(cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE_POST)) { + if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) && + !cmd->data_length) + goto queue_rsp; + return; } else if (rc) { ret = transport_send_check_condition_and_sense(cmd, @@ -1990,6 +2077,7 @@ static void target_complete_ok_work(struct work_struct *work) } } +queue_rsp: switch (cmd->data_direction) { case DMA_FROM_DEVICE: spin_lock(&cmd->se_lun->lun_sep_lock); @@ -2003,8 +2091,7 @@ static void target_complete_ok_work(struct work_struct *work) * backend had PI enabled, if the transport will not be * performing hardware READ_STRIP offload. */ - if (cmd->prot_op == TARGET_PROT_DIN_STRIP && - target_check_read_strip(cmd)) { + if (target_read_prot_action(cmd)) { ret = transport_send_check_condition_and_sense(cmd, cmd->pi_err, 0); if (ret == -EAGAIN || ret == -ENOMEM) @@ -2094,6 +2181,16 @@ static inline void transport_reset_sgl_orig(struct se_cmd *cmd) static inline void transport_free_pages(struct se_cmd *cmd) { if (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) { + /* + * Release special case READ buffer payload required for + * SG_TO_MEM_NOALLOC to function with COMPARE_AND_WRITE + */ + if (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) { + transport_free_sgl(cmd->t_bidi_data_sg, + cmd->t_bidi_data_nents); + cmd->t_bidi_data_sg = NULL; + cmd->t_bidi_data_nents = 0; + } transport_reset_sgl_orig(cmd); return; } @@ -2246,6 +2343,7 @@ sense_reason_t transport_generic_new_cmd(struct se_cmd *cmd) { int ret = 0; + bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB); /* * Determine is the TCM fabric module has already allocated physical @@ -2254,7 +2352,6 @@ transport_generic_new_cmd(struct se_cmd *cmd) */ if (!(cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) && cmd->data_length) { - bool zero_flag = !(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB); if ((cmd->se_cmd_flags & SCF_BIDI) || (cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE)) { @@ -2285,6 +2382,20 @@ transport_generic_new_cmd(struct se_cmd *cmd) cmd->data_length, zero_flag); if (ret < 0) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } else if ((cmd->se_cmd_flags & SCF_COMPARE_AND_WRITE) && + cmd->data_length) { + /* + * Special case for COMPARE_AND_WRITE with fabrics + * using SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC. + */ + u32 caw_length = cmd->t_task_nolb * + cmd->se_dev->dev_attrib.block_size; + + ret = target_alloc_sgl(&cmd->t_bidi_data_sg, + &cmd->t_bidi_data_nents, + caw_length, zero_flag); + if (ret < 0) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } /* * If this command is not a write we can execute it right here, @@ -2376,10 +2487,8 @@ int target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd, * fabric acknowledgement that requires two target_put_sess_cmd() * invocations before se_cmd descriptor release. */ - if (ack_kref) { + if (ack_kref) kref_get(&se_cmd->cmd_kref); - se_cmd->se_cmd_flags |= SCF_ACK_KREF; - } spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); if (se_sess->sess_tearing_down) { @@ -2398,6 +2507,7 @@ out: EXPORT_SYMBOL(target_get_sess_cmd); static void target_release_cmd_kref(struct kref *kref) + __releases(&se_cmd->se_sess->sess_cmd_lock) { struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref); struct se_session *se_sess = se_cmd->se_sess; diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 1a1bcf71ec9d..dbc872a6c981 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -344,8 +344,11 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) entry = (void *) mb + CMDR_OFF + cmd_head; tcmu_flush_dcache_range(entry, sizeof(*entry)); - tcmu_hdr_set_op(&entry->hdr, TCMU_OP_PAD); - tcmu_hdr_set_len(&entry->hdr, pad_size); + tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_PAD); + tcmu_hdr_set_len(&entry->hdr.len_op, pad_size); + entry->hdr.cmd_id = 0; /* not used for PAD */ + entry->hdr.kflags = 0; + entry->hdr.uflags = 0; UPDATE_HEAD(mb->cmd_head, pad_size, udev->cmdr_size); @@ -355,9 +358,11 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) entry = (void *) mb + CMDR_OFF + cmd_head; tcmu_flush_dcache_range(entry, sizeof(*entry)); - tcmu_hdr_set_op(&entry->hdr, TCMU_OP_CMD); - tcmu_hdr_set_len(&entry->hdr, command_size); - entry->cmd_id = tcmu_cmd->cmd_id; + tcmu_hdr_set_op(&entry->hdr.len_op, TCMU_OP_CMD); + tcmu_hdr_set_len(&entry->hdr.len_op, command_size); + entry->hdr.cmd_id = tcmu_cmd->cmd_id; + entry->hdr.kflags = 0; + entry->hdr.uflags = 0; /* * Fix up iovecs, and handle if allocation in data ring wrapped. @@ -376,7 +381,8 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) /* Even iov_base is relative to mb_addr */ iov->iov_len = copy_bytes; - iov->iov_base = (void *) udev->data_off + udev->data_head; + iov->iov_base = (void __user *) udev->data_off + + udev->data_head; iov_cnt++; iov++; @@ -388,7 +394,8 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) copy_bytes = sg->length - copy_bytes; iov->iov_len = copy_bytes; - iov->iov_base = (void *) udev->data_off + udev->data_head; + iov->iov_base = (void __user *) udev->data_off + + udev->data_head; if (se_cmd->data_direction == DMA_TO_DEVICE) { to = (void *) mb + udev->data_off + udev->data_head; @@ -405,6 +412,8 @@ static int tcmu_queue_cmd_ring(struct tcmu_cmd *tcmu_cmd) kunmap_atomic(from); } entry->req.iov_cnt = iov_cnt; + entry->req.iov_bidi_cnt = 0; + entry->req.iov_dif_cnt = 0; /* All offsets relative to mb_addr, not start of entry! */ cdb_off = CMDR_OFF + cmd_head + base_command_size; @@ -462,6 +471,17 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry * return; } + if (entry->hdr.uflags & TCMU_UFLAG_UNKNOWN_OP) { + UPDATE_HEAD(udev->data_tail, cmd->data_length, udev->data_size); + pr_warn("TCMU: Userspace set UNKNOWN_OP flag on se_cmd %p\n", + cmd->se_cmd); + transport_generic_request_failure(cmd->se_cmd, + TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE); + cmd->se_cmd = NULL; + kmem_cache_free(tcmu_cmd_cache, cmd); + return; + } + if (entry->rsp.scsi_status == SAM_STAT_CHECK_CONDITION) { memcpy(se_cmd->sense_buffer, entry->rsp.sense_buffer, se_cmd->scsi_sense_length); @@ -540,14 +560,16 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) tcmu_flush_dcache_range(entry, sizeof(*entry)); - if (tcmu_hdr_get_op(&entry->hdr) == TCMU_OP_PAD) { - UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); + if (tcmu_hdr_get_op(entry->hdr.len_op) == TCMU_OP_PAD) { + UPDATE_HEAD(udev->cmdr_last_cleaned, + tcmu_hdr_get_len(entry->hdr.len_op), + udev->cmdr_size); continue; } - WARN_ON(tcmu_hdr_get_op(&entry->hdr) != TCMU_OP_CMD); + WARN_ON(tcmu_hdr_get_op(entry->hdr.len_op) != TCMU_OP_CMD); spin_lock(&udev->commands_lock); - cmd = idr_find(&udev->commands, entry->cmd_id); + cmd = idr_find(&udev->commands, entry->hdr.cmd_id); if (cmd) idr_remove(&udev->commands, cmd->cmd_id); spin_unlock(&udev->commands_lock); @@ -560,7 +582,9 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev) tcmu_handle_completion(cmd, entry); - UPDATE_HEAD(udev->cmdr_last_cleaned, tcmu_hdr_get_len(&entry->hdr), udev->cmdr_size); + UPDATE_HEAD(udev->cmdr_last_cleaned, + tcmu_hdr_get_len(entry->hdr.len_op), + udev->cmdr_size); handled++; } @@ -838,14 +862,14 @@ static int tcmu_configure_device(struct se_device *dev) udev->data_size = TCMU_RING_SIZE - CMDR_SIZE; mb = udev->mb_addr; - mb->version = 1; + mb->version = TCMU_MAILBOX_VERSION; mb->cmdr_off = CMDR_OFF; mb->cmdr_size = udev->cmdr_size; WARN_ON(!PAGE_ALIGNED(udev->data_off)); WARN_ON(udev->data_size % PAGE_SIZE); - info->version = "1"; + info->version = xstr(TCMU_MAILBOX_VERSION); info->mem[0].name = "tcm-user command & data buffer"; info->mem[0].addr = (phys_addr_t) udev->mb_addr; diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c index 33ac39bf75e5..a600ff15dcfd 100644 --- a/drivers/target/target_core_xcopy.c +++ b/drivers/target/target_core_xcopy.c @@ -34,20 +34,12 @@ #include <target/target_core_fabric.h> #include <target/target_core_configfs.h> +#include "target_core_internal.h" #include "target_core_pr.h" #include "target_core_ua.h" #include "target_core_xcopy.h" static struct workqueue_struct *xcopy_wq = NULL; -/* - * From target_core_device.c - */ -extern struct mutex g_device_mutex; -extern struct list_head g_device_list; -/* - * From target_core_configfs.c - */ -extern struct configfs_subsystem *target_core_subsystem[]; static int target_xcopy_gen_naa_ieee(struct se_device *dev, unsigned char *buf) { @@ -433,7 +425,7 @@ static int xcopy_pt_queue_status(struct se_cmd *se_cmd) return 0; } -static struct target_core_fabric_ops xcopy_pt_tfo = { +static const struct target_core_fabric_ops xcopy_pt_tfo = { .get_fabric_name = xcopy_pt_get_fabric_name, .get_task_tag = xcopy_pt_get_tag, .get_cmd_state = xcopy_pt_get_cmd_state, @@ -548,33 +540,22 @@ static void target_xcopy_setup_pt_port( } } -static int target_xcopy_init_pt_lun( - struct xcopy_pt_cmd *xpt_cmd, - struct xcopy_op *xop, - struct se_device *se_dev, - struct se_cmd *pt_cmd, - bool remote_port) +static void target_xcopy_init_pt_lun(struct se_device *se_dev, + struct se_cmd *pt_cmd, bool remote_port) { /* * Don't allocate + init an pt_cmd->se_lun if honoring local port for * reservations. The pt_cmd->se_lun pointer will be setup from within * target_xcopy_setup_pt_port() */ - if (!remote_port) { - pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH; - return 0; + if (remote_port) { + pr_debug("Setup emulated se_dev: %p from se_dev\n", + pt_cmd->se_dev); + pt_cmd->se_lun = &se_dev->xcopy_lun; + pt_cmd->se_dev = se_dev; } - pt_cmd->se_lun = &se_dev->xcopy_lun; - pt_cmd->se_dev = se_dev; - - pr_debug("Setup emulated se_dev: %p from se_dev\n", pt_cmd->se_dev); - pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD | SCF_CMD_XCOPY_PASSTHROUGH; - - pr_debug("Setup emulated se_dev: %p to pt_cmd->se_lun->lun_se_dev\n", - pt_cmd->se_lun->lun_se_dev); - - return 0; + pt_cmd->se_cmd_flags |= SCF_SE_LUN_CMD; } static int target_xcopy_setup_pt_cmd( @@ -592,11 +573,8 @@ static int target_xcopy_setup_pt_cmd( * Setup LUN+port to honor reservations based upon xop->op_origin for * X-COPY PUSH or X-COPY PULL based upon where the CDB was received. */ - rc = target_xcopy_init_pt_lun(xpt_cmd, xop, se_dev, cmd, remote_port); - if (rc < 0) { - ret = rc; - goto out; - } + target_xcopy_init_pt_lun(se_dev, cmd, remote_port); + xpt_cmd->xcopy_op = xop; target_xcopy_setup_pt_port(xpt_cmd, xop, remote_port); diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h index a0bcfd3e7e7d..881deb3d499a 100644 --- a/drivers/target/tcm_fc/tcm_fc.h +++ b/drivers/target/tcm_fc/tcm_fc.h @@ -129,7 +129,6 @@ struct ft_cmd { extern struct mutex ft_lport_lock; extern struct fc4_prov ft_prov; -extern struct target_fabric_configfs *ft_configfs; extern unsigned int ft_debug_logging; /* diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index efdcb9663a1a..65dce1345966 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -48,7 +48,7 @@ #include "tcm_fc.h" -struct target_fabric_configfs *ft_configfs; +static const struct target_core_fabric_ops ft_fabric_ops; static LIST_HEAD(ft_wwn_list); DEFINE_MUTEX(ft_lport_lock); @@ -337,7 +337,7 @@ static struct se_portal_group *ft_add_tpg( return NULL; } - ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg, + ret = core_tpg_register(&ft_fabric_ops, wwn, &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL); if (ret < 0) { destroy_workqueue(wq); @@ -507,7 +507,9 @@ static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg) return tpg->index; } -static struct target_core_fabric_ops ft_fabric_ops = { +static const struct target_core_fabric_ops ft_fabric_ops = { + .module = THIS_MODULE, + .name = "fc", .get_fabric_name = ft_get_fabric_name, .get_fabric_proto_ident = fc_get_fabric_proto_ident, .tpg_get_wwn = ft_get_fabric_wwn, @@ -552,62 +554,10 @@ static struct target_core_fabric_ops ft_fabric_ops = { .fabric_drop_np = NULL, .fabric_make_nodeacl = &ft_add_acl, .fabric_drop_nodeacl = &ft_del_acl, -}; - -static int ft_register_configfs(void) -{ - struct target_fabric_configfs *fabric; - int ret; - - /* - * Register the top level struct config_item_type with TCM core - */ - fabric = target_fabric_configfs_init(THIS_MODULE, "fc"); - if (IS_ERR(fabric)) { - pr_err("%s: target_fabric_configfs_init() failed!\n", - __func__); - return PTR_ERR(fabric); - } - fabric->tf_ops = ft_fabric_ops; - - /* - * Setup default attribute lists for various fabric->tf_cit_tmpl - */ - fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = ft_wwn_attrs; - fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = - ft_nacl_base_attrs; - fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; - fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL; - /* - * register the fabric for use within TCM - */ - ret = target_fabric_configfs_register(fabric); - if (ret < 0) { - pr_debug("target_fabric_configfs_register() for" - " FC Target failed!\n"); - target_fabric_configfs_free(fabric); - return -1; - } - - /* - * Setup our local pointer to *fabric. - */ - ft_configfs = fabric; - return 0; -} -static void ft_deregister_configfs(void) -{ - if (!ft_configfs) - return; - target_fabric_configfs_deregister(ft_configfs); - ft_configfs = NULL; -} + .tfc_wwn_attrs = ft_wwn_attrs, + .tfc_tpg_nacl_base_attrs = ft_nacl_base_attrs, +}; static struct notifier_block ft_notifier = { .notifier_call = ft_lport_notify @@ -615,15 +565,24 @@ static struct notifier_block ft_notifier = { static int __init ft_init(void) { - if (ft_register_configfs()) - return -1; - if (fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov)) { - ft_deregister_configfs(); - return -1; - } + int ret; + + ret = target_register_template(&ft_fabric_ops); + if (ret) + goto out; + + ret = fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov); + if (ret) + goto out_unregister_template; + blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier); fc_lport_iterate(ft_lport_add, NULL); return 0; + +out_unregister_template: + target_unregister_template(&ft_fabric_ops); +out: + return ret; } static void __exit ft_exit(void) @@ -632,7 +591,7 @@ static void __exit ft_exit(void) &ft_notifier); fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov); fc_lport_iterate(ft_lport_del, NULL); - ft_deregister_configfs(); + target_unregister_template(&ft_fabric_ops); synchronize_rcu(); } |