162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SBP2 target driver (SCSI over IEEE1394 in target mode) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Chris Boot <bootc@bootc.net> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define KMSG_COMPONENT "sbp_target" 962306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/string.h> 1662306a36Sopenharmony_ci#include <linux/configfs.h> 1762306a36Sopenharmony_ci#include <linux/ctype.h> 1862306a36Sopenharmony_ci#include <linux/delay.h> 1962306a36Sopenharmony_ci#include <linux/firewire.h> 2062306a36Sopenharmony_ci#include <linux/firewire-constants.h> 2162306a36Sopenharmony_ci#include <scsi/scsi_proto.h> 2262306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 2362306a36Sopenharmony_ci#include <target/target_core_base.h> 2462306a36Sopenharmony_ci#include <target/target_core_backend.h> 2562306a36Sopenharmony_ci#include <target/target_core_fabric.h> 2662306a36Sopenharmony_ci#include <asm/unaligned.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "sbp_target.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* FireWire address region for management and command block address handlers */ 3162306a36Sopenharmony_cistatic const struct fw_address_region sbp_register_region = { 3262306a36Sopenharmony_ci .start = CSR_REGISTER_BASE + 0x10000, 3362306a36Sopenharmony_ci .end = 0x1000000000000ULL, 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic const u32 sbp_unit_directory_template[] = { 3762306a36Sopenharmony_ci 0x1200609e, /* unit_specifier_id: NCITS/T10 */ 3862306a36Sopenharmony_ci 0x13010483, /* unit_sw_version: 1155D Rev 4 */ 3962306a36Sopenharmony_ci 0x3800609e, /* command_set_specifier_id: NCITS/T10 */ 4062306a36Sopenharmony_ci 0x390104d8, /* command_set: SPC-2 */ 4162306a36Sopenharmony_ci 0x3b000000, /* command_set_revision: 0 */ 4262306a36Sopenharmony_ci 0x3c000001, /* firmware_revision: 1 */ 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define SESSION_MAINTENANCE_INTERVAL HZ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic atomic_t login_id = ATOMIC_INIT(0); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void session_maintenance_work(struct work_struct *); 5062306a36Sopenharmony_cistatic int sbp_run_transaction(struct fw_card *, int, int, int, int, 5162306a36Sopenharmony_ci unsigned long long, void *, size_t); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int read_peer_guid(u64 *guid, const struct sbp_management_request *req) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci int ret; 5662306a36Sopenharmony_ci __be32 high, low; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST, 5962306a36Sopenharmony_ci req->node_addr, req->generation, req->speed, 6062306a36Sopenharmony_ci (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4, 6162306a36Sopenharmony_ci &high, sizeof(high)); 6262306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) 6362306a36Sopenharmony_ci return ret; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST, 6662306a36Sopenharmony_ci req->node_addr, req->generation, req->speed, 6762306a36Sopenharmony_ci (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4, 6862306a36Sopenharmony_ci &low, sizeof(low)); 6962306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) 7062306a36Sopenharmony_ci return ret; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci *guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return RCODE_COMPLETE; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct sbp_session *sbp_session_find_by_guid( 7862306a36Sopenharmony_ci struct sbp_tpg *tpg, u64 guid) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct se_session *se_sess; 8162306a36Sopenharmony_ci struct sbp_session *sess, *found = NULL; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci spin_lock_bh(&tpg->se_tpg.session_lock); 8462306a36Sopenharmony_ci list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) { 8562306a36Sopenharmony_ci sess = se_sess->fabric_sess_ptr; 8662306a36Sopenharmony_ci if (sess->guid == guid) 8762306a36Sopenharmony_ci found = sess; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci spin_unlock_bh(&tpg->se_tpg.session_lock); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return found; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct sbp_login_descriptor *sbp_login_find_by_lun( 9562306a36Sopenharmony_ci struct sbp_session *session, u32 unpacked_lun) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct sbp_login_descriptor *login, *found = NULL; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci spin_lock_bh(&session->lock); 10062306a36Sopenharmony_ci list_for_each_entry(login, &session->login_list, link) { 10162306a36Sopenharmony_ci if (login->login_lun == unpacked_lun) 10262306a36Sopenharmony_ci found = login; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci spin_unlock_bh(&session->lock); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return found; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int sbp_login_count_all_by_lun( 11062306a36Sopenharmony_ci struct sbp_tpg *tpg, 11162306a36Sopenharmony_ci u32 unpacked_lun, 11262306a36Sopenharmony_ci int exclusive) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct se_session *se_sess; 11562306a36Sopenharmony_ci struct sbp_session *sess; 11662306a36Sopenharmony_ci struct sbp_login_descriptor *login; 11762306a36Sopenharmony_ci int count = 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci spin_lock_bh(&tpg->se_tpg.session_lock); 12062306a36Sopenharmony_ci list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) { 12162306a36Sopenharmony_ci sess = se_sess->fabric_sess_ptr; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 12462306a36Sopenharmony_ci list_for_each_entry(login, &sess->login_list, link) { 12562306a36Sopenharmony_ci if (login->login_lun != unpacked_lun) 12662306a36Sopenharmony_ci continue; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (!exclusive || login->exclusive) 12962306a36Sopenharmony_ci count++; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci spin_unlock_bh(&tpg->se_tpg.session_lock); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return count; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic struct sbp_login_descriptor *sbp_login_find_by_id( 13962306a36Sopenharmony_ci struct sbp_tpg *tpg, int login_id) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct se_session *se_sess; 14262306a36Sopenharmony_ci struct sbp_session *sess; 14362306a36Sopenharmony_ci struct sbp_login_descriptor *login, *found = NULL; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci spin_lock_bh(&tpg->se_tpg.session_lock); 14662306a36Sopenharmony_ci list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) { 14762306a36Sopenharmony_ci sess = se_sess->fabric_sess_ptr; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 15062306a36Sopenharmony_ci list_for_each_entry(login, &sess->login_list, link) { 15162306a36Sopenharmony_ci if (login->login_id == login_id) 15262306a36Sopenharmony_ci found = login; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci spin_unlock_bh(&tpg->se_tpg.session_lock); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return found; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic u32 sbp_get_lun_from_tpg(struct sbp_tpg *tpg, u32 login_lun, int *err) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct se_portal_group *se_tpg = &tpg->se_tpg; 16462306a36Sopenharmony_ci struct se_lun *se_lun; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci rcu_read_lock(); 16762306a36Sopenharmony_ci hlist_for_each_entry_rcu(se_lun, &se_tpg->tpg_lun_hlist, link) { 16862306a36Sopenharmony_ci if (se_lun->unpacked_lun == login_lun) { 16962306a36Sopenharmony_ci rcu_read_unlock(); 17062306a36Sopenharmony_ci *err = 0; 17162306a36Sopenharmony_ci return login_lun; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci rcu_read_unlock(); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci *err = -ENODEV; 17762306a36Sopenharmony_ci return login_lun; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct sbp_session *sbp_session_create( 18162306a36Sopenharmony_ci struct sbp_tpg *tpg, 18262306a36Sopenharmony_ci u64 guid) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct sbp_session *sess; 18562306a36Sopenharmony_ci int ret; 18662306a36Sopenharmony_ci char guid_str[17]; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci snprintf(guid_str, sizeof(guid_str), "%016llx", guid); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci sess = kmalloc(sizeof(*sess), GFP_KERNEL); 19162306a36Sopenharmony_ci if (!sess) 19262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci spin_lock_init(&sess->lock); 19562306a36Sopenharmony_ci INIT_LIST_HEAD(&sess->login_list); 19662306a36Sopenharmony_ci INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work); 19762306a36Sopenharmony_ci sess->guid = guid; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci sess->se_sess = target_setup_session(&tpg->se_tpg, 128, 20062306a36Sopenharmony_ci sizeof(struct sbp_target_request), 20162306a36Sopenharmony_ci TARGET_PROT_NORMAL, guid_str, 20262306a36Sopenharmony_ci sess, NULL); 20362306a36Sopenharmony_ci if (IS_ERR(sess->se_sess)) { 20462306a36Sopenharmony_ci pr_err("failed to init se_session\n"); 20562306a36Sopenharmony_ci ret = PTR_ERR(sess->se_sess); 20662306a36Sopenharmony_ci kfree(sess); 20762306a36Sopenharmony_ci return ERR_PTR(ret); 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return sess; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void sbp_session_release(struct sbp_session *sess, bool cancel_work) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 21662306a36Sopenharmony_ci if (!list_empty(&sess->login_list)) { 21762306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (cancel_work) 22362306a36Sopenharmony_ci cancel_delayed_work_sync(&sess->maint_work); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci target_remove_session(sess->se_sess); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (sess->card) 22862306a36Sopenharmony_ci fw_card_put(sess->card); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci kfree(sess); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void sbp_target_agent_unregister(struct sbp_target_agent *); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void sbp_login_release(struct sbp_login_descriptor *login, 23662306a36Sopenharmony_ci bool cancel_work) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct sbp_session *sess = login->sess; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* FIXME: abort/wait on tasks */ 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci sbp_target_agent_unregister(login->tgt_agt); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (sess) { 24562306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 24662306a36Sopenharmony_ci list_del(&login->link); 24762306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci sbp_session_release(sess, cancel_work); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci kfree(login); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic struct sbp_target_agent *sbp_target_agent_register( 25662306a36Sopenharmony_ci struct sbp_login_descriptor *); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void sbp_management_request_login( 25962306a36Sopenharmony_ci struct sbp_management_agent *agent, struct sbp_management_request *req, 26062306a36Sopenharmony_ci int *status_data_size) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct sbp_tport *tport = agent->tport; 26362306a36Sopenharmony_ci struct sbp_tpg *tpg = tport->tpg; 26462306a36Sopenharmony_ci struct sbp_session *sess; 26562306a36Sopenharmony_ci struct sbp_login_descriptor *login; 26662306a36Sopenharmony_ci struct sbp_login_response_block *response; 26762306a36Sopenharmony_ci u64 guid; 26862306a36Sopenharmony_ci u32 unpacked_lun; 26962306a36Sopenharmony_ci int login_response_len, ret; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci unpacked_lun = sbp_get_lun_from_tpg(tpg, 27262306a36Sopenharmony_ci LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)), &ret); 27362306a36Sopenharmony_ci if (ret) { 27462306a36Sopenharmony_ci pr_notice("login to unknown LUN: %d\n", 27562306a36Sopenharmony_ci LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc))); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci req->status.status = cpu_to_be32( 27862306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 27962306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP)); 28062306a36Sopenharmony_ci return; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ret = read_peer_guid(&guid, req); 28462306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) { 28562306a36Sopenharmony_ci pr_warn("failed to read peer GUID: %d\n", ret); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci req->status.status = cpu_to_be32( 28862306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) | 28962306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR)); 29062306a36Sopenharmony_ci return; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n", 29462306a36Sopenharmony_ci unpacked_lun, guid); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci sess = sbp_session_find_by_guid(tpg, guid); 29762306a36Sopenharmony_ci if (sess) { 29862306a36Sopenharmony_ci login = sbp_login_find_by_lun(sess, unpacked_lun); 29962306a36Sopenharmony_ci if (login) { 30062306a36Sopenharmony_ci pr_notice("initiator already logged-in\n"); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* 30362306a36Sopenharmony_ci * SBP-2 R4 says we should return access denied, but 30462306a36Sopenharmony_ci * that can confuse initiators. Instead we need to 30562306a36Sopenharmony_ci * treat this like a reconnect, but send the login 30662306a36Sopenharmony_ci * response block like a fresh login. 30762306a36Sopenharmony_ci * 30862306a36Sopenharmony_ci * This is required particularly in the case of Apple 30962306a36Sopenharmony_ci * devices booting off the FireWire target, where 31062306a36Sopenharmony_ci * the firmware has an active login to the target. When 31162306a36Sopenharmony_ci * the OS takes control of the session it issues its own 31262306a36Sopenharmony_ci * LOGIN rather than a RECONNECT. To avoid the machine 31362306a36Sopenharmony_ci * waiting until the reconnect_hold expires, we can skip 31462306a36Sopenharmony_ci * the ACCESS_DENIED errors to speed things up. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci goto already_logged_in; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * check exclusive bit in login request 32362306a36Sopenharmony_ci * reject with access_denied if any logins present 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ci if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) && 32662306a36Sopenharmony_ci sbp_login_count_all_by_lun(tpg, unpacked_lun, 0)) { 32762306a36Sopenharmony_ci pr_warn("refusing exclusive login with other active logins\n"); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci req->status.status = cpu_to_be32( 33062306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 33162306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED)); 33262306a36Sopenharmony_ci return; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* 33662306a36Sopenharmony_ci * check exclusive bit in any existing login descriptor 33762306a36Sopenharmony_ci * reject with access_denied if any exclusive logins present 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci if (sbp_login_count_all_by_lun(tpg, unpacked_lun, 1)) { 34062306a36Sopenharmony_ci pr_warn("refusing login while another exclusive login present\n"); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci req->status.status = cpu_to_be32( 34362306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 34462306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED)); 34562306a36Sopenharmony_ci return; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * check we haven't exceeded the number of allowed logins 35062306a36Sopenharmony_ci * reject with resources_unavailable if we have 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ci if (sbp_login_count_all_by_lun(tpg, unpacked_lun, 0) >= 35362306a36Sopenharmony_ci tport->max_logins_per_lun) { 35462306a36Sopenharmony_ci pr_warn("max number of logins reached\n"); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci req->status.status = cpu_to_be32( 35762306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 35862306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL)); 35962306a36Sopenharmony_ci return; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (!sess) { 36362306a36Sopenharmony_ci sess = sbp_session_create(tpg, guid); 36462306a36Sopenharmony_ci if (IS_ERR(sess)) { 36562306a36Sopenharmony_ci switch (PTR_ERR(sess)) { 36662306a36Sopenharmony_ci case -EPERM: 36762306a36Sopenharmony_ci ret = SBP_STATUS_ACCESS_DENIED; 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci default: 37062306a36Sopenharmony_ci ret = SBP_STATUS_RESOURCES_UNAVAIL; 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci req->status.status = cpu_to_be32( 37562306a36Sopenharmony_ci STATUS_BLOCK_RESP( 37662306a36Sopenharmony_ci STATUS_RESP_REQUEST_COMPLETE) | 37762306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(ret)); 37862306a36Sopenharmony_ci return; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci sess->node_id = req->node_addr; 38262306a36Sopenharmony_ci sess->card = fw_card_get(req->card); 38362306a36Sopenharmony_ci sess->generation = req->generation; 38462306a36Sopenharmony_ci sess->speed = req->speed; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci schedule_delayed_work(&sess->maint_work, 38762306a36Sopenharmony_ci SESSION_MAINTENANCE_INTERVAL); 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* only take the latest reconnect_hold into account */ 39162306a36Sopenharmony_ci sess->reconnect_hold = min( 39262306a36Sopenharmony_ci 1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)), 39362306a36Sopenharmony_ci tport->max_reconnect_timeout) - 1; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci login = kmalloc(sizeof(*login), GFP_KERNEL); 39662306a36Sopenharmony_ci if (!login) { 39762306a36Sopenharmony_ci pr_err("failed to allocate login descriptor\n"); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci sbp_session_release(sess, true); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci req->status.status = cpu_to_be32( 40262306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 40362306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL)); 40462306a36Sopenharmony_ci return; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci login->sess = sess; 40862306a36Sopenharmony_ci login->login_lun = unpacked_lun; 40962306a36Sopenharmony_ci login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo); 41062306a36Sopenharmony_ci login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)); 41162306a36Sopenharmony_ci login->login_id = atomic_inc_return(&login_id); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci login->tgt_agt = sbp_target_agent_register(login); 41462306a36Sopenharmony_ci if (IS_ERR(login->tgt_agt)) { 41562306a36Sopenharmony_ci ret = PTR_ERR(login->tgt_agt); 41662306a36Sopenharmony_ci pr_err("failed to map command block handler: %d\n", ret); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci sbp_session_release(sess, true); 41962306a36Sopenharmony_ci kfree(login); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci req->status.status = cpu_to_be32( 42262306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 42362306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL)); 42462306a36Sopenharmony_ci return; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 42862306a36Sopenharmony_ci list_add_tail(&login->link, &sess->login_list); 42962306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cialready_logged_in: 43262306a36Sopenharmony_ci response = kzalloc(sizeof(*response), GFP_KERNEL); 43362306a36Sopenharmony_ci if (!response) { 43462306a36Sopenharmony_ci pr_err("failed to allocate login response block\n"); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci sbp_login_release(login, true); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci req->status.status = cpu_to_be32( 43962306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 44062306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL)); 44162306a36Sopenharmony_ci return; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci login_response_len = clamp_val( 44562306a36Sopenharmony_ci LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length)), 44662306a36Sopenharmony_ci 12, sizeof(*response)); 44762306a36Sopenharmony_ci response->misc = cpu_to_be32( 44862306a36Sopenharmony_ci ((login_response_len & 0xffff) << 16) | 44962306a36Sopenharmony_ci (login->login_id & 0xffff)); 45062306a36Sopenharmony_ci response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff); 45162306a36Sopenharmony_ci addr_to_sbp2_pointer(login->tgt_agt->handler.offset, 45262306a36Sopenharmony_ci &response->command_block_agent); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci ret = sbp_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST, 45562306a36Sopenharmony_ci sess->node_id, sess->generation, sess->speed, 45662306a36Sopenharmony_ci sbp2_pointer_to_addr(&req->orb.ptr2), response, 45762306a36Sopenharmony_ci login_response_len); 45862306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) { 45962306a36Sopenharmony_ci pr_debug("failed to write login response block: %x\n", ret); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci kfree(response); 46262306a36Sopenharmony_ci sbp_login_release(login, true); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci req->status.status = cpu_to_be32( 46562306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) | 46662306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR)); 46762306a36Sopenharmony_ci return; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci kfree(response); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci req->status.status = cpu_to_be32( 47362306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 47462306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK)); 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic void sbp_management_request_query_logins( 47862306a36Sopenharmony_ci struct sbp_management_agent *agent, struct sbp_management_request *req, 47962306a36Sopenharmony_ci int *status_data_size) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci pr_notice("QUERY LOGINS not implemented\n"); 48262306a36Sopenharmony_ci /* FIXME: implement */ 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci req->status.status = cpu_to_be32( 48562306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 48662306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP)); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic void sbp_management_request_reconnect( 49062306a36Sopenharmony_ci struct sbp_management_agent *agent, struct sbp_management_request *req, 49162306a36Sopenharmony_ci int *status_data_size) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct sbp_tport *tport = agent->tport; 49462306a36Sopenharmony_ci struct sbp_tpg *tpg = tport->tpg; 49562306a36Sopenharmony_ci int ret; 49662306a36Sopenharmony_ci u64 guid; 49762306a36Sopenharmony_ci struct sbp_login_descriptor *login; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci ret = read_peer_guid(&guid, req); 50062306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) { 50162306a36Sopenharmony_ci pr_warn("failed to read peer GUID: %d\n", ret); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci req->status.status = cpu_to_be32( 50462306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) | 50562306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR)); 50662306a36Sopenharmony_ci return; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci pr_notice("mgt_agent RECONNECT from %016llx\n", guid); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci login = sbp_login_find_by_id(tpg, 51262306a36Sopenharmony_ci RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc))); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (!login) { 51562306a36Sopenharmony_ci pr_err("mgt_agent RECONNECT unknown login ID\n"); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci req->status.status = cpu_to_be32( 51862306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 51962306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED)); 52062306a36Sopenharmony_ci return; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (login->sess->guid != guid) { 52462306a36Sopenharmony_ci pr_err("mgt_agent RECONNECT login GUID doesn't match\n"); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci req->status.status = cpu_to_be32( 52762306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 52862306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED)); 52962306a36Sopenharmony_ci return; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci spin_lock_bh(&login->sess->lock); 53362306a36Sopenharmony_ci if (login->sess->card) 53462306a36Sopenharmony_ci fw_card_put(login->sess->card); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* update the node details */ 53762306a36Sopenharmony_ci login->sess->generation = req->generation; 53862306a36Sopenharmony_ci login->sess->node_id = req->node_addr; 53962306a36Sopenharmony_ci login->sess->card = fw_card_get(req->card); 54062306a36Sopenharmony_ci login->sess->speed = req->speed; 54162306a36Sopenharmony_ci spin_unlock_bh(&login->sess->lock); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci req->status.status = cpu_to_be32( 54462306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 54562306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK)); 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cistatic void sbp_management_request_logout( 54962306a36Sopenharmony_ci struct sbp_management_agent *agent, struct sbp_management_request *req, 55062306a36Sopenharmony_ci int *status_data_size) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct sbp_tport *tport = agent->tport; 55362306a36Sopenharmony_ci struct sbp_tpg *tpg = tport->tpg; 55462306a36Sopenharmony_ci int id; 55562306a36Sopenharmony_ci struct sbp_login_descriptor *login; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci login = sbp_login_find_by_id(tpg, id); 56062306a36Sopenharmony_ci if (!login) { 56162306a36Sopenharmony_ci pr_warn("cannot find login: %d\n", id); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci req->status.status = cpu_to_be32( 56462306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 56562306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN)); 56662306a36Sopenharmony_ci return; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci pr_info("mgt_agent LOGOUT from LUN %d session %d\n", 57062306a36Sopenharmony_ci login->login_lun, login->login_id); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (req->node_addr != login->sess->node_id) { 57362306a36Sopenharmony_ci pr_warn("logout from different node ID\n"); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci req->status.status = cpu_to_be32( 57662306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 57762306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED)); 57862306a36Sopenharmony_ci return; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci sbp_login_release(login, true); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci req->status.status = cpu_to_be32( 58462306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 58562306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK)); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic void session_check_for_reset(struct sbp_session *sess) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci bool card_valid = false; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (sess->card) { 59562306a36Sopenharmony_ci spin_lock_irq(&sess->card->lock); 59662306a36Sopenharmony_ci card_valid = (sess->card->local_node != NULL); 59762306a36Sopenharmony_ci spin_unlock_irq(&sess->card->lock); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (!card_valid) { 60062306a36Sopenharmony_ci fw_card_put(sess->card); 60162306a36Sopenharmony_ci sess->card = NULL; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (!card_valid || (sess->generation != sess->card->generation)) { 60662306a36Sopenharmony_ci pr_info("Waiting for reconnect from node: %016llx\n", 60762306a36Sopenharmony_ci sess->guid); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci sess->node_id = -1; 61062306a36Sopenharmony_ci sess->reconnect_expires = get_jiffies_64() + 61162306a36Sopenharmony_ci ((sess->reconnect_hold + 1) * HZ); 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic void session_reconnect_expired(struct sbp_session *sess) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci struct sbp_login_descriptor *login, *temp; 62062306a36Sopenharmony_ci LIST_HEAD(login_list); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci pr_info("Reconnect timer expired for node: %016llx\n", sess->guid); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 62562306a36Sopenharmony_ci list_for_each_entry_safe(login, temp, &sess->login_list, link) { 62662306a36Sopenharmony_ci login->sess = NULL; 62762306a36Sopenharmony_ci list_move_tail(&login->link, &login_list); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci list_for_each_entry_safe(login, temp, &login_list, link) { 63262306a36Sopenharmony_ci list_del(&login->link); 63362306a36Sopenharmony_ci sbp_login_release(login, false); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci sbp_session_release(sess, false); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic void session_maintenance_work(struct work_struct *work) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct sbp_session *sess = container_of(work, struct sbp_session, 64262306a36Sopenharmony_ci maint_work.work); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* could be called while tearing down the session */ 64562306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 64662306a36Sopenharmony_ci if (list_empty(&sess->login_list)) { 64762306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 64862306a36Sopenharmony_ci return; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (sess->node_id != -1) { 65362306a36Sopenharmony_ci /* check for bus reset and make node_id invalid */ 65462306a36Sopenharmony_ci session_check_for_reset(sess); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci schedule_delayed_work(&sess->maint_work, 65762306a36Sopenharmony_ci SESSION_MAINTENANCE_INTERVAL); 65862306a36Sopenharmony_ci } else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) { 65962306a36Sopenharmony_ci /* still waiting for reconnect */ 66062306a36Sopenharmony_ci schedule_delayed_work(&sess->maint_work, 66162306a36Sopenharmony_ci SESSION_MAINTENANCE_INTERVAL); 66262306a36Sopenharmony_ci } else { 66362306a36Sopenharmony_ci /* reconnect timeout has expired */ 66462306a36Sopenharmony_ci session_reconnect_expired(sess); 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data, 66962306a36Sopenharmony_ci struct sbp_target_agent *agent) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci int state; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci switch (tcode) { 67462306a36Sopenharmony_ci case TCODE_READ_QUADLET_REQUEST: 67562306a36Sopenharmony_ci pr_debug("tgt_agent AGENT_STATE READ\n"); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 67862306a36Sopenharmony_ci state = agent->state; 67962306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci *(__be32 *)data = cpu_to_be32(state); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return RCODE_COMPLETE; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci case TCODE_WRITE_QUADLET_REQUEST: 68662306a36Sopenharmony_ci /* ignored */ 68762306a36Sopenharmony_ci return RCODE_COMPLETE; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci default: 69062306a36Sopenharmony_ci return RCODE_TYPE_ERROR; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int tgt_agent_rw_agent_reset(struct fw_card *card, int tcode, void *data, 69562306a36Sopenharmony_ci struct sbp_target_agent *agent) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci switch (tcode) { 69862306a36Sopenharmony_ci case TCODE_WRITE_QUADLET_REQUEST: 69962306a36Sopenharmony_ci pr_debug("tgt_agent AGENT_RESET\n"); 70062306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 70162306a36Sopenharmony_ci agent->state = AGENT_STATE_RESET; 70262306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 70362306a36Sopenharmony_ci return RCODE_COMPLETE; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci default: 70662306a36Sopenharmony_ci return RCODE_TYPE_ERROR; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int tgt_agent_rw_orb_pointer(struct fw_card *card, int tcode, void *data, 71162306a36Sopenharmony_ci struct sbp_target_agent *agent) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct sbp2_pointer *ptr = data; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci switch (tcode) { 71662306a36Sopenharmony_ci case TCODE_WRITE_BLOCK_REQUEST: 71762306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 71862306a36Sopenharmony_ci if (agent->state != AGENT_STATE_SUSPENDED && 71962306a36Sopenharmony_ci agent->state != AGENT_STATE_RESET) { 72062306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 72162306a36Sopenharmony_ci pr_notice("Ignoring ORB_POINTER write while active.\n"); 72262306a36Sopenharmony_ci return RCODE_CONFLICT_ERROR; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci agent->state = AGENT_STATE_ACTIVE; 72562306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci agent->orb_pointer = sbp2_pointer_to_addr(ptr); 72862306a36Sopenharmony_ci agent->doorbell = false; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci pr_debug("tgt_agent ORB_POINTER write: 0x%llx\n", 73162306a36Sopenharmony_ci agent->orb_pointer); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci queue_work(system_unbound_wq, &agent->work); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return RCODE_COMPLETE; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci case TCODE_READ_BLOCK_REQUEST: 73862306a36Sopenharmony_ci pr_debug("tgt_agent ORB_POINTER READ\n"); 73962306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 74062306a36Sopenharmony_ci addr_to_sbp2_pointer(agent->orb_pointer, ptr); 74162306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 74262306a36Sopenharmony_ci return RCODE_COMPLETE; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci default: 74562306a36Sopenharmony_ci return RCODE_TYPE_ERROR; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic int tgt_agent_rw_doorbell(struct fw_card *card, int tcode, void *data, 75062306a36Sopenharmony_ci struct sbp_target_agent *agent) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci switch (tcode) { 75362306a36Sopenharmony_ci case TCODE_WRITE_QUADLET_REQUEST: 75462306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 75562306a36Sopenharmony_ci if (agent->state != AGENT_STATE_SUSPENDED) { 75662306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 75762306a36Sopenharmony_ci pr_debug("Ignoring DOORBELL while active.\n"); 75862306a36Sopenharmony_ci return RCODE_CONFLICT_ERROR; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci agent->state = AGENT_STATE_ACTIVE; 76162306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci agent->doorbell = true; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci pr_debug("tgt_agent DOORBELL\n"); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci queue_work(system_unbound_wq, &agent->work); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci return RCODE_COMPLETE; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci case TCODE_READ_QUADLET_REQUEST: 77262306a36Sopenharmony_ci return RCODE_COMPLETE; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci default: 77562306a36Sopenharmony_ci return RCODE_TYPE_ERROR; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card, 78062306a36Sopenharmony_ci int tcode, void *data, struct sbp_target_agent *agent) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci switch (tcode) { 78362306a36Sopenharmony_ci case TCODE_WRITE_QUADLET_REQUEST: 78462306a36Sopenharmony_ci pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n"); 78562306a36Sopenharmony_ci /* ignored as we don't send unsolicited status */ 78662306a36Sopenharmony_ci return RCODE_COMPLETE; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci case TCODE_READ_QUADLET_REQUEST: 78962306a36Sopenharmony_ci return RCODE_COMPLETE; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci default: 79262306a36Sopenharmony_ci return RCODE_TYPE_ERROR; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic void tgt_agent_rw(struct fw_card *card, struct fw_request *request, 79762306a36Sopenharmony_ci int tcode, int destination, int source, int generation, 79862306a36Sopenharmony_ci unsigned long long offset, void *data, size_t length, 79962306a36Sopenharmony_ci void *callback_data) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct sbp_target_agent *agent = callback_data; 80262306a36Sopenharmony_ci struct sbp_session *sess = agent->login->sess; 80362306a36Sopenharmony_ci int sess_gen, sess_node, rcode; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 80662306a36Sopenharmony_ci sess_gen = sess->generation; 80762306a36Sopenharmony_ci sess_node = sess->node_id; 80862306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (generation != sess_gen) { 81162306a36Sopenharmony_ci pr_notice("ignoring request with wrong generation\n"); 81262306a36Sopenharmony_ci rcode = RCODE_TYPE_ERROR; 81362306a36Sopenharmony_ci goto out; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (source != sess_node) { 81762306a36Sopenharmony_ci pr_notice("ignoring request from foreign node (%x != %x)\n", 81862306a36Sopenharmony_ci source, sess_node); 81962306a36Sopenharmony_ci rcode = RCODE_TYPE_ERROR; 82062306a36Sopenharmony_ci goto out; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* turn offset into the offset from the start of the block */ 82462306a36Sopenharmony_ci offset -= agent->handler.offset; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (offset == 0x00 && length == 4) { 82762306a36Sopenharmony_ci /* AGENT_STATE */ 82862306a36Sopenharmony_ci rcode = tgt_agent_rw_agent_state(card, tcode, data, agent); 82962306a36Sopenharmony_ci } else if (offset == 0x04 && length == 4) { 83062306a36Sopenharmony_ci /* AGENT_RESET */ 83162306a36Sopenharmony_ci rcode = tgt_agent_rw_agent_reset(card, tcode, data, agent); 83262306a36Sopenharmony_ci } else if (offset == 0x08 && length == 8) { 83362306a36Sopenharmony_ci /* ORB_POINTER */ 83462306a36Sopenharmony_ci rcode = tgt_agent_rw_orb_pointer(card, tcode, data, agent); 83562306a36Sopenharmony_ci } else if (offset == 0x10 && length == 4) { 83662306a36Sopenharmony_ci /* DOORBELL */ 83762306a36Sopenharmony_ci rcode = tgt_agent_rw_doorbell(card, tcode, data, agent); 83862306a36Sopenharmony_ci } else if (offset == 0x14 && length == 4) { 83962306a36Sopenharmony_ci /* UNSOLICITED_STATUS_ENABLE */ 84062306a36Sopenharmony_ci rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode, 84162306a36Sopenharmony_ci data, agent); 84262306a36Sopenharmony_ci } else { 84362306a36Sopenharmony_ci rcode = RCODE_ADDRESS_ERROR; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ciout: 84762306a36Sopenharmony_ci fw_send_response(card, request, rcode); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic void sbp_handle_command(struct sbp_target_request *); 85162306a36Sopenharmony_cistatic int sbp_send_status(struct sbp_target_request *); 85262306a36Sopenharmony_cistatic void sbp_free_request(struct sbp_target_request *); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic void tgt_agent_process_work(struct work_struct *work) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct sbp_target_request *req = 85762306a36Sopenharmony_ci container_of(work, struct sbp_target_request, work); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci pr_debug("tgt_orb ptr:0x%llx next_ORB:0x%llx data_descriptor:0x%llx misc:0x%x\n", 86062306a36Sopenharmony_ci req->orb_pointer, 86162306a36Sopenharmony_ci sbp2_pointer_to_addr(&req->orb.next_orb), 86262306a36Sopenharmony_ci sbp2_pointer_to_addr(&req->orb.data_descriptor), 86362306a36Sopenharmony_ci be32_to_cpu(req->orb.misc)); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (req->orb_pointer >> 32) 86662306a36Sopenharmony_ci pr_debug("ORB with high bits set\n"); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) { 86962306a36Sopenharmony_ci case 0:/* Format specified by this standard */ 87062306a36Sopenharmony_ci sbp_handle_command(req); 87162306a36Sopenharmony_ci return; 87262306a36Sopenharmony_ci case 1: /* Reserved for future standardization */ 87362306a36Sopenharmony_ci case 2: /* Vendor-dependent */ 87462306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 87562306a36Sopenharmony_ci STATUS_BLOCK_RESP( 87662306a36Sopenharmony_ci STATUS_RESP_REQUEST_COMPLETE) | 87762306a36Sopenharmony_ci STATUS_BLOCK_DEAD(0) | 87862306a36Sopenharmony_ci STATUS_BLOCK_LEN(1) | 87962306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS( 88062306a36Sopenharmony_ci SBP_STATUS_REQ_TYPE_NOTSUPP)); 88162306a36Sopenharmony_ci sbp_send_status(req); 88262306a36Sopenharmony_ci return; 88362306a36Sopenharmony_ci case 3: /* Dummy ORB */ 88462306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 88562306a36Sopenharmony_ci STATUS_BLOCK_RESP( 88662306a36Sopenharmony_ci STATUS_RESP_REQUEST_COMPLETE) | 88762306a36Sopenharmony_ci STATUS_BLOCK_DEAD(0) | 88862306a36Sopenharmony_ci STATUS_BLOCK_LEN(1) | 88962306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS( 89062306a36Sopenharmony_ci SBP_STATUS_DUMMY_ORB_COMPLETE)); 89162306a36Sopenharmony_ci sbp_send_status(req); 89262306a36Sopenharmony_ci return; 89362306a36Sopenharmony_ci default: 89462306a36Sopenharmony_ci BUG(); 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci/* used to double-check we haven't been issued an AGENT_RESET */ 89962306a36Sopenharmony_cistatic inline bool tgt_agent_check_active(struct sbp_target_agent *agent) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci bool active; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 90462306a36Sopenharmony_ci active = (agent->state == AGENT_STATE_ACTIVE); 90562306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci return active; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic struct sbp_target_request *sbp_mgt_get_req(struct sbp_session *sess, 91162306a36Sopenharmony_ci struct fw_card *card, u64 next_orb) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct se_session *se_sess = sess->se_sess; 91462306a36Sopenharmony_ci struct sbp_target_request *req; 91562306a36Sopenharmony_ci int tag, cpu; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci tag = sbitmap_queue_get(&se_sess->sess_tag_pool, &cpu); 91862306a36Sopenharmony_ci if (tag < 0) 91962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci req = &((struct sbp_target_request *)se_sess->sess_cmd_map)[tag]; 92262306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 92362306a36Sopenharmony_ci req->se_cmd.map_tag = tag; 92462306a36Sopenharmony_ci req->se_cmd.map_cpu = cpu; 92562306a36Sopenharmony_ci req->se_cmd.tag = next_orb; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return req; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic void tgt_agent_fetch_work(struct work_struct *work) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct sbp_target_agent *agent = 93362306a36Sopenharmony_ci container_of(work, struct sbp_target_agent, work); 93462306a36Sopenharmony_ci struct sbp_session *sess = agent->login->sess; 93562306a36Sopenharmony_ci struct sbp_target_request *req; 93662306a36Sopenharmony_ci int ret; 93762306a36Sopenharmony_ci bool doorbell = agent->doorbell; 93862306a36Sopenharmony_ci u64 next_orb = agent->orb_pointer; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci while (next_orb && tgt_agent_check_active(agent)) { 94162306a36Sopenharmony_ci req = sbp_mgt_get_req(sess, sess->card, next_orb); 94262306a36Sopenharmony_ci if (IS_ERR(req)) { 94362306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 94462306a36Sopenharmony_ci agent->state = AGENT_STATE_DEAD; 94562306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 94662306a36Sopenharmony_ci return; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci req->login = agent->login; 95062306a36Sopenharmony_ci req->orb_pointer = next_orb; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci req->status.status = cpu_to_be32(STATUS_BLOCK_ORB_OFFSET_HIGH( 95362306a36Sopenharmony_ci req->orb_pointer >> 32)); 95462306a36Sopenharmony_ci req->status.orb_low = cpu_to_be32( 95562306a36Sopenharmony_ci req->orb_pointer & 0xfffffffc); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* read in the ORB */ 95862306a36Sopenharmony_ci ret = sbp_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST, 95962306a36Sopenharmony_ci sess->node_id, sess->generation, sess->speed, 96062306a36Sopenharmony_ci req->orb_pointer, &req->orb, sizeof(req->orb)); 96162306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) { 96262306a36Sopenharmony_ci pr_debug("tgt_orb fetch failed: %x\n", ret); 96362306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 96462306a36Sopenharmony_ci STATUS_BLOCK_SRC( 96562306a36Sopenharmony_ci STATUS_SRC_ORB_FINISHED) | 96662306a36Sopenharmony_ci STATUS_BLOCK_RESP( 96762306a36Sopenharmony_ci STATUS_RESP_TRANSPORT_FAILURE) | 96862306a36Sopenharmony_ci STATUS_BLOCK_DEAD(1) | 96962306a36Sopenharmony_ci STATUS_BLOCK_LEN(1) | 97062306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS( 97162306a36Sopenharmony_ci SBP_STATUS_UNSPECIFIED_ERROR)); 97262306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 97362306a36Sopenharmony_ci agent->state = AGENT_STATE_DEAD; 97462306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci sbp_send_status(req); 97762306a36Sopenharmony_ci return; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* check the next_ORB field */ 98162306a36Sopenharmony_ci if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) { 98262306a36Sopenharmony_ci next_orb = 0; 98362306a36Sopenharmony_ci req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC( 98462306a36Sopenharmony_ci STATUS_SRC_ORB_FINISHED)); 98562306a36Sopenharmony_ci } else { 98662306a36Sopenharmony_ci next_orb = sbp2_pointer_to_addr(&req->orb.next_orb); 98762306a36Sopenharmony_ci req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC( 98862306a36Sopenharmony_ci STATUS_SRC_ORB_CONTINUING)); 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (tgt_agent_check_active(agent) && !doorbell) { 99262306a36Sopenharmony_ci INIT_WORK(&req->work, tgt_agent_process_work); 99362306a36Sopenharmony_ci queue_work(system_unbound_wq, &req->work); 99462306a36Sopenharmony_ci } else { 99562306a36Sopenharmony_ci /* don't process this request, just check next_ORB */ 99662306a36Sopenharmony_ci sbp_free_request(req); 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 100062306a36Sopenharmony_ci doorbell = agent->doorbell = false; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* check if we should carry on processing */ 100362306a36Sopenharmony_ci if (next_orb) 100462306a36Sopenharmony_ci agent->orb_pointer = next_orb; 100562306a36Sopenharmony_ci else 100662306a36Sopenharmony_ci agent->state = AGENT_STATE_SUSPENDED; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic struct sbp_target_agent *sbp_target_agent_register( 101362306a36Sopenharmony_ci struct sbp_login_descriptor *login) 101462306a36Sopenharmony_ci{ 101562306a36Sopenharmony_ci struct sbp_target_agent *agent; 101662306a36Sopenharmony_ci int ret; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci agent = kmalloc(sizeof(*agent), GFP_KERNEL); 101962306a36Sopenharmony_ci if (!agent) 102062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci spin_lock_init(&agent->lock); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci agent->handler.length = 0x20; 102562306a36Sopenharmony_ci agent->handler.address_callback = tgt_agent_rw; 102662306a36Sopenharmony_ci agent->handler.callback_data = agent; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci agent->login = login; 102962306a36Sopenharmony_ci agent->state = AGENT_STATE_RESET; 103062306a36Sopenharmony_ci INIT_WORK(&agent->work, tgt_agent_fetch_work); 103162306a36Sopenharmony_ci agent->orb_pointer = 0; 103262306a36Sopenharmony_ci agent->doorbell = false; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci ret = fw_core_add_address_handler(&agent->handler, 103562306a36Sopenharmony_ci &sbp_register_region); 103662306a36Sopenharmony_ci if (ret < 0) { 103762306a36Sopenharmony_ci kfree(agent); 103862306a36Sopenharmony_ci return ERR_PTR(ret); 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci return agent; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic void sbp_target_agent_unregister(struct sbp_target_agent *agent) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci fw_core_remove_address_handler(&agent->handler); 104762306a36Sopenharmony_ci cancel_work_sync(&agent->work); 104862306a36Sopenharmony_ci kfree(agent); 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci/* 105262306a36Sopenharmony_ci * Simple wrapper around fw_run_transaction that retries the transaction several 105362306a36Sopenharmony_ci * times in case of failure, with an exponential backoff. 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_cistatic int sbp_run_transaction(struct fw_card *card, int tcode, int destination_id, 105662306a36Sopenharmony_ci int generation, int speed, unsigned long long offset, 105762306a36Sopenharmony_ci void *payload, size_t length) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci int attempt, ret, delay; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci for (attempt = 1; attempt <= 5; attempt++) { 106262306a36Sopenharmony_ci ret = fw_run_transaction(card, tcode, destination_id, 106362306a36Sopenharmony_ci generation, speed, offset, payload, length); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci switch (ret) { 106662306a36Sopenharmony_ci case RCODE_COMPLETE: 106762306a36Sopenharmony_ci case RCODE_TYPE_ERROR: 106862306a36Sopenharmony_ci case RCODE_ADDRESS_ERROR: 106962306a36Sopenharmony_ci case RCODE_GENERATION: 107062306a36Sopenharmony_ci return ret; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci default: 107362306a36Sopenharmony_ci delay = 5 * attempt * attempt; 107462306a36Sopenharmony_ci usleep_range(delay, delay * 2); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci return ret; 107962306a36Sopenharmony_ci} 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci/* 108262306a36Sopenharmony_ci * Wrapper around sbp_run_transaction that gets the card, destination, 108362306a36Sopenharmony_ci * generation and speed out of the request's session. 108462306a36Sopenharmony_ci */ 108562306a36Sopenharmony_cistatic int sbp_run_request_transaction(struct sbp_target_request *req, 108662306a36Sopenharmony_ci int tcode, unsigned long long offset, void *payload, 108762306a36Sopenharmony_ci size_t length) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct sbp_login_descriptor *login = req->login; 109062306a36Sopenharmony_ci struct sbp_session *sess = login->sess; 109162306a36Sopenharmony_ci struct fw_card *card; 109262306a36Sopenharmony_ci int node_id, generation, speed, ret; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 109562306a36Sopenharmony_ci card = fw_card_get(sess->card); 109662306a36Sopenharmony_ci node_id = sess->node_id; 109762306a36Sopenharmony_ci generation = sess->generation; 109862306a36Sopenharmony_ci speed = sess->speed; 109962306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci ret = sbp_run_transaction(card, tcode, node_id, generation, speed, 110262306a36Sopenharmony_ci offset, payload, length); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci fw_card_put(card); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci return ret; 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic int sbp_fetch_command(struct sbp_target_request *req) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci int ret, cmd_len, copy_len; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci cmd_len = scsi_command_size(req->orb.command_block); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL); 111662306a36Sopenharmony_ci if (!req->cmd_buf) 111762306a36Sopenharmony_ci return -ENOMEM; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci memcpy(req->cmd_buf, req->orb.command_block, 112062306a36Sopenharmony_ci min_t(int, cmd_len, sizeof(req->orb.command_block))); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci if (cmd_len > sizeof(req->orb.command_block)) { 112362306a36Sopenharmony_ci pr_debug("sbp_fetch_command: filling in long command\n"); 112462306a36Sopenharmony_ci copy_len = cmd_len - sizeof(req->orb.command_block); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci ret = sbp_run_request_transaction(req, 112762306a36Sopenharmony_ci TCODE_READ_BLOCK_REQUEST, 112862306a36Sopenharmony_ci req->orb_pointer + sizeof(req->orb), 112962306a36Sopenharmony_ci req->cmd_buf + sizeof(req->orb.command_block), 113062306a36Sopenharmony_ci copy_len); 113162306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) 113262306a36Sopenharmony_ci return -EIO; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci return 0; 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistatic int sbp_fetch_page_table(struct sbp_target_request *req) 113962306a36Sopenharmony_ci{ 114062306a36Sopenharmony_ci int pg_tbl_sz, ret; 114162306a36Sopenharmony_ci struct sbp_page_table_entry *pg_tbl; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc))) 114462306a36Sopenharmony_ci return 0; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) * 114762306a36Sopenharmony_ci sizeof(struct sbp_page_table_entry); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL); 115062306a36Sopenharmony_ci if (!pg_tbl) 115162306a36Sopenharmony_ci return -ENOMEM; 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci ret = sbp_run_request_transaction(req, TCODE_READ_BLOCK_REQUEST, 115462306a36Sopenharmony_ci sbp2_pointer_to_addr(&req->orb.data_descriptor), 115562306a36Sopenharmony_ci pg_tbl, pg_tbl_sz); 115662306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) { 115762306a36Sopenharmony_ci kfree(pg_tbl); 115862306a36Sopenharmony_ci return -EIO; 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci req->pg_tbl = pg_tbl; 116262306a36Sopenharmony_ci return 0; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic void sbp_calc_data_length_direction(struct sbp_target_request *req, 116662306a36Sopenharmony_ci u32 *data_len, enum dma_data_direction *data_dir) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci int data_size, direction, idx; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)); 117162306a36Sopenharmony_ci direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc)); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if (!data_size) { 117462306a36Sopenharmony_ci *data_len = 0; 117562306a36Sopenharmony_ci *data_dir = DMA_NONE; 117662306a36Sopenharmony_ci return; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci *data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (req->pg_tbl) { 118262306a36Sopenharmony_ci *data_len = 0; 118362306a36Sopenharmony_ci for (idx = 0; idx < data_size; idx++) { 118462306a36Sopenharmony_ci *data_len += be16_to_cpu( 118562306a36Sopenharmony_ci req->pg_tbl[idx].segment_length); 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci } else { 118862306a36Sopenharmony_ci *data_len = data_size; 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cistatic void sbp_handle_command(struct sbp_target_request *req) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci struct sbp_login_descriptor *login = req->login; 119562306a36Sopenharmony_ci struct sbp_session *sess = login->sess; 119662306a36Sopenharmony_ci int ret, unpacked_lun; 119762306a36Sopenharmony_ci u32 data_length; 119862306a36Sopenharmony_ci enum dma_data_direction data_dir; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci ret = sbp_fetch_command(req); 120162306a36Sopenharmony_ci if (ret) { 120262306a36Sopenharmony_ci pr_debug("sbp_handle_command: fetch command failed: %d\n", ret); 120362306a36Sopenharmony_ci goto err; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci ret = sbp_fetch_page_table(req); 120762306a36Sopenharmony_ci if (ret) { 120862306a36Sopenharmony_ci pr_debug("sbp_handle_command: fetch page table failed: %d\n", 120962306a36Sopenharmony_ci ret); 121062306a36Sopenharmony_ci goto err; 121162306a36Sopenharmony_ci } 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci unpacked_lun = req->login->login_lun; 121462306a36Sopenharmony_ci sbp_calc_data_length_direction(req, &data_length, &data_dir); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci pr_debug("sbp_handle_command ORB:0x%llx unpacked_lun:%d data_len:%d data_dir:%d\n", 121762306a36Sopenharmony_ci req->orb_pointer, unpacked_lun, data_length, data_dir); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* only used for printk until we do TMRs */ 122062306a36Sopenharmony_ci req->se_cmd.tag = req->orb_pointer; 122162306a36Sopenharmony_ci target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf, 122262306a36Sopenharmony_ci req->sense_buf, unpacked_lun, data_length, 122362306a36Sopenharmony_ci TCM_SIMPLE_TAG, data_dir, TARGET_SCF_ACK_KREF); 122462306a36Sopenharmony_ci return; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cierr: 122762306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 122862306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) | 122962306a36Sopenharmony_ci STATUS_BLOCK_DEAD(0) | 123062306a36Sopenharmony_ci STATUS_BLOCK_LEN(1) | 123162306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR)); 123262306a36Sopenharmony_ci sbp_send_status(req); 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci/* 123662306a36Sopenharmony_ci * DMA_TO_DEVICE = read from initiator (SCSI WRITE) 123762306a36Sopenharmony_ci * DMA_FROM_DEVICE = write to initiator (SCSI READ) 123862306a36Sopenharmony_ci */ 123962306a36Sopenharmony_cistatic int sbp_rw_data(struct sbp_target_request *req) 124062306a36Sopenharmony_ci{ 124162306a36Sopenharmony_ci struct sbp_session *sess = req->login->sess; 124262306a36Sopenharmony_ci int tcode, sg_miter_flags, max_payload, pg_size, speed, node_id, 124362306a36Sopenharmony_ci generation, num_pte, length, tfr_length, 124462306a36Sopenharmony_ci rcode = RCODE_COMPLETE; 124562306a36Sopenharmony_ci struct sbp_page_table_entry *pte; 124662306a36Sopenharmony_ci unsigned long long offset; 124762306a36Sopenharmony_ci struct fw_card *card; 124862306a36Sopenharmony_ci struct sg_mapping_iter iter; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (req->se_cmd.data_direction == DMA_FROM_DEVICE) { 125162306a36Sopenharmony_ci tcode = TCODE_WRITE_BLOCK_REQUEST; 125262306a36Sopenharmony_ci sg_miter_flags = SG_MITER_FROM_SG; 125362306a36Sopenharmony_ci } else { 125462306a36Sopenharmony_ci tcode = TCODE_READ_BLOCK_REQUEST; 125562306a36Sopenharmony_ci sg_miter_flags = SG_MITER_TO_SG; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc)); 125962306a36Sopenharmony_ci speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc)); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc)); 126262306a36Sopenharmony_ci if (pg_size) { 126362306a36Sopenharmony_ci pr_err("sbp_run_transaction: page size ignored\n"); 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci spin_lock_bh(&sess->lock); 126762306a36Sopenharmony_ci card = fw_card_get(sess->card); 126862306a36Sopenharmony_ci node_id = sess->node_id; 126962306a36Sopenharmony_ci generation = sess->generation; 127062306a36Sopenharmony_ci spin_unlock_bh(&sess->lock); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci if (req->pg_tbl) { 127362306a36Sopenharmony_ci pte = req->pg_tbl; 127462306a36Sopenharmony_ci num_pte = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci offset = 0; 127762306a36Sopenharmony_ci length = 0; 127862306a36Sopenharmony_ci } else { 127962306a36Sopenharmony_ci pte = NULL; 128062306a36Sopenharmony_ci num_pte = 0; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci offset = sbp2_pointer_to_addr(&req->orb.data_descriptor); 128362306a36Sopenharmony_ci length = req->se_cmd.data_length; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci sg_miter_start(&iter, req->se_cmd.t_data_sg, req->se_cmd.t_data_nents, 128762306a36Sopenharmony_ci sg_miter_flags); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci while (length || num_pte) { 129062306a36Sopenharmony_ci if (!length) { 129162306a36Sopenharmony_ci offset = (u64)be16_to_cpu(pte->segment_base_hi) << 32 | 129262306a36Sopenharmony_ci be32_to_cpu(pte->segment_base_lo); 129362306a36Sopenharmony_ci length = be16_to_cpu(pte->segment_length); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci pte++; 129662306a36Sopenharmony_ci num_pte--; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci sg_miter_next(&iter); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci tfr_length = min3(length, max_payload, (int)iter.length); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci /* FIXME: take page_size into account */ 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci rcode = sbp_run_transaction(card, tcode, node_id, 130662306a36Sopenharmony_ci generation, speed, 130762306a36Sopenharmony_ci offset, iter.addr, tfr_length); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (rcode != RCODE_COMPLETE) 131062306a36Sopenharmony_ci break; 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci length -= tfr_length; 131362306a36Sopenharmony_ci offset += tfr_length; 131462306a36Sopenharmony_ci iter.consumed = tfr_length; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci sg_miter_stop(&iter); 131862306a36Sopenharmony_ci fw_card_put(card); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci if (rcode == RCODE_COMPLETE) { 132162306a36Sopenharmony_ci WARN_ON(length != 0); 132262306a36Sopenharmony_ci return 0; 132362306a36Sopenharmony_ci } else { 132462306a36Sopenharmony_ci return -EIO; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci} 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_cistatic int sbp_send_status(struct sbp_target_request *req) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci int rc, ret = 0, length; 133162306a36Sopenharmony_ci struct sbp_login_descriptor *login = req->login; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci rc = sbp_run_request_transaction(req, TCODE_WRITE_BLOCK_REQUEST, 133662306a36Sopenharmony_ci login->status_fifo_addr, &req->status, length); 133762306a36Sopenharmony_ci if (rc != RCODE_COMPLETE) { 133862306a36Sopenharmony_ci pr_debug("sbp_send_status: write failed: 0x%x\n", rc); 133962306a36Sopenharmony_ci ret = -EIO; 134062306a36Sopenharmony_ci goto put_ref; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci pr_debug("sbp_send_status: status write complete for ORB: 0x%llx\n", 134462306a36Sopenharmony_ci req->orb_pointer); 134562306a36Sopenharmony_ci /* 134662306a36Sopenharmony_ci * Drop the extra ACK_KREF reference taken by target_submit_cmd() 134762306a36Sopenharmony_ci * ahead of sbp_check_stop_free() -> transport_generic_free_cmd() 134862306a36Sopenharmony_ci * final se_cmd->cmd_kref put. 134962306a36Sopenharmony_ci */ 135062306a36Sopenharmony_ciput_ref: 135162306a36Sopenharmony_ci target_put_sess_cmd(&req->se_cmd); 135262306a36Sopenharmony_ci return ret; 135362306a36Sopenharmony_ci} 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_cistatic void sbp_sense_mangle(struct sbp_target_request *req) 135662306a36Sopenharmony_ci{ 135762306a36Sopenharmony_ci struct se_cmd *se_cmd = &req->se_cmd; 135862306a36Sopenharmony_ci u8 *sense = req->sense_buf; 135962306a36Sopenharmony_ci u8 *status = req->status.data; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci WARN_ON(se_cmd->scsi_sense_length < 18); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci switch (sense[0] & 0x7f) { /* sfmt */ 136462306a36Sopenharmony_ci case 0x70: /* current, fixed */ 136562306a36Sopenharmony_ci status[0] = 0 << 6; 136662306a36Sopenharmony_ci break; 136762306a36Sopenharmony_ci case 0x71: /* deferred, fixed */ 136862306a36Sopenharmony_ci status[0] = 1 << 6; 136962306a36Sopenharmony_ci break; 137062306a36Sopenharmony_ci case 0x72: /* current, descriptor */ 137162306a36Sopenharmony_ci case 0x73: /* deferred, descriptor */ 137262306a36Sopenharmony_ci default: 137362306a36Sopenharmony_ci /* 137462306a36Sopenharmony_ci * TODO: SBP-3 specifies what we should do with descriptor 137562306a36Sopenharmony_ci * format sense data 137662306a36Sopenharmony_ci */ 137762306a36Sopenharmony_ci pr_err("sbp_send_sense: unknown sense format: 0x%x\n", 137862306a36Sopenharmony_ci sense[0]); 137962306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 138062306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 138162306a36Sopenharmony_ci STATUS_BLOCK_DEAD(0) | 138262306a36Sopenharmony_ci STATUS_BLOCK_LEN(1) | 138362306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED)); 138462306a36Sopenharmony_ci return; 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci status[0] |= se_cmd->scsi_status & 0x3f;/* status */ 138862306a36Sopenharmony_ci status[1] = 138962306a36Sopenharmony_ci (sense[0] & 0x80) | /* valid */ 139062306a36Sopenharmony_ci ((sense[2] & 0xe0) >> 1) | /* mark, eom, ili */ 139162306a36Sopenharmony_ci (sense[2] & 0x0f); /* sense_key */ 139262306a36Sopenharmony_ci status[2] = 0; /* XXX sense_code */ 139362306a36Sopenharmony_ci status[3] = 0; /* XXX sense_qualifier */ 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci /* information */ 139662306a36Sopenharmony_ci status[4] = sense[3]; 139762306a36Sopenharmony_ci status[5] = sense[4]; 139862306a36Sopenharmony_ci status[6] = sense[5]; 139962306a36Sopenharmony_ci status[7] = sense[6]; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci /* CDB-dependent */ 140262306a36Sopenharmony_ci status[8] = sense[8]; 140362306a36Sopenharmony_ci status[9] = sense[9]; 140462306a36Sopenharmony_ci status[10] = sense[10]; 140562306a36Sopenharmony_ci status[11] = sense[11]; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci /* fru */ 140862306a36Sopenharmony_ci status[12] = sense[14]; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci /* sense_key-dependent */ 141162306a36Sopenharmony_ci status[13] = sense[15]; 141262306a36Sopenharmony_ci status[14] = sense[16]; 141362306a36Sopenharmony_ci status[15] = sense[17]; 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 141662306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 141762306a36Sopenharmony_ci STATUS_BLOCK_DEAD(0) | 141862306a36Sopenharmony_ci STATUS_BLOCK_LEN(5) | 141962306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK)); 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic int sbp_send_sense(struct sbp_target_request *req) 142362306a36Sopenharmony_ci{ 142462306a36Sopenharmony_ci struct se_cmd *se_cmd = &req->se_cmd; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (se_cmd->scsi_sense_length) { 142762306a36Sopenharmony_ci sbp_sense_mangle(req); 142862306a36Sopenharmony_ci } else { 142962306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 143062306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 143162306a36Sopenharmony_ci STATUS_BLOCK_DEAD(0) | 143262306a36Sopenharmony_ci STATUS_BLOCK_LEN(1) | 143362306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK)); 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci return sbp_send_status(req); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cistatic void sbp_free_request(struct sbp_target_request *req) 144062306a36Sopenharmony_ci{ 144162306a36Sopenharmony_ci struct se_cmd *se_cmd = &req->se_cmd; 144262306a36Sopenharmony_ci struct se_session *se_sess = se_cmd->se_sess; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci kfree(req->pg_tbl); 144562306a36Sopenharmony_ci kfree(req->cmd_buf); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci target_free_tag(se_sess, se_cmd); 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_cistatic void sbp_mgt_agent_process(struct work_struct *work) 145162306a36Sopenharmony_ci{ 145262306a36Sopenharmony_ci struct sbp_management_agent *agent = 145362306a36Sopenharmony_ci container_of(work, struct sbp_management_agent, work); 145462306a36Sopenharmony_ci struct sbp_management_request *req = agent->request; 145562306a36Sopenharmony_ci int ret; 145662306a36Sopenharmony_ci int status_data_len = 0; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* fetch the ORB from the initiator */ 145962306a36Sopenharmony_ci ret = sbp_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST, 146062306a36Sopenharmony_ci req->node_addr, req->generation, req->speed, 146162306a36Sopenharmony_ci agent->orb_offset, &req->orb, sizeof(req->orb)); 146262306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) { 146362306a36Sopenharmony_ci pr_debug("mgt_orb fetch failed: %x\n", ret); 146462306a36Sopenharmony_ci goto out; 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x status_fifo:0x%llx\n", 146862306a36Sopenharmony_ci sbp2_pointer_to_addr(&req->orb.ptr1), 146962306a36Sopenharmony_ci sbp2_pointer_to_addr(&req->orb.ptr2), 147062306a36Sopenharmony_ci be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length), 147162306a36Sopenharmony_ci sbp2_pointer_to_addr(&req->orb.status_fifo)); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) || 147462306a36Sopenharmony_ci ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) { 147562306a36Sopenharmony_ci pr_err("mgt_orb bad request\n"); 147662306a36Sopenharmony_ci goto out; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) { 148062306a36Sopenharmony_ci case MANAGEMENT_ORB_FUNCTION_LOGIN: 148162306a36Sopenharmony_ci sbp_management_request_login(agent, req, &status_data_len); 148262306a36Sopenharmony_ci break; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS: 148562306a36Sopenharmony_ci sbp_management_request_query_logins(agent, req, 148662306a36Sopenharmony_ci &status_data_len); 148762306a36Sopenharmony_ci break; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci case MANAGEMENT_ORB_FUNCTION_RECONNECT: 149062306a36Sopenharmony_ci sbp_management_request_reconnect(agent, req, &status_data_len); 149162306a36Sopenharmony_ci break; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD: 149462306a36Sopenharmony_ci pr_notice("SET PASSWORD not implemented\n"); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci req->status.status = cpu_to_be32( 149762306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 149862306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP)); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci break; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci case MANAGEMENT_ORB_FUNCTION_LOGOUT: 150362306a36Sopenharmony_ci sbp_management_request_logout(agent, req, &status_data_len); 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci case MANAGEMENT_ORB_FUNCTION_ABORT_TASK: 150762306a36Sopenharmony_ci pr_notice("ABORT TASK not implemented\n"); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci req->status.status = cpu_to_be32( 151062306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 151162306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP)); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci break; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET: 151662306a36Sopenharmony_ci pr_notice("ABORT TASK SET not implemented\n"); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci req->status.status = cpu_to_be32( 151962306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 152062306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP)); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci break; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET: 152562306a36Sopenharmony_ci pr_notice("LOGICAL UNIT RESET not implemented\n"); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci req->status.status = cpu_to_be32( 152862306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 152962306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP)); 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci break; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci case MANAGEMENT_ORB_FUNCTION_TARGET_RESET: 153462306a36Sopenharmony_ci pr_notice("TARGET RESET not implemented\n"); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci req->status.status = cpu_to_be32( 153762306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 153862306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP)); 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci break; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci default: 154362306a36Sopenharmony_ci pr_notice("unknown management function 0x%x\n", 154462306a36Sopenharmony_ci MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci req->status.status = cpu_to_be32( 154762306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) | 154862306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP)); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci break; 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 155462306a36Sopenharmony_ci STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */ 155562306a36Sopenharmony_ci STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) | 155662306a36Sopenharmony_ci STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32)); 155762306a36Sopenharmony_ci req->status.orb_low = cpu_to_be32(agent->orb_offset); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci /* write the status block back to the initiator */ 156062306a36Sopenharmony_ci ret = sbp_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST, 156162306a36Sopenharmony_ci req->node_addr, req->generation, req->speed, 156262306a36Sopenharmony_ci sbp2_pointer_to_addr(&req->orb.status_fifo), 156362306a36Sopenharmony_ci &req->status, 8 + status_data_len); 156462306a36Sopenharmony_ci if (ret != RCODE_COMPLETE) { 156562306a36Sopenharmony_ci pr_debug("mgt_orb status write failed: %x\n", ret); 156662306a36Sopenharmony_ci goto out; 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ciout: 157062306a36Sopenharmony_ci fw_card_put(req->card); 157162306a36Sopenharmony_ci kfree(req); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 157462306a36Sopenharmony_ci agent->state = MANAGEMENT_AGENT_STATE_IDLE; 157562306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 157662306a36Sopenharmony_ci} 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_cistatic void sbp_mgt_agent_rw(struct fw_card *card, 157962306a36Sopenharmony_ci struct fw_request *request, int tcode, int destination, int source, 158062306a36Sopenharmony_ci int generation, unsigned long long offset, void *data, size_t length, 158162306a36Sopenharmony_ci void *callback_data) 158262306a36Sopenharmony_ci{ 158362306a36Sopenharmony_ci struct sbp_management_agent *agent = callback_data; 158462306a36Sopenharmony_ci struct sbp2_pointer *ptr = data; 158562306a36Sopenharmony_ci int rcode = RCODE_ADDRESS_ERROR; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (!agent->tport->enable) 158862306a36Sopenharmony_ci goto out; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if ((offset != agent->handler.offset) || (length != 8)) 159162306a36Sopenharmony_ci goto out; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci if (tcode == TCODE_WRITE_BLOCK_REQUEST) { 159462306a36Sopenharmony_ci struct sbp_management_request *req; 159562306a36Sopenharmony_ci int prev_state; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci spin_lock_bh(&agent->lock); 159862306a36Sopenharmony_ci prev_state = agent->state; 159962306a36Sopenharmony_ci agent->state = MANAGEMENT_AGENT_STATE_BUSY; 160062306a36Sopenharmony_ci spin_unlock_bh(&agent->lock); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci if (prev_state == MANAGEMENT_AGENT_STATE_BUSY) { 160362306a36Sopenharmony_ci pr_notice("ignoring management request while busy\n"); 160462306a36Sopenharmony_ci rcode = RCODE_CONFLICT_ERROR; 160562306a36Sopenharmony_ci goto out; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_ATOMIC); 160862306a36Sopenharmony_ci if (!req) { 160962306a36Sopenharmony_ci rcode = RCODE_CONFLICT_ERROR; 161062306a36Sopenharmony_ci goto out; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci req->card = fw_card_get(card); 161462306a36Sopenharmony_ci req->generation = generation; 161562306a36Sopenharmony_ci req->node_addr = source; 161662306a36Sopenharmony_ci req->speed = fw_get_request_speed(request); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci agent->orb_offset = sbp2_pointer_to_addr(ptr); 161962306a36Sopenharmony_ci agent->request = req; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci queue_work(system_unbound_wq, &agent->work); 162262306a36Sopenharmony_ci rcode = RCODE_COMPLETE; 162362306a36Sopenharmony_ci } else if (tcode == TCODE_READ_BLOCK_REQUEST) { 162462306a36Sopenharmony_ci addr_to_sbp2_pointer(agent->orb_offset, ptr); 162562306a36Sopenharmony_ci rcode = RCODE_COMPLETE; 162662306a36Sopenharmony_ci } else { 162762306a36Sopenharmony_ci rcode = RCODE_TYPE_ERROR; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ciout: 163162306a36Sopenharmony_ci fw_send_response(card, request, rcode); 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_cistatic struct sbp_management_agent *sbp_management_agent_register( 163562306a36Sopenharmony_ci struct sbp_tport *tport) 163662306a36Sopenharmony_ci{ 163762306a36Sopenharmony_ci int ret; 163862306a36Sopenharmony_ci struct sbp_management_agent *agent; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci agent = kmalloc(sizeof(*agent), GFP_KERNEL); 164162306a36Sopenharmony_ci if (!agent) 164262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci spin_lock_init(&agent->lock); 164562306a36Sopenharmony_ci agent->tport = tport; 164662306a36Sopenharmony_ci agent->handler.length = 0x08; 164762306a36Sopenharmony_ci agent->handler.address_callback = sbp_mgt_agent_rw; 164862306a36Sopenharmony_ci agent->handler.callback_data = agent; 164962306a36Sopenharmony_ci agent->state = MANAGEMENT_AGENT_STATE_IDLE; 165062306a36Sopenharmony_ci INIT_WORK(&agent->work, sbp_mgt_agent_process); 165162306a36Sopenharmony_ci agent->orb_offset = 0; 165262306a36Sopenharmony_ci agent->request = NULL; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci ret = fw_core_add_address_handler(&agent->handler, 165562306a36Sopenharmony_ci &sbp_register_region); 165662306a36Sopenharmony_ci if (ret < 0) { 165762306a36Sopenharmony_ci kfree(agent); 165862306a36Sopenharmony_ci return ERR_PTR(ret); 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci return agent; 166262306a36Sopenharmony_ci} 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_cistatic void sbp_management_agent_unregister(struct sbp_management_agent *agent) 166562306a36Sopenharmony_ci{ 166662306a36Sopenharmony_ci fw_core_remove_address_handler(&agent->handler); 166762306a36Sopenharmony_ci cancel_work_sync(&agent->work); 166862306a36Sopenharmony_ci kfree(agent); 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_cistatic int sbp_check_true(struct se_portal_group *se_tpg) 167262306a36Sopenharmony_ci{ 167362306a36Sopenharmony_ci return 1; 167462306a36Sopenharmony_ci} 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_cistatic char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg) 167762306a36Sopenharmony_ci{ 167862306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 167962306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci return &tport->tport_name[0]; 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_cistatic u16 sbp_get_tag(struct se_portal_group *se_tpg) 168562306a36Sopenharmony_ci{ 168662306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 168762306a36Sopenharmony_ci return tpg->tport_tpgt; 168862306a36Sopenharmony_ci} 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic void sbp_release_cmd(struct se_cmd *se_cmd) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci struct sbp_target_request *req = container_of(se_cmd, 169362306a36Sopenharmony_ci struct sbp_target_request, se_cmd); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci sbp_free_request(req); 169662306a36Sopenharmony_ci} 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_cistatic int sbp_write_pending(struct se_cmd *se_cmd) 169962306a36Sopenharmony_ci{ 170062306a36Sopenharmony_ci struct sbp_target_request *req = container_of(se_cmd, 170162306a36Sopenharmony_ci struct sbp_target_request, se_cmd); 170262306a36Sopenharmony_ci int ret; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci ret = sbp_rw_data(req); 170562306a36Sopenharmony_ci if (ret) { 170662306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 170762306a36Sopenharmony_ci STATUS_BLOCK_RESP( 170862306a36Sopenharmony_ci STATUS_RESP_TRANSPORT_FAILURE) | 170962306a36Sopenharmony_ci STATUS_BLOCK_DEAD(0) | 171062306a36Sopenharmony_ci STATUS_BLOCK_LEN(1) | 171162306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS( 171262306a36Sopenharmony_ci SBP_STATUS_UNSPECIFIED_ERROR)); 171362306a36Sopenharmony_ci sbp_send_status(req); 171462306a36Sopenharmony_ci return ret; 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci target_execute_cmd(se_cmd); 171862306a36Sopenharmony_ci return 0; 171962306a36Sopenharmony_ci} 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_cistatic int sbp_queue_data_in(struct se_cmd *se_cmd) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci struct sbp_target_request *req = container_of(se_cmd, 172462306a36Sopenharmony_ci struct sbp_target_request, se_cmd); 172562306a36Sopenharmony_ci int ret; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci ret = sbp_rw_data(req); 172862306a36Sopenharmony_ci if (ret) { 172962306a36Sopenharmony_ci req->status.status |= cpu_to_be32( 173062306a36Sopenharmony_ci STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) | 173162306a36Sopenharmony_ci STATUS_BLOCK_DEAD(0) | 173262306a36Sopenharmony_ci STATUS_BLOCK_LEN(1) | 173362306a36Sopenharmony_ci STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR)); 173462306a36Sopenharmony_ci sbp_send_status(req); 173562306a36Sopenharmony_ci return ret; 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci return sbp_send_sense(req); 173962306a36Sopenharmony_ci} 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci/* 174262306a36Sopenharmony_ci * Called after command (no data transfer) or after the write (to device) 174362306a36Sopenharmony_ci * operation is completed 174462306a36Sopenharmony_ci */ 174562306a36Sopenharmony_cistatic int sbp_queue_status(struct se_cmd *se_cmd) 174662306a36Sopenharmony_ci{ 174762306a36Sopenharmony_ci struct sbp_target_request *req = container_of(se_cmd, 174862306a36Sopenharmony_ci struct sbp_target_request, se_cmd); 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return sbp_send_sense(req); 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_cistatic void sbp_queue_tm_rsp(struct se_cmd *se_cmd) 175462306a36Sopenharmony_ci{ 175562306a36Sopenharmony_ci} 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_cistatic void sbp_aborted_task(struct se_cmd *se_cmd) 175862306a36Sopenharmony_ci{ 175962306a36Sopenharmony_ci return; 176062306a36Sopenharmony_ci} 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_cistatic int sbp_check_stop_free(struct se_cmd *se_cmd) 176362306a36Sopenharmony_ci{ 176462306a36Sopenharmony_ci struct sbp_target_request *req = container_of(se_cmd, 176562306a36Sopenharmony_ci struct sbp_target_request, se_cmd); 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci return transport_generic_free_cmd(&req->se_cmd, 0); 176862306a36Sopenharmony_ci} 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_cistatic int sbp_count_se_tpg_luns(struct se_portal_group *tpg) 177162306a36Sopenharmony_ci{ 177262306a36Sopenharmony_ci struct se_lun *lun; 177362306a36Sopenharmony_ci int count = 0; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci rcu_read_lock(); 177662306a36Sopenharmony_ci hlist_for_each_entry_rcu(lun, &tpg->tpg_lun_hlist, link) 177762306a36Sopenharmony_ci count++; 177862306a36Sopenharmony_ci rcu_read_unlock(); 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci return count; 178162306a36Sopenharmony_ci} 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_cistatic int sbp_update_unit_directory(struct sbp_tport *tport) 178462306a36Sopenharmony_ci{ 178562306a36Sopenharmony_ci struct se_lun *lun; 178662306a36Sopenharmony_ci int num_luns, num_entries, idx = 0, mgt_agt_addr, ret; 178762306a36Sopenharmony_ci u32 *data; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci if (tport->unit_directory.data) { 179062306a36Sopenharmony_ci fw_core_remove_descriptor(&tport->unit_directory); 179162306a36Sopenharmony_ci kfree(tport->unit_directory.data); 179262306a36Sopenharmony_ci tport->unit_directory.data = NULL; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if (!tport->enable || !tport->tpg) 179662306a36Sopenharmony_ci return 0; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci num_luns = sbp_count_se_tpg_luns(&tport->tpg->se_tpg); 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci /* 180162306a36Sopenharmony_ci * Number of entries in the final unit directory: 180262306a36Sopenharmony_ci * - all of those in the template 180362306a36Sopenharmony_ci * - management_agent 180462306a36Sopenharmony_ci * - unit_characteristics 180562306a36Sopenharmony_ci * - reconnect_timeout 180662306a36Sopenharmony_ci * - unit unique ID 180762306a36Sopenharmony_ci * - one for each LUN 180862306a36Sopenharmony_ci * 180962306a36Sopenharmony_ci * MUST NOT include leaf or sub-directory entries 181062306a36Sopenharmony_ci */ 181162306a36Sopenharmony_ci num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci if (tport->directory_id != -1) 181462306a36Sopenharmony_ci num_entries++; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci /* allocate num_entries + 4 for the header and unique ID leaf */ 181762306a36Sopenharmony_ci data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL); 181862306a36Sopenharmony_ci if (!data) 181962306a36Sopenharmony_ci return -ENOMEM; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci /* directory_length */ 182262306a36Sopenharmony_ci data[idx++] = num_entries << 16; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci /* directory_id */ 182562306a36Sopenharmony_ci if (tport->directory_id != -1) 182662306a36Sopenharmony_ci data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci /* unit directory template */ 182962306a36Sopenharmony_ci memcpy(&data[idx], sbp_unit_directory_template, 183062306a36Sopenharmony_ci sizeof(sbp_unit_directory_template)); 183162306a36Sopenharmony_ci idx += ARRAY_SIZE(sbp_unit_directory_template); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* management_agent */ 183462306a36Sopenharmony_ci mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4; 183562306a36Sopenharmony_ci data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff); 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci /* unit_characteristics */ 183862306a36Sopenharmony_ci data[idx++] = 0x3a000000 | 183962306a36Sopenharmony_ci (((tport->mgt_orb_timeout * 2) << 8) & 0xff00) | 184062306a36Sopenharmony_ci SBP_ORB_FETCH_SIZE; 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci /* reconnect_timeout */ 184362306a36Sopenharmony_ci data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci /* unit unique ID (leaf is just after LUNs) */ 184662306a36Sopenharmony_ci data[idx++] = 0x8d000000 | (num_luns + 1); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci rcu_read_lock(); 184962306a36Sopenharmony_ci hlist_for_each_entry_rcu(lun, &tport->tpg->se_tpg.tpg_lun_hlist, link) { 185062306a36Sopenharmony_ci struct se_device *dev; 185162306a36Sopenharmony_ci int type; 185262306a36Sopenharmony_ci /* 185362306a36Sopenharmony_ci * rcu_dereference_raw protected by se_lun->lun_group symlink 185462306a36Sopenharmony_ci * reference to se_device->dev_group. 185562306a36Sopenharmony_ci */ 185662306a36Sopenharmony_ci dev = rcu_dereference_raw(lun->lun_se_dev); 185762306a36Sopenharmony_ci type = dev->transport->get_device_type(dev); 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci /* logical_unit_number */ 186062306a36Sopenharmony_ci data[idx++] = 0x14000000 | 186162306a36Sopenharmony_ci ((type << 16) & 0x1f0000) | 186262306a36Sopenharmony_ci (lun->unpacked_lun & 0xffff); 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci rcu_read_unlock(); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci /* unit unique ID leaf */ 186762306a36Sopenharmony_ci data[idx++] = 2 << 16; 186862306a36Sopenharmony_ci data[idx++] = tport->guid >> 32; 186962306a36Sopenharmony_ci data[idx++] = tport->guid; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci tport->unit_directory.length = idx; 187262306a36Sopenharmony_ci tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24; 187362306a36Sopenharmony_ci tport->unit_directory.data = data; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci ret = fw_core_add_descriptor(&tport->unit_directory); 187662306a36Sopenharmony_ci if (ret < 0) { 187762306a36Sopenharmony_ci kfree(tport->unit_directory.data); 187862306a36Sopenharmony_ci tport->unit_directory.data = NULL; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci return ret; 188262306a36Sopenharmony_ci} 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_cistatic ssize_t sbp_parse_wwn(const char *name, u64 *wwn) 188562306a36Sopenharmony_ci{ 188662306a36Sopenharmony_ci const char *cp; 188762306a36Sopenharmony_ci char c, nibble; 188862306a36Sopenharmony_ci int pos = 0, err; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci *wwn = 0; 189162306a36Sopenharmony_ci for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) { 189262306a36Sopenharmony_ci c = *cp; 189362306a36Sopenharmony_ci if (c == '\n' && cp[1] == '\0') 189462306a36Sopenharmony_ci continue; 189562306a36Sopenharmony_ci if (c == '\0') { 189662306a36Sopenharmony_ci err = 2; 189762306a36Sopenharmony_ci if (pos != 16) 189862306a36Sopenharmony_ci goto fail; 189962306a36Sopenharmony_ci return cp - name; 190062306a36Sopenharmony_ci } 190162306a36Sopenharmony_ci err = 3; 190262306a36Sopenharmony_ci if (isdigit(c)) 190362306a36Sopenharmony_ci nibble = c - '0'; 190462306a36Sopenharmony_ci else if (isxdigit(c)) 190562306a36Sopenharmony_ci nibble = tolower(c) - 'a' + 10; 190662306a36Sopenharmony_ci else 190762306a36Sopenharmony_ci goto fail; 190862306a36Sopenharmony_ci *wwn = (*wwn << 4) | nibble; 190962306a36Sopenharmony_ci pos++; 191062306a36Sopenharmony_ci } 191162306a36Sopenharmony_ci err = 4; 191262306a36Sopenharmony_cifail: 191362306a36Sopenharmony_ci printk(KERN_INFO "err %u len %zu pos %u\n", 191462306a36Sopenharmony_ci err, cp - name, pos); 191562306a36Sopenharmony_ci return -1; 191662306a36Sopenharmony_ci} 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_cistatic ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn) 191962306a36Sopenharmony_ci{ 192062306a36Sopenharmony_ci return snprintf(buf, len, "%016llx", wwn); 192162306a36Sopenharmony_ci} 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_cistatic int sbp_init_nodeacl(struct se_node_acl *se_nacl, const char *name) 192462306a36Sopenharmony_ci{ 192562306a36Sopenharmony_ci u64 guid = 0; 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci if (sbp_parse_wwn(name, &guid) < 0) 192862306a36Sopenharmony_ci return -EINVAL; 192962306a36Sopenharmony_ci return 0; 193062306a36Sopenharmony_ci} 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_cistatic int sbp_post_link_lun( 193362306a36Sopenharmony_ci struct se_portal_group *se_tpg, 193462306a36Sopenharmony_ci struct se_lun *se_lun) 193562306a36Sopenharmony_ci{ 193662306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci return sbp_update_unit_directory(tpg->tport); 193962306a36Sopenharmony_ci} 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_cistatic void sbp_pre_unlink_lun( 194262306a36Sopenharmony_ci struct se_portal_group *se_tpg, 194362306a36Sopenharmony_ci struct se_lun *se_lun) 194462306a36Sopenharmony_ci{ 194562306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 194662306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 194762306a36Sopenharmony_ci int ret; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0) 195062306a36Sopenharmony_ci tport->enable = 0; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci ret = sbp_update_unit_directory(tport); 195362306a36Sopenharmony_ci if (ret < 0) 195462306a36Sopenharmony_ci pr_err("unlink LUN: failed to update unit directory\n"); 195562306a36Sopenharmony_ci} 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_cistatic struct se_portal_group *sbp_make_tpg(struct se_wwn *wwn, 195862306a36Sopenharmony_ci const char *name) 195962306a36Sopenharmony_ci{ 196062306a36Sopenharmony_ci struct sbp_tport *tport = 196162306a36Sopenharmony_ci container_of(wwn, struct sbp_tport, tport_wwn); 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci struct sbp_tpg *tpg; 196462306a36Sopenharmony_ci unsigned long tpgt; 196562306a36Sopenharmony_ci int ret; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci if (strstr(name, "tpgt_") != name) 196862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 196962306a36Sopenharmony_ci if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX) 197062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci if (tport->tpg) { 197362306a36Sopenharmony_ci pr_err("Only one TPG per Unit is possible.\n"); 197462306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); 197862306a36Sopenharmony_ci if (!tpg) 197962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci tpg->tport = tport; 198262306a36Sopenharmony_ci tpg->tport_tpgt = tpgt; 198362306a36Sopenharmony_ci tport->tpg = tpg; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci /* default attribute values */ 198662306a36Sopenharmony_ci tport->enable = 0; 198762306a36Sopenharmony_ci tport->directory_id = -1; 198862306a36Sopenharmony_ci tport->mgt_orb_timeout = 15; 198962306a36Sopenharmony_ci tport->max_reconnect_timeout = 5; 199062306a36Sopenharmony_ci tport->max_logins_per_lun = 1; 199162306a36Sopenharmony_ci 199262306a36Sopenharmony_ci tport->mgt_agt = sbp_management_agent_register(tport); 199362306a36Sopenharmony_ci if (IS_ERR(tport->mgt_agt)) { 199462306a36Sopenharmony_ci ret = PTR_ERR(tport->mgt_agt); 199562306a36Sopenharmony_ci goto out_free_tpg; 199662306a36Sopenharmony_ci } 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_SBP); 199962306a36Sopenharmony_ci if (ret < 0) 200062306a36Sopenharmony_ci goto out_unreg_mgt_agt; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci return &tpg->se_tpg; 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ciout_unreg_mgt_agt: 200562306a36Sopenharmony_ci sbp_management_agent_unregister(tport->mgt_agt); 200662306a36Sopenharmony_ciout_free_tpg: 200762306a36Sopenharmony_ci tport->tpg = NULL; 200862306a36Sopenharmony_ci kfree(tpg); 200962306a36Sopenharmony_ci return ERR_PTR(ret); 201062306a36Sopenharmony_ci} 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_cistatic void sbp_drop_tpg(struct se_portal_group *se_tpg) 201362306a36Sopenharmony_ci{ 201462306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 201562306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci core_tpg_deregister(se_tpg); 201862306a36Sopenharmony_ci sbp_management_agent_unregister(tport->mgt_agt); 201962306a36Sopenharmony_ci tport->tpg = NULL; 202062306a36Sopenharmony_ci kfree(tpg); 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_cistatic struct se_wwn *sbp_make_tport( 202462306a36Sopenharmony_ci struct target_fabric_configfs *tf, 202562306a36Sopenharmony_ci struct config_group *group, 202662306a36Sopenharmony_ci const char *name) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci struct sbp_tport *tport; 202962306a36Sopenharmony_ci u64 guid = 0; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci if (sbp_parse_wwn(name, &guid) < 0) 203262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci tport = kzalloc(sizeof(*tport), GFP_KERNEL); 203562306a36Sopenharmony_ci if (!tport) 203662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci tport->guid = guid; 203962306a36Sopenharmony_ci sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid); 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci return &tport->tport_wwn; 204262306a36Sopenharmony_ci} 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_cistatic void sbp_drop_tport(struct se_wwn *wwn) 204562306a36Sopenharmony_ci{ 204662306a36Sopenharmony_ci struct sbp_tport *tport = 204762306a36Sopenharmony_ci container_of(wwn, struct sbp_tport, tport_wwn); 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci kfree(tport); 205062306a36Sopenharmony_ci} 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_cistatic ssize_t sbp_wwn_version_show(struct config_item *item, char *page) 205362306a36Sopenharmony_ci{ 205462306a36Sopenharmony_ci return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION); 205562306a36Sopenharmony_ci} 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ciCONFIGFS_ATTR_RO(sbp_wwn_, version); 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_cistatic struct configfs_attribute *sbp_wwn_attrs[] = { 206062306a36Sopenharmony_ci &sbp_wwn_attr_version, 206162306a36Sopenharmony_ci NULL, 206262306a36Sopenharmony_ci}; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_cistatic ssize_t sbp_tpg_directory_id_show(struct config_item *item, char *page) 206562306a36Sopenharmony_ci{ 206662306a36Sopenharmony_ci struct se_portal_group *se_tpg = to_tpg(item); 206762306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 206862306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci if (tport->directory_id == -1) 207162306a36Sopenharmony_ci return sprintf(page, "implicit\n"); 207262306a36Sopenharmony_ci else 207362306a36Sopenharmony_ci return sprintf(page, "%06x\n", tport->directory_id); 207462306a36Sopenharmony_ci} 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_cistatic ssize_t sbp_tpg_directory_id_store(struct config_item *item, 207762306a36Sopenharmony_ci const char *page, size_t count) 207862306a36Sopenharmony_ci{ 207962306a36Sopenharmony_ci struct se_portal_group *se_tpg = to_tpg(item); 208062306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 208162306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 208262306a36Sopenharmony_ci unsigned long val; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci if (tport->enable) { 208562306a36Sopenharmony_ci pr_err("Cannot change the directory_id on an active target.\n"); 208662306a36Sopenharmony_ci return -EBUSY; 208762306a36Sopenharmony_ci } 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci if (strstr(page, "implicit") == page) { 209062306a36Sopenharmony_ci tport->directory_id = -1; 209162306a36Sopenharmony_ci } else { 209262306a36Sopenharmony_ci if (kstrtoul(page, 16, &val) < 0) 209362306a36Sopenharmony_ci return -EINVAL; 209462306a36Sopenharmony_ci if (val > 0xffffff) 209562306a36Sopenharmony_ci return -EINVAL; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci tport->directory_id = val; 209862306a36Sopenharmony_ci } 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci return count; 210162306a36Sopenharmony_ci} 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_cistatic int sbp_enable_tpg(struct se_portal_group *se_tpg, bool enable) 210462306a36Sopenharmony_ci{ 210562306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 210662306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 210762306a36Sopenharmony_ci int ret; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci if (enable) { 211062306a36Sopenharmony_ci if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0) { 211162306a36Sopenharmony_ci pr_err("Cannot enable a target with no LUNs!\n"); 211262306a36Sopenharmony_ci return -EINVAL; 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci } else { 211562306a36Sopenharmony_ci /* XXX: force-shutdown sessions instead? */ 211662306a36Sopenharmony_ci spin_lock_bh(&se_tpg->session_lock); 211762306a36Sopenharmony_ci if (!list_empty(&se_tpg->tpg_sess_list)) { 211862306a36Sopenharmony_ci spin_unlock_bh(&se_tpg->session_lock); 211962306a36Sopenharmony_ci return -EBUSY; 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci spin_unlock_bh(&se_tpg->session_lock); 212262306a36Sopenharmony_ci } 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci tport->enable = enable; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci ret = sbp_update_unit_directory(tport); 212762306a36Sopenharmony_ci if (ret < 0) { 212862306a36Sopenharmony_ci pr_err("Could not update Config ROM\n"); 212962306a36Sopenharmony_ci return ret; 213062306a36Sopenharmony_ci } 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci return 0; 213362306a36Sopenharmony_ci} 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ciCONFIGFS_ATTR(sbp_tpg_, directory_id); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_cistatic struct configfs_attribute *sbp_tpg_base_attrs[] = { 213862306a36Sopenharmony_ci &sbp_tpg_attr_directory_id, 213962306a36Sopenharmony_ci NULL, 214062306a36Sopenharmony_ci}; 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_cistatic ssize_t sbp_tpg_attrib_mgt_orb_timeout_show(struct config_item *item, 214362306a36Sopenharmony_ci char *page) 214462306a36Sopenharmony_ci{ 214562306a36Sopenharmony_ci struct se_portal_group *se_tpg = attrib_to_tpg(item); 214662306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 214762306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 214862306a36Sopenharmony_ci return sprintf(page, "%d\n", tport->mgt_orb_timeout); 214962306a36Sopenharmony_ci} 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_cistatic ssize_t sbp_tpg_attrib_mgt_orb_timeout_store(struct config_item *item, 215262306a36Sopenharmony_ci const char *page, size_t count) 215362306a36Sopenharmony_ci{ 215462306a36Sopenharmony_ci struct se_portal_group *se_tpg = attrib_to_tpg(item); 215562306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 215662306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 215762306a36Sopenharmony_ci unsigned long val; 215862306a36Sopenharmony_ci int ret; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci if (kstrtoul(page, 0, &val) < 0) 216162306a36Sopenharmony_ci return -EINVAL; 216262306a36Sopenharmony_ci if ((val < 1) || (val > 127)) 216362306a36Sopenharmony_ci return -EINVAL; 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci if (tport->mgt_orb_timeout == val) 216662306a36Sopenharmony_ci return count; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci tport->mgt_orb_timeout = val; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci ret = sbp_update_unit_directory(tport); 217162306a36Sopenharmony_ci if (ret < 0) 217262306a36Sopenharmony_ci return ret; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci return count; 217562306a36Sopenharmony_ci} 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_cistatic ssize_t sbp_tpg_attrib_max_reconnect_timeout_show(struct config_item *item, 217862306a36Sopenharmony_ci char *page) 217962306a36Sopenharmony_ci{ 218062306a36Sopenharmony_ci struct se_portal_group *se_tpg = attrib_to_tpg(item); 218162306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 218262306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 218362306a36Sopenharmony_ci return sprintf(page, "%d\n", tport->max_reconnect_timeout); 218462306a36Sopenharmony_ci} 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_cistatic ssize_t sbp_tpg_attrib_max_reconnect_timeout_store(struct config_item *item, 218762306a36Sopenharmony_ci const char *page, size_t count) 218862306a36Sopenharmony_ci{ 218962306a36Sopenharmony_ci struct se_portal_group *se_tpg = attrib_to_tpg(item); 219062306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 219162306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 219262306a36Sopenharmony_ci unsigned long val; 219362306a36Sopenharmony_ci int ret; 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci if (kstrtoul(page, 0, &val) < 0) 219662306a36Sopenharmony_ci return -EINVAL; 219762306a36Sopenharmony_ci if ((val < 1) || (val > 32767)) 219862306a36Sopenharmony_ci return -EINVAL; 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci if (tport->max_reconnect_timeout == val) 220162306a36Sopenharmony_ci return count; 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci tport->max_reconnect_timeout = val; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci ret = sbp_update_unit_directory(tport); 220662306a36Sopenharmony_ci if (ret < 0) 220762306a36Sopenharmony_ci return ret; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci return count; 221062306a36Sopenharmony_ci} 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_cistatic ssize_t sbp_tpg_attrib_max_logins_per_lun_show(struct config_item *item, 221362306a36Sopenharmony_ci char *page) 221462306a36Sopenharmony_ci{ 221562306a36Sopenharmony_ci struct se_portal_group *se_tpg = attrib_to_tpg(item); 221662306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 221762306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 221862306a36Sopenharmony_ci return sprintf(page, "%d\n", tport->max_logins_per_lun); 221962306a36Sopenharmony_ci} 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_cistatic ssize_t sbp_tpg_attrib_max_logins_per_lun_store(struct config_item *item, 222262306a36Sopenharmony_ci const char *page, size_t count) 222362306a36Sopenharmony_ci{ 222462306a36Sopenharmony_ci struct se_portal_group *se_tpg = attrib_to_tpg(item); 222562306a36Sopenharmony_ci struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg); 222662306a36Sopenharmony_ci struct sbp_tport *tport = tpg->tport; 222762306a36Sopenharmony_ci unsigned long val; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_ci if (kstrtoul(page, 0, &val) < 0) 223062306a36Sopenharmony_ci return -EINVAL; 223162306a36Sopenharmony_ci if ((val < 1) || (val > 127)) 223262306a36Sopenharmony_ci return -EINVAL; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci /* XXX: also check against current count? */ 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci tport->max_logins_per_lun = val; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci return count; 223962306a36Sopenharmony_ci} 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ciCONFIGFS_ATTR(sbp_tpg_attrib_, mgt_orb_timeout); 224262306a36Sopenharmony_ciCONFIGFS_ATTR(sbp_tpg_attrib_, max_reconnect_timeout); 224362306a36Sopenharmony_ciCONFIGFS_ATTR(sbp_tpg_attrib_, max_logins_per_lun); 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_cistatic struct configfs_attribute *sbp_tpg_attrib_attrs[] = { 224662306a36Sopenharmony_ci &sbp_tpg_attrib_attr_mgt_orb_timeout, 224762306a36Sopenharmony_ci &sbp_tpg_attrib_attr_max_reconnect_timeout, 224862306a36Sopenharmony_ci &sbp_tpg_attrib_attr_max_logins_per_lun, 224962306a36Sopenharmony_ci NULL, 225062306a36Sopenharmony_ci}; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_cistatic const struct target_core_fabric_ops sbp_ops = { 225362306a36Sopenharmony_ci .module = THIS_MODULE, 225462306a36Sopenharmony_ci .fabric_name = "sbp", 225562306a36Sopenharmony_ci .tpg_get_wwn = sbp_get_fabric_wwn, 225662306a36Sopenharmony_ci .tpg_get_tag = sbp_get_tag, 225762306a36Sopenharmony_ci .tpg_check_demo_mode = sbp_check_true, 225862306a36Sopenharmony_ci .tpg_check_demo_mode_cache = sbp_check_true, 225962306a36Sopenharmony_ci .release_cmd = sbp_release_cmd, 226062306a36Sopenharmony_ci .write_pending = sbp_write_pending, 226162306a36Sopenharmony_ci .queue_data_in = sbp_queue_data_in, 226262306a36Sopenharmony_ci .queue_status = sbp_queue_status, 226362306a36Sopenharmony_ci .queue_tm_rsp = sbp_queue_tm_rsp, 226462306a36Sopenharmony_ci .aborted_task = sbp_aborted_task, 226562306a36Sopenharmony_ci .check_stop_free = sbp_check_stop_free, 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci .fabric_make_wwn = sbp_make_tport, 226862306a36Sopenharmony_ci .fabric_drop_wwn = sbp_drop_tport, 226962306a36Sopenharmony_ci .fabric_make_tpg = sbp_make_tpg, 227062306a36Sopenharmony_ci .fabric_enable_tpg = sbp_enable_tpg, 227162306a36Sopenharmony_ci .fabric_drop_tpg = sbp_drop_tpg, 227262306a36Sopenharmony_ci .fabric_post_link = sbp_post_link_lun, 227362306a36Sopenharmony_ci .fabric_pre_unlink = sbp_pre_unlink_lun, 227462306a36Sopenharmony_ci .fabric_make_np = NULL, 227562306a36Sopenharmony_ci .fabric_drop_np = NULL, 227662306a36Sopenharmony_ci .fabric_init_nodeacl = sbp_init_nodeacl, 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci .tfc_wwn_attrs = sbp_wwn_attrs, 227962306a36Sopenharmony_ci .tfc_tpg_base_attrs = sbp_tpg_base_attrs, 228062306a36Sopenharmony_ci .tfc_tpg_attrib_attrs = sbp_tpg_attrib_attrs, 228162306a36Sopenharmony_ci}; 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_cistatic int __init sbp_init(void) 228462306a36Sopenharmony_ci{ 228562306a36Sopenharmony_ci return target_register_template(&sbp_ops); 228662306a36Sopenharmony_ci}; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_cistatic void __exit sbp_exit(void) 228962306a36Sopenharmony_ci{ 229062306a36Sopenharmony_ci target_unregister_template(&sbp_ops); 229162306a36Sopenharmony_ci}; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ciMODULE_DESCRIPTION("FireWire SBP fabric driver"); 229462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 229562306a36Sopenharmony_cimodule_init(sbp_init); 229662306a36Sopenharmony_cimodule_exit(sbp_exit); 2297