Skip to content

Commit

Permalink
target/iscsi: Fix network portal creation race
Browse files Browse the repository at this point in the history
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 <[email protected]>
Cc: <[email protected]> #3.1+
Signed-off-by: Nicholas Bellinger <[email protected]>
  • Loading branch information
Andy Grover authored and Nicholas Bellinger committed Jan 30, 2014
1 parent 76736db commit ee291e6
Showing 1 changed file with 21 additions and 13 deletions.
34 changes: 21 additions & 13 deletions drivers/target/iscsi/iscsi_target.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -307,18 +307,20 @@ 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)
{
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;
}

Expand All @@ -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;
}
Expand All @@ -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);
}

Expand All @@ -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);
}

Expand All @@ -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);
}
/*
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit ee291e6

Please sign in to comment.