target/iscsi: Fix network portal creation race
When creating network portals rapidly, such as when restoring a configuration, LIO's code to reuse existing portals can return a false negative if the thread hasn't run yet and set np_thread_state to ISCSI_NP_THREAD_ACTIVE. This causes an error in the network stack when attempting to bind to the same address/port. This patch sets NP_THREAD_ACTIVE before the np is placed on g_np_list, so even if the thread hasn't run yet, iscsit_get_np will return the existing np. Also, convert np_lock -> np_mutex + hold across adding new net portal to g_np_list to prevent a race where two threads may attempt to create the same network portal, resulting in one of them failing. (nab: Add missing mutex_unlocks in iscsit_add_np failure paths) (DanC: Fix incorrect spin_unlock -> spin_unlock_bh) Signed-off-by: Andy Grover <agrover@redhat.com> Cc: <stable@vger.kernel.org> #3.1+ Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
This commit is contained in:
parent
76736db3e2
commit
ee291e6329
|
@ -52,7 +52,7 @@
|
|||
static LIST_HEAD(g_tiqn_list);
|
||||
static LIST_HEAD(g_np_list);
|
||||
static DEFINE_SPINLOCK(tiqn_lock);
|
||||
static DEFINE_SPINLOCK(np_lock);
|
||||
static DEFINE_MUTEX(np_lock);
|
||||
|
||||
static struct idr tiqn_idr;
|
||||
struct idr sess_idr;
|
||||
|
@ -307,6 +307,9 @@ bool iscsit_check_np_match(
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with mutex np_lock held
|
||||
*/
|
||||
static struct iscsi_np *iscsit_get_np(
|
||||
struct __kernel_sockaddr_storage *sockaddr,
|
||||
int network_transport)
|
||||
|
@ -314,11 +317,10 @@ static struct iscsi_np *iscsit_get_np(
|
|||
struct iscsi_np *np;
|
||||
bool match;
|
||||
|
||||
spin_lock_bh(&np_lock);
|
||||
list_for_each_entry(np, &g_np_list, np_list) {
|
||||
spin_lock(&np->np_thread_lock);
|
||||
spin_lock_bh(&np->np_thread_lock);
|
||||
if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
|
||||
spin_unlock(&np->np_thread_lock);
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -330,13 +332,11 @@ static struct iscsi_np *iscsit_get_np(
|
|||
* while iscsi_tpg_add_network_portal() is called.
|
||||
*/
|
||||
np->np_exports++;
|
||||
spin_unlock(&np->np_thread_lock);
|
||||
spin_unlock_bh(&np_lock);
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
return np;
|
||||
}
|
||||
spin_unlock(&np->np_thread_lock);
|
||||
spin_unlock_bh(&np->np_thread_lock);
|
||||
}
|
||||
spin_unlock_bh(&np_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -350,16 +350,22 @@ struct iscsi_np *iscsit_add_np(
|
|||
struct sockaddr_in6 *sock_in6;
|
||||
struct iscsi_np *np;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&np_lock);
|
||||
|
||||
/*
|
||||
* Locate the existing struct iscsi_np if already active..
|
||||
*/
|
||||
np = iscsit_get_np(sockaddr, network_transport);
|
||||
if (np)
|
||||
if (np) {
|
||||
mutex_unlock(&np_lock);
|
||||
return np;
|
||||
}
|
||||
|
||||
np = kzalloc(sizeof(struct iscsi_np), GFP_KERNEL);
|
||||
if (!np) {
|
||||
pr_err("Unable to allocate memory for struct iscsi_np\n");
|
||||
mutex_unlock(&np_lock);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
|
@ -382,6 +388,7 @@ struct iscsi_np *iscsit_add_np(
|
|||
ret = iscsi_target_setup_login_socket(np, sockaddr);
|
||||
if (ret != 0) {
|
||||
kfree(np);
|
||||
mutex_unlock(&np_lock);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
|
@ -390,6 +397,7 @@ struct iscsi_np *iscsit_add_np(
|
|||
pr_err("Unable to create kthread: iscsi_np\n");
|
||||
ret = PTR_ERR(np->np_thread);
|
||||
kfree(np);
|
||||
mutex_unlock(&np_lock);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
/*
|
||||
|
@ -400,10 +408,10 @@ struct iscsi_np *iscsit_add_np(
|
|||
* point because iscsi_np has not been added to g_np_list yet.
|
||||
*/
|
||||
np->np_exports = 1;
|
||||
np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
|
||||
|
||||
spin_lock_bh(&np_lock);
|
||||
list_add_tail(&np->np_list, &g_np_list);
|
||||
spin_unlock_bh(&np_lock);
|
||||
mutex_unlock(&np_lock);
|
||||
|
||||
pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n",
|
||||
np->np_ip, np->np_port, np->np_transport->name);
|
||||
|
@ -469,9 +477,9 @@ int iscsit_del_np(struct iscsi_np *np)
|
|||
|
||||
np->np_transport->iscsit_free_np(np);
|
||||
|
||||
spin_lock_bh(&np_lock);
|
||||
mutex_lock(&np_lock);
|
||||
list_del(&np->np_list);
|
||||
spin_unlock_bh(&np_lock);
|
||||
mutex_unlock(&np_lock);
|
||||
|
||||
pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n",
|
||||
np->np_ip, np->np_port, np->np_transport->name);
|
||||
|
|
Loading…
Reference in New Issue