diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2016-01-08 01:09:27 -0500 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2016-01-20 04:34:15 -0500 |
commit | 21aaa23b0ebbd19334fa461370c03cbb076b3295 (patch) | |
tree | ee480a095123426c81a9aac8fed0fba8fe27fc74 /drivers/target | |
parent | d36ad77f702356afb1009d2987b0ab55da4c7d57 (diff) |
target: Obtain se_node_acl->acl_kref during get_initiator_node_acl
This patch addresses a long standing race where obtaining
se_node_acl->acl_kref in __transport_register_session()
happens a bit too late, and leaves open the potential
for core_tpg_del_initiator_node_acl() to hit a NULL
pointer dereference.
Instead, take ->acl_kref in core_tpg_get_initiator_node_acl()
while se_portal_group->acl_node_mutex is held, and move the
final target_put_nacl() from transport_deregister_session()
into transport_free_session() so that fabric driver login
failure handling using the modern method to still work
as expected.
Also, update core_tpg_get_initiator_node_acl() to take
an extra reference for dynamically generated acls for
demo-mode, before returning to fabric caller. Also
update iscsi-target sendtargets special case handling
to use target_tpg_has_node_acl() when checking if
demo_mode_discovery == true during discovery lookup.
Note the existing wait_for_completion(&acl->acl_free_comp)
in core_tpg_del_initiator_node_acl() does not change.
Cc: Sagi Grimberg <sagig@mellanox.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Andy Grover <agrover@redhat.com>
Cc: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/iscsi/iscsi_target.c | 2 | ||||
-rw-r--r-- | drivers/target/target_core_tpg.c | 42 | ||||
-rw-r--r-- | drivers/target/target_core_transport.c | 19 |
3 files changed, 55 insertions, 8 deletions
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index a81c0e5ca293..762b2d6ea1cc 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c | |||
@@ -3435,7 +3435,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, | |||
3435 | 3435 | ||
3436 | if ((tpg->tpg_attrib.generate_node_acls == 0) && | 3436 | if ((tpg->tpg_attrib.generate_node_acls == 0) && |
3437 | (tpg->tpg_attrib.demo_mode_discovery == 0) && | 3437 | (tpg->tpg_attrib.demo_mode_discovery == 0) && |
3438 | (!core_tpg_get_initiator_node_acl(&tpg->tpg_se_tpg, | 3438 | (!target_tpg_has_node_acl(&tpg->tpg_se_tpg, |
3439 | cmd->conn->sess->sess_ops->InitiatorName))) { | 3439 | cmd->conn->sess->sess_ops->InitiatorName))) { |
3440 | continue; | 3440 | continue; |
3441 | } | 3441 | } |
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 67be44da29ff..3608b1b5ecf7 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c | |||
@@ -75,9 +75,21 @@ struct se_node_acl *core_tpg_get_initiator_node_acl( | |||
75 | unsigned char *initiatorname) | 75 | unsigned char *initiatorname) |
76 | { | 76 | { |
77 | struct se_node_acl *acl; | 77 | struct se_node_acl *acl; |
78 | 78 | /* | |
79 | * Obtain se_node_acl->acl_kref using fabric driver provided | ||
80 | * initiatorname[] during node acl endpoint lookup driven by | ||
81 | * new se_session login. | ||
82 | * | ||
83 | * The reference is held until se_session shutdown -> release | ||
84 | * occurs via fabric driver invoked transport_deregister_session() | ||
85 | * or transport_free_session() code. | ||
86 | */ | ||
79 | mutex_lock(&tpg->acl_node_mutex); | 87 | mutex_lock(&tpg->acl_node_mutex); |
80 | acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); | 88 | acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); |
89 | if (acl) { | ||
90 | if (!kref_get_unless_zero(&acl->acl_kref)) | ||
91 | acl = NULL; | ||
92 | } | ||
81 | mutex_unlock(&tpg->acl_node_mutex); | 93 | mutex_unlock(&tpg->acl_node_mutex); |
82 | 94 | ||
83 | return acl; | 95 | return acl; |
@@ -224,6 +236,25 @@ static void target_add_node_acl(struct se_node_acl *acl) | |||
224 | acl->initiatorname); | 236 | acl->initiatorname); |
225 | } | 237 | } |
226 | 238 | ||
239 | bool target_tpg_has_node_acl(struct se_portal_group *tpg, | ||
240 | const char *initiatorname) | ||
241 | { | ||
242 | struct se_node_acl *acl; | ||
243 | bool found = false; | ||
244 | |||
245 | mutex_lock(&tpg->acl_node_mutex); | ||
246 | list_for_each_entry(acl, &tpg->acl_node_list, acl_list) { | ||
247 | if (!strcmp(acl->initiatorname, initiatorname)) { | ||
248 | found = true; | ||
249 | break; | ||
250 | } | ||
251 | } | ||
252 | mutex_unlock(&tpg->acl_node_mutex); | ||
253 | |||
254 | return found; | ||
255 | } | ||
256 | EXPORT_SYMBOL(target_tpg_has_node_acl); | ||
257 | |||
227 | struct se_node_acl *core_tpg_check_initiator_node_acl( | 258 | struct se_node_acl *core_tpg_check_initiator_node_acl( |
228 | struct se_portal_group *tpg, | 259 | struct se_portal_group *tpg, |
229 | unsigned char *initiatorname) | 260 | unsigned char *initiatorname) |
@@ -240,6 +271,15 @@ struct se_node_acl *core_tpg_check_initiator_node_acl( | |||
240 | acl = target_alloc_node_acl(tpg, initiatorname); | 271 | acl = target_alloc_node_acl(tpg, initiatorname); |
241 | if (!acl) | 272 | if (!acl) |
242 | return NULL; | 273 | return NULL; |
274 | /* | ||
275 | * When allocating a dynamically generated node_acl, go ahead | ||
276 | * and take the extra kref now before returning to the fabric | ||
277 | * driver caller. | ||
278 | * | ||
279 | * Note this reference will be released at session shutdown | ||
280 | * time within transport_free_session() code. | ||
281 | */ | ||
282 | kref_get(&acl->acl_kref); | ||
243 | acl->dynamic_node_acl = 1; | 283 | acl->dynamic_node_acl = 1; |
244 | 284 | ||
245 | /* | 285 | /* |
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 7b05ebf8053c..a7c1bb54cf72 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
@@ -341,7 +341,6 @@ void __transport_register_session( | |||
341 | &buf[0], PR_REG_ISID_LEN); | 341 | &buf[0], PR_REG_ISID_LEN); |
342 | se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]); | 342 | se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]); |
343 | } | 343 | } |
344 | kref_get(&se_nacl->acl_kref); | ||
345 | 344 | ||
346 | spin_lock_irq(&se_nacl->nacl_sess_lock); | 345 | spin_lock_irq(&se_nacl->nacl_sess_lock); |
347 | /* | 346 | /* |
@@ -432,6 +431,7 @@ void target_put_nacl(struct se_node_acl *nacl) | |||
432 | { | 431 | { |
433 | kref_put(&nacl->acl_kref, target_complete_nacl); | 432 | kref_put(&nacl->acl_kref, target_complete_nacl); |
434 | } | 433 | } |
434 | EXPORT_SYMBOL(target_put_nacl); | ||
435 | 435 | ||
436 | void transport_deregister_session_configfs(struct se_session *se_sess) | 436 | void transport_deregister_session_configfs(struct se_session *se_sess) |
437 | { | 437 | { |
@@ -464,6 +464,15 @@ EXPORT_SYMBOL(transport_deregister_session_configfs); | |||
464 | 464 | ||
465 | void transport_free_session(struct se_session *se_sess) | 465 | void transport_free_session(struct se_session *se_sess) |
466 | { | 466 | { |
467 | struct se_node_acl *se_nacl = se_sess->se_node_acl; | ||
468 | /* | ||
469 | * Drop the se_node_acl->nacl_kref obtained from within | ||
470 | * core_tpg_get_initiator_node_acl(). | ||
471 | */ | ||
472 | if (se_nacl) { | ||
473 | se_sess->se_node_acl = NULL; | ||
474 | target_put_nacl(se_nacl); | ||
475 | } | ||
467 | if (se_sess->sess_cmd_map) { | 476 | if (se_sess->sess_cmd_map) { |
468 | percpu_ida_destroy(&se_sess->sess_tag_pool); | 477 | percpu_ida_destroy(&se_sess->sess_tag_pool); |
469 | kvfree(se_sess->sess_cmd_map); | 478 | kvfree(se_sess->sess_cmd_map); |
@@ -478,7 +487,7 @@ void transport_deregister_session(struct se_session *se_sess) | |||
478 | const struct target_core_fabric_ops *se_tfo; | 487 | const struct target_core_fabric_ops *se_tfo; |
479 | struct se_node_acl *se_nacl; | 488 | struct se_node_acl *se_nacl; |
480 | unsigned long flags; | 489 | unsigned long flags; |
481 | bool comp_nacl = true, drop_nacl = false; | 490 | bool drop_nacl = false; |
482 | 491 | ||
483 | if (!se_tpg) { | 492 | if (!se_tpg) { |
484 | transport_free_session(se_sess); | 493 | transport_free_session(se_sess); |
@@ -510,18 +519,16 @@ void transport_deregister_session(struct se_session *se_sess) | |||
510 | if (drop_nacl) { | 519 | if (drop_nacl) { |
511 | core_tpg_wait_for_nacl_pr_ref(se_nacl); | 520 | core_tpg_wait_for_nacl_pr_ref(se_nacl); |
512 | core_free_device_list_for_node(se_nacl, se_tpg); | 521 | core_free_device_list_for_node(se_nacl, se_tpg); |
522 | se_sess->se_node_acl = NULL; | ||
513 | kfree(se_nacl); | 523 | kfree(se_nacl); |
514 | comp_nacl = false; | ||
515 | } | 524 | } |
516 | pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n", | 525 | pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n", |
517 | se_tpg->se_tpg_tfo->get_fabric_name()); | 526 | se_tpg->se_tpg_tfo->get_fabric_name()); |
518 | /* | 527 | /* |
519 | * If last kref is dropping now for an explicit NodeACL, awake sleeping | 528 | * If last kref is dropping now for an explicit NodeACL, awake sleeping |
520 | * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group | 529 | * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group |
521 | * removal context. | 530 | * removal context from within transport_free_session() code. |
522 | */ | 531 | */ |
523 | if (se_nacl && comp_nacl) | ||
524 | target_put_nacl(se_nacl); | ||
525 | 532 | ||
526 | transport_free_session(se_sess); | 533 | transport_free_session(se_sess); |
527 | } | 534 | } |