sub_api_initialized = 1;
 }
 
+static void target_release_sess_cmd_refcnt(struct percpu_ref *ref)
+{
+       struct se_session *sess = container_of(ref, typeof(*sess), cmd_count);
+
+       wake_up(&sess->cmd_list_wq);
+}
+
 /**
  * transport_init_session - initialize a session object
  * @se_sess: Session object pointer.
  *
  * The caller must have zero-initialized @se_sess before calling this function.
  */
-void transport_init_session(struct se_session *se_sess)
+int transport_init_session(struct se_session *se_sess)
 {
        INIT_LIST_HEAD(&se_sess->sess_list);
        INIT_LIST_HEAD(&se_sess->sess_acl_list);
        INIT_LIST_HEAD(&se_sess->sess_cmd_list);
        spin_lock_init(&se_sess->sess_cmd_lock);
        init_waitqueue_head(&se_sess->cmd_list_wq);
+       return percpu_ref_init(&se_sess->cmd_count,
+                              target_release_sess_cmd_refcnt, 0, GFP_KERNEL);
 }
 EXPORT_SYMBOL(transport_init_session);
 
 struct se_session *transport_alloc_session(enum target_prot_op sup_prot_ops)
 {
        struct se_session *se_sess;
+       int ret;
 
        se_sess = kmem_cache_zalloc(se_sess_cache, GFP_KERNEL);
        if (!se_sess) {
                                " se_sess_cache\n");
                return ERR_PTR(-ENOMEM);
        }
-       transport_init_session(se_sess);
+       ret = transport_init_session(se_sess);
+       if (ret < 0) {
+               kfree(se_sess);
+               return ERR_PTR(ret);
+       }
        se_sess->sup_prot_ops = sup_prot_ops;
 
        return se_sess;
                sbitmap_queue_free(&se_sess->sess_tag_pool);
                kvfree(se_sess->sess_cmd_map);
        }
+       percpu_ref_exit(&se_sess->cmd_count);
        kmem_cache_free(se_sess_cache, se_sess);
 }
 EXPORT_SYMBOL(transport_free_session);
        }
        se_cmd->transport_state |= CMD_T_PRE_EXECUTE;
        list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
+       percpu_ref_get(&se_sess->cmd_count);
 out:
        spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
 
        if (se_sess) {
                spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
                list_del_init(&se_cmd->se_cmd_list);
-               if (se_sess->sess_tearing_down && list_empty(&se_sess->sess_cmd_list))
-                       wake_up(&se_sess->cmd_list_wq);
                spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
        }
 
        se_cmd->se_tfo->release_cmd(se_cmd);
        if (compl)
                complete(compl);
+
+       percpu_ref_put(&se_sess->cmd_count);
 }
 
 /**
        spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
        se_sess->sess_tearing_down = 1;
        spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+
+       percpu_ref_kill(&se_sess->cmd_count);
 }
 EXPORT_SYMBOL(target_sess_cmd_list_set_waiting);
 
 
        WARN_ON_ONCE(!se_sess->sess_tearing_down);
 
-       spin_lock_irq(&se_sess->sess_cmd_lock);
        do {
-               ret = wait_event_lock_irq_timeout(
-                               se_sess->cmd_list_wq,
-                               list_empty(&se_sess->sess_cmd_list),
-                               se_sess->sess_cmd_lock, 180 * HZ);
+               ret = wait_event_timeout(se_sess->cmd_list_wq,
+                               percpu_ref_is_zero(&se_sess->cmd_count),
+                               180 * HZ);
                list_for_each_entry(cmd, &se_sess->sess_cmd_list, se_cmd_list)
                        target_show_cmd("session shutdown: still waiting for ",
                                        cmd);
        } while (ret <= 0);
-       spin_unlock_irq(&se_sess->sess_cmd_lock);
 }
 EXPORT_SYMBOL(target_wait_for_sess_cmds);