// SPDX-License-Identifier: GPL-2.0-or-later
/*******************************************************************************
* This file contains iSCSI Target Portal Group related functions.
*
* (c) Copyright 2007-2013 Datera, Inc.
*
* Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
*
******************************************************************************/
#include <linux/slab.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
#include <target/iscsi/iscsi_target_core.h>
#include "iscsi_target_erl0.h"
#include "iscsi_target_login.h"
#include "iscsi_target_nodeattrib.h"
#include "iscsi_target_tpg.h"
#include "iscsi_target_util.h"
#include "iscsi_target.h"
#include "iscsi_target_parameters.h"
#include <target/iscsi/iscsi_transport.h>
struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *tiqn, u16 tpgt)
{
struct iscsi_portal_group *tpg;
tpg = kzalloc(sizeof(struct iscsi_portal_group), GFP_KERNEL);
if (!tpg) {
pr_err("Unable to allocate struct iscsi_portal_group\n");
return NULL;
}
tpg->tpgt = tpgt;
tpg->tpg_state = TPG_STATE_FREE;
tpg->tpg_tiqn = tiqn;
INIT_LIST_HEAD(&tpg->tpg_gnp_list);
INIT_LIST_HEAD(&tpg->tpg_list);
mutex_init(&tpg->tpg_access_lock);
sema_init(&tpg->np_login_sem, 1);
spin_lock_init(&tpg->tpg_state_lock);
spin_lock_init(&tpg->tpg_np_lock);
return tpg;
}
static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *);
int iscsit_load_discovery_tpg(void)
{
struct iscsi_param *param;
struct iscsi_portal_group *tpg;
int ret;
tpg = iscsit_alloc_portal_group(NULL, 1);
if (!tpg) {
pr_err("Unable to allocate struct iscsi_portal_group\n");
return -1;
}
/*
* Save iscsi_ops pointer for special case discovery TPG that
* doesn't exist as se_wwn->wwn_group within configfs.
*/
tpg->tpg_se_tpg.se_tpg_tfo = &iscsi_ops;
ret = core_tpg_register(NULL, &tpg->tpg_se_tpg, -1);
if (ret < 0) {
kfree(tpg);
return -1;
}
tpg->sid = 1; /* First Assigned LIO Session ID */
iscsit_set_default_tpg_attribs(tpg);
if (iscsi_create_default_params(&tpg->param_list) < 0)
goto out;
/*
* By default we disable authentication for discovery sessions,
* this can be changed with:
*
* /sys/kernel/config/target/iscsi/discovery_auth/enforce_discovery_auth
*/
param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list);
if (!param)
goto free_pl_out;
if (iscsi_update_param_value(param, "CHAP,None") < 0)
goto free_pl_out;
tpg->tpg_attrib.authentication = 0;
spin_lock(&tpg->tpg_state_lock);
tpg->tpg_state = TPG_STATE_ACTIVE;
spin_unlock(&tpg->tpg_state_lock);
iscsit_global->discovery_tpg = tpg;
pr_debug("CORE[0] - Allocated Discovery TPG\n");
return 0;
free_pl_out:
iscsi_release_param_list(tpg->param_list);
out:
if (tpg->sid == 1)
core_tpg_deregister(&tpg->tpg_se_tpg);
kfree(tpg);
return -1;
}
void iscsit_release_discovery_tpg(void)
{
struct iscsi_portal_group *tpg = iscsit_global->discovery_tpg;
if (!tpg)
return;
iscsi_release_param_list(tpg->param_list);
core_tpg_deregister(&tpg->tpg_se_tpg);
kfree(tpg);
iscsit_global->discovery_tpg = NULL;
}
struct iscsi_portal_group *iscsit_get_tpg_from_np(
struct iscsi_tiqn *tiqn,
struct iscsi_np *np,
struct iscsi_tpg_np **tpg_np_out)
{
struct iscsi_portal_group *tpg = NULL;
struct iscsi_tpg_np *tpg_np;
spin_lock(&tiqn->tiqn_tpg_lock);
list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) {
spin_lock(&tpg->tpg_state_lock);
if (tpg->tpg_state != TPG_STATE_ACTIVE) {
spin_unlock(&tpg->tpg_state_lock);
continue;
}
spin_unlock(&tpg->tpg_state_lock);
spin_lock(&tpg->tpg_np_lock);
list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) {
if (tpg_np->tpg_np == np) {
*tpg_np_out = tpg_np;
kref_get(&tpg_np->tpg_np_kref);
spin_unlock(&tpg->tpg_np_lock);
spin_unlock(&tiqn->tiqn_tpg_lock);
return tpg;
}
}
spin_unlock(&tpg->tpg_np_lock);
}
spin_unlock(&tiqn->tiqn_tpg_lock);
return NULL;
}
int iscsit_get_tpg(
struct iscsi_portal_group *tpg)
{
return mutex_lock_interruptible(&tpg->tpg_access_lock);
}
void iscsit_put_tpg(struct iscsi_portal_group *tpg)
{
mutex_unlock(&tpg->tpg_access_lock);
}
static void iscsit_clear_tpg_np_login_thread(
struct iscsi_tpg_np *tpg_np,
struct iscsi_portal_group *tpg,
bool shutdown)
{
if (!tpg_np->tpg_np) {
pr_err("struct iscsi_tpg_np->tpg_np is NULL!\n");
return;
}
if (shutdown)
tpg_np