Re: [PATCH 2/3] target: close target_put_sess_cmd() vs. core_tmr_abort_task() race v5

2013-05-14 Thread Nicholas A. Bellinger
On Tue, 2013-05-14 at 15:04 -0400, Greg Kroah-Hartman wrote:
> On Mon, May 13, 2013 at 04:30:06PM -0400, Joern Engel wrote:
> > It is possible for one thread to to take se_sess->sess_cmd_lock in
> > core_tmr_abort_task() before taking a reference count on
> > se_cmd->cmd_kref, while another thread in target_put_sess_cmd() drops
> > se_cmd->cmd_kref before taking se_sess->sess_cmd_lock.
> > 
> > This introduces kref_put_spinlock_irqsave() and uses it in
> > target_put_sess_cmd() to close the race window.
> > 
> > Signed-off-by: Joern Engel 
> 
> For the kref.h part, please feel free to add:
> 
> Acked-by: Greg Kroah-Hartman 

Applied to target-pending/queue

Since this fixes a real long-standing bug within tcm_qla2xxx code, I'm
adding a CC' to stable as well.

Thanks Joern + Greg-KH!

--nab


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


Re: [PATCH 2/3] target: close target_put_sess_cmd() vs. core_tmr_abort_task() race v5

2013-05-14 Thread Greg Kroah-Hartman
On Mon, May 13, 2013 at 04:30:06PM -0400, Joern Engel wrote:
> It is possible for one thread to to take se_sess->sess_cmd_lock in
> core_tmr_abort_task() before taking a reference count on
> se_cmd->cmd_kref, while another thread in target_put_sess_cmd() drops
> se_cmd->cmd_kref before taking se_sess->sess_cmd_lock.
> 
> This introduces kref_put_spinlock_irqsave() and uses it in
> target_put_sess_cmd() to close the race window.
> 
> Signed-off-by: Joern Engel 

For the kref.h part, please feel free to add:

Acked-by: Greg Kroah-Hartman 
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


Re: [PATCH 2/3] target: close target_put_sess_cmd() vs. core_tmr_abort_task() race v5

2013-05-14 Thread Greg Kroah-Hartman
On Mon, May 13, 2013 at 04:30:06PM -0400, Joern Engel wrote:
 It is possible for one thread to to take se_sess-sess_cmd_lock in
 core_tmr_abort_task() before taking a reference count on
 se_cmd-cmd_kref, while another thread in target_put_sess_cmd() drops
 se_cmd-cmd_kref before taking se_sess-sess_cmd_lock.
 
 This introduces kref_put_spinlock_irqsave() and uses it in
 target_put_sess_cmd() to close the race window.
 
 Signed-off-by: Joern Engel jo...@logfs.org

For the kref.h part, please feel free to add:

Acked-by: Greg Kroah-Hartman gre...@linuxfoundation.org
--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


Re: [PATCH 2/3] target: close target_put_sess_cmd() vs. core_tmr_abort_task() race v5

2013-05-14 Thread Nicholas A. Bellinger
On Tue, 2013-05-14 at 15:04 -0400, Greg Kroah-Hartman wrote:
 On Mon, May 13, 2013 at 04:30:06PM -0400, Joern Engel wrote:
  It is possible for one thread to to take se_sess-sess_cmd_lock in
  core_tmr_abort_task() before taking a reference count on
  se_cmd-cmd_kref, while another thread in target_put_sess_cmd() drops
  se_cmd-cmd_kref before taking se_sess-sess_cmd_lock.
  
  This introduces kref_put_spinlock_irqsave() and uses it in
  target_put_sess_cmd() to close the race window.
  
  Signed-off-by: Joern Engel jo...@logfs.org
 
 For the kref.h part, please feel free to add:
 
 Acked-by: Greg Kroah-Hartman gre...@linuxfoundation.org

Applied to target-pending/queue

Since this fixes a real long-standing bug within tcm_qla2xxx code, I'm
adding a CC' to stable as well.

Thanks Joern + Greg-KH!

--nab


--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


Re: [PATCH 2/3] target: close target_put_sess_cmd() vs. core_tmr_abort_task() race v5

2013-05-13 Thread Nicholas A. Bellinger
On Mon, 2013-05-13 at 16:30 -0400, Joern Engel wrote:
> It is possible for one thread to to take se_sess->sess_cmd_lock in
> core_tmr_abort_task() before taking a reference count on
> se_cmd->cmd_kref, while another thread in target_put_sess_cmd() drops
> se_cmd->cmd_kref before taking se_sess->sess_cmd_lock.
> 
> This introduces kref_put_spinlock_irqsave() and uses it in
> target_put_sess_cmd() to close the race window.
> 
> Signed-off-by: Joern Engel 
> ---

I'm fine with applying this one..

Greg-KH, are you OK with the kref.h changes here..?

--nab

>  drivers/target/target_core_transport.c |   11 +--
>  include/linux/kref.h   |   33 
> 
>  2 files changed, 38 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/target/target_core_transport.c 
> b/drivers/target/target_core_transport.c
> index 3243ea7..0d46276 100644
> --- a/drivers/target/target_core_transport.c
> +++ b/drivers/target/target_core_transport.c
> @@ -2213,21 +2213,19 @@ static void target_release_cmd_kref(struct kref *kref)
>  {
>   struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
>   struct se_session *se_sess = se_cmd->se_sess;
> - unsigned long flags;
>  
> - spin_lock_irqsave(_sess->sess_cmd_lock, flags);
>   if (list_empty(_cmd->se_cmd_list)) {
> - spin_unlock_irqrestore(_sess->sess_cmd_lock, flags);
> + spin_unlock(_sess->sess_cmd_lock);
>   se_cmd->se_tfo->release_cmd(se_cmd);
>   return;
>   }
>   if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
> - spin_unlock_irqrestore(_sess->sess_cmd_lock, flags);
> + spin_unlock(_sess->sess_cmd_lock);
>   complete(_cmd->cmd_wait_comp);
>   return;
>   }
>   list_del(_cmd->se_cmd_list);
> - spin_unlock_irqrestore(_sess->sess_cmd_lock, flags);
> + spin_unlock(_sess->sess_cmd_lock);
>  
>   se_cmd->se_tfo->release_cmd(se_cmd);
>  }
> @@ -2238,7 +2236,8 @@ static void target_release_cmd_kref(struct kref *kref)
>   */
>  int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd)
>  {
> - return kref_put(_cmd->cmd_kref, target_release_cmd_kref);
> + return kref_put_spinlock_irqsave(_cmd->cmd_kref, 
> target_release_cmd_kref,
> + _sess->sess_cmd_lock);
>  }
>  EXPORT_SYMBOL(target_put_sess_cmd);
>  
> diff --git a/include/linux/kref.h b/include/linux/kref.h
> index 4972e6e..7419c02 100644
> --- a/include/linux/kref.h
> +++ b/include/linux/kref.h
> @@ -19,6 +19,7 @@
>  #include 
>  #include 
>  #include 
> +#include 
>  
>  struct kref {
>   atomic_t refcount;
> @@ -95,6 +96,38 @@ static inline int kref_put(struct kref *kref, void 
> (*release)(struct kref *kref)
>   return kref_sub(kref, 1, release);
>  }
>  
> +/**
> + * kref_put_spinlock_irqsave - decrement refcount for object.
> + * @kref: object.
> + * @release: pointer to the function that will clean up the object when the
> + *last reference to the object is released.
> + *This pointer is required, and it is not acceptable to pass kfree
> + *in as this function.
> + * @lock: lock to take in release case
> + *
> + * Behaves identical to kref_put with one exception.  If the reference count
> + * drops to zero, the lock will be taken atomically wrt dropping the 
> reference
> + * count.  The release function has to call spin_unlock() without 
> _irqrestore.
> + */
> +static inline int kref_put_spinlock_irqsave(struct kref *kref,
> + void (*release)(struct kref *kref),
> + spinlock_t *lock)
> +{
> + unsigned long flags;
> +
> + WARN_ON(release == NULL);
> + if (atomic_add_unless(>refcount, -1, 1))
> + return 0;
> + spin_lock_irqsave(lock, flags);
> + if (atomic_dec_and_test(>refcount)) {
> + release(kref);
> + local_irq_restore(flags);
> + return 1;
> + }
> + spin_unlock_irqrestore(lock, flags);
> + return 0;
> +}
> +
>  static inline int kref_put_mutex(struct kref *kref,
>void (*release)(struct kref *kref),
>struct mutex *lock)


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[PATCH 2/3] target: close target_put_sess_cmd() vs. core_tmr_abort_task() race v5

2013-05-13 Thread Joern Engel
It is possible for one thread to to take se_sess->sess_cmd_lock in
core_tmr_abort_task() before taking a reference count on
se_cmd->cmd_kref, while another thread in target_put_sess_cmd() drops
se_cmd->cmd_kref before taking se_sess->sess_cmd_lock.

This introduces kref_put_spinlock_irqsave() and uses it in
target_put_sess_cmd() to close the race window.

Signed-off-by: Joern Engel 
---
 drivers/target/target_core_transport.c |   11 +--
 include/linux/kref.h   |   33 
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/drivers/target/target_core_transport.c 
b/drivers/target/target_core_transport.c
index 3243ea7..0d46276 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -2213,21 +2213,19 @@ static void target_release_cmd_kref(struct kref *kref)
 {
struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
struct se_session *se_sess = se_cmd->se_sess;
-   unsigned long flags;
 
-   spin_lock_irqsave(_sess->sess_cmd_lock, flags);
if (list_empty(_cmd->se_cmd_list)) {
-   spin_unlock_irqrestore(_sess->sess_cmd_lock, flags);
+   spin_unlock(_sess->sess_cmd_lock);
se_cmd->se_tfo->release_cmd(se_cmd);
return;
}
if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
-   spin_unlock_irqrestore(_sess->sess_cmd_lock, flags);
+   spin_unlock(_sess->sess_cmd_lock);
complete(_cmd->cmd_wait_comp);
return;
}
list_del(_cmd->se_cmd_list);
-   spin_unlock_irqrestore(_sess->sess_cmd_lock, flags);
+   spin_unlock(_sess->sess_cmd_lock);
 
se_cmd->se_tfo->release_cmd(se_cmd);
 }
@@ -2238,7 +2236,8 @@ static void target_release_cmd_kref(struct kref *kref)
  */
 int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd)
 {
-   return kref_put(_cmd->cmd_kref, target_release_cmd_kref);
+   return kref_put_spinlock_irqsave(_cmd->cmd_kref, 
target_release_cmd_kref,
+   _sess->sess_cmd_lock);
 }
 EXPORT_SYMBOL(target_put_sess_cmd);
 
diff --git a/include/linux/kref.h b/include/linux/kref.h
index 4972e6e..7419c02 100644
--- a/include/linux/kref.h
+++ b/include/linux/kref.h
@@ -19,6 +19,7 @@
 #include 
 #include 
 #include 
+#include 
 
 struct kref {
atomic_t refcount;
@@ -95,6 +96,38 @@ static inline int kref_put(struct kref *kref, void 
(*release)(struct kref *kref)
return kref_sub(kref, 1, release);
 }
 
+/**
+ * kref_put_spinlock_irqsave - decrement refcount for object.
+ * @kref: object.
+ * @release: pointer to the function that will clean up the object when the
+ *  last reference to the object is released.
+ *  This pointer is required, and it is not acceptable to pass kfree
+ *  in as this function.
+ * @lock: lock to take in release case
+ *
+ * Behaves identical to kref_put with one exception.  If the reference count
+ * drops to zero, the lock will be taken atomically wrt dropping the reference
+ * count.  The release function has to call spin_unlock() without _irqrestore.
+ */
+static inline int kref_put_spinlock_irqsave(struct kref *kref,
+   void (*release)(struct kref *kref),
+   spinlock_t *lock)
+{
+   unsigned long flags;
+
+   WARN_ON(release == NULL);
+   if (atomic_add_unless(>refcount, -1, 1))
+   return 0;
+   spin_lock_irqsave(lock, flags);
+   if (atomic_dec_and_test(>refcount)) {
+   release(kref);
+   local_irq_restore(flags);
+   return 1;
+   }
+   spin_unlock_irqrestore(lock, flags);
+   return 0;
+}
+
 static inline int kref_put_mutex(struct kref *kref,
 void (*release)(struct kref *kref),
 struct mutex *lock)
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[PATCH 2/3] target: close target_put_sess_cmd() vs. core_tmr_abort_task() race v5

2013-05-13 Thread Joern Engel
It is possible for one thread to to take se_sess-sess_cmd_lock in
core_tmr_abort_task() before taking a reference count on
se_cmd-cmd_kref, while another thread in target_put_sess_cmd() drops
se_cmd-cmd_kref before taking se_sess-sess_cmd_lock.

This introduces kref_put_spinlock_irqsave() and uses it in
target_put_sess_cmd() to close the race window.

Signed-off-by: Joern Engel jo...@logfs.org
---
 drivers/target/target_core_transport.c |   11 +--
 include/linux/kref.h   |   33 
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/drivers/target/target_core_transport.c 
b/drivers/target/target_core_transport.c
index 3243ea7..0d46276 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -2213,21 +2213,19 @@ static void target_release_cmd_kref(struct kref *kref)
 {
struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
struct se_session *se_sess = se_cmd-se_sess;
-   unsigned long flags;
 
-   spin_lock_irqsave(se_sess-sess_cmd_lock, flags);
if (list_empty(se_cmd-se_cmd_list)) {
-   spin_unlock_irqrestore(se_sess-sess_cmd_lock, flags);
+   spin_unlock(se_sess-sess_cmd_lock);
se_cmd-se_tfo-release_cmd(se_cmd);
return;
}
if (se_sess-sess_tearing_down  se_cmd-cmd_wait_set) {
-   spin_unlock_irqrestore(se_sess-sess_cmd_lock, flags);
+   spin_unlock(se_sess-sess_cmd_lock);
complete(se_cmd-cmd_wait_comp);
return;
}
list_del(se_cmd-se_cmd_list);
-   spin_unlock_irqrestore(se_sess-sess_cmd_lock, flags);
+   spin_unlock(se_sess-sess_cmd_lock);
 
se_cmd-se_tfo-release_cmd(se_cmd);
 }
@@ -2238,7 +2236,8 @@ static void target_release_cmd_kref(struct kref *kref)
  */
 int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd)
 {
-   return kref_put(se_cmd-cmd_kref, target_release_cmd_kref);
+   return kref_put_spinlock_irqsave(se_cmd-cmd_kref, 
target_release_cmd_kref,
+   se_sess-sess_cmd_lock);
 }
 EXPORT_SYMBOL(target_put_sess_cmd);
 
diff --git a/include/linux/kref.h b/include/linux/kref.h
index 4972e6e..7419c02 100644
--- a/include/linux/kref.h
+++ b/include/linux/kref.h
@@ -19,6 +19,7 @@
 #include linux/atomic.h
 #include linux/kernel.h
 #include linux/mutex.h
+#include linux/spinlock.h
 
 struct kref {
atomic_t refcount;
@@ -95,6 +96,38 @@ static inline int kref_put(struct kref *kref, void 
(*release)(struct kref *kref)
return kref_sub(kref, 1, release);
 }
 
+/**
+ * kref_put_spinlock_irqsave - decrement refcount for object.
+ * @kref: object.
+ * @release: pointer to the function that will clean up the object when the
+ *  last reference to the object is released.
+ *  This pointer is required, and it is not acceptable to pass kfree
+ *  in as this function.
+ * @lock: lock to take in release case
+ *
+ * Behaves identical to kref_put with one exception.  If the reference count
+ * drops to zero, the lock will be taken atomically wrt dropping the reference
+ * count.  The release function has to call spin_unlock() without _irqrestore.
+ */
+static inline int kref_put_spinlock_irqsave(struct kref *kref,
+   void (*release)(struct kref *kref),
+   spinlock_t *lock)
+{
+   unsigned long flags;
+
+   WARN_ON(release == NULL);
+   if (atomic_add_unless(kref-refcount, -1, 1))
+   return 0;
+   spin_lock_irqsave(lock, flags);
+   if (atomic_dec_and_test(kref-refcount)) {
+   release(kref);
+   local_irq_restore(flags);
+   return 1;
+   }
+   spin_unlock_irqrestore(lock, flags);
+   return 0;
+}
+
 static inline int kref_put_mutex(struct kref *kref,
 void (*release)(struct kref *kref),
 struct mutex *lock)
-- 
1.7.10.4

--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


Re: [PATCH 2/3] target: close target_put_sess_cmd() vs. core_tmr_abort_task() race v5

2013-05-13 Thread Nicholas A. Bellinger
On Mon, 2013-05-13 at 16:30 -0400, Joern Engel wrote:
 It is possible for one thread to to take se_sess-sess_cmd_lock in
 core_tmr_abort_task() before taking a reference count on
 se_cmd-cmd_kref, while another thread in target_put_sess_cmd() drops
 se_cmd-cmd_kref before taking se_sess-sess_cmd_lock.
 
 This introduces kref_put_spinlock_irqsave() and uses it in
 target_put_sess_cmd() to close the race window.
 
 Signed-off-by: Joern Engel jo...@logfs.org
 ---

I'm fine with applying this one..

Greg-KH, are you OK with the kref.h changes here..?

--nab

  drivers/target/target_core_transport.c |   11 +--
  include/linux/kref.h   |   33 
 
  2 files changed, 38 insertions(+), 6 deletions(-)
 
 diff --git a/drivers/target/target_core_transport.c 
 b/drivers/target/target_core_transport.c
 index 3243ea7..0d46276 100644
 --- a/drivers/target/target_core_transport.c
 +++ b/drivers/target/target_core_transport.c
 @@ -2213,21 +2213,19 @@ static void target_release_cmd_kref(struct kref *kref)
  {
   struct se_cmd *se_cmd = container_of(kref, struct se_cmd, cmd_kref);
   struct se_session *se_sess = se_cmd-se_sess;
 - unsigned long flags;
  
 - spin_lock_irqsave(se_sess-sess_cmd_lock, flags);
   if (list_empty(se_cmd-se_cmd_list)) {
 - spin_unlock_irqrestore(se_sess-sess_cmd_lock, flags);
 + spin_unlock(se_sess-sess_cmd_lock);
   se_cmd-se_tfo-release_cmd(se_cmd);
   return;
   }
   if (se_sess-sess_tearing_down  se_cmd-cmd_wait_set) {
 - spin_unlock_irqrestore(se_sess-sess_cmd_lock, flags);
 + spin_unlock(se_sess-sess_cmd_lock);
   complete(se_cmd-cmd_wait_comp);
   return;
   }
   list_del(se_cmd-se_cmd_list);
 - spin_unlock_irqrestore(se_sess-sess_cmd_lock, flags);
 + spin_unlock(se_sess-sess_cmd_lock);
  
   se_cmd-se_tfo-release_cmd(se_cmd);
  }
 @@ -2238,7 +2236,8 @@ static void target_release_cmd_kref(struct kref *kref)
   */
  int target_put_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd)
  {
 - return kref_put(se_cmd-cmd_kref, target_release_cmd_kref);
 + return kref_put_spinlock_irqsave(se_cmd-cmd_kref, 
 target_release_cmd_kref,
 + se_sess-sess_cmd_lock);
  }
  EXPORT_SYMBOL(target_put_sess_cmd);
  
 diff --git a/include/linux/kref.h b/include/linux/kref.h
 index 4972e6e..7419c02 100644
 --- a/include/linux/kref.h
 +++ b/include/linux/kref.h
 @@ -19,6 +19,7 @@
  #include linux/atomic.h
  #include linux/kernel.h
  #include linux/mutex.h
 +#include linux/spinlock.h
  
  struct kref {
   atomic_t refcount;
 @@ -95,6 +96,38 @@ static inline int kref_put(struct kref *kref, void 
 (*release)(struct kref *kref)
   return kref_sub(kref, 1, release);
  }
  
 +/**
 + * kref_put_spinlock_irqsave - decrement refcount for object.
 + * @kref: object.
 + * @release: pointer to the function that will clean up the object when the
 + *last reference to the object is released.
 + *This pointer is required, and it is not acceptable to pass kfree
 + *in as this function.
 + * @lock: lock to take in release case
 + *
 + * Behaves identical to kref_put with one exception.  If the reference count
 + * drops to zero, the lock will be taken atomically wrt dropping the 
 reference
 + * count.  The release function has to call spin_unlock() without 
 _irqrestore.
 + */
 +static inline int kref_put_spinlock_irqsave(struct kref *kref,
 + void (*release)(struct kref *kref),
 + spinlock_t *lock)
 +{
 + unsigned long flags;
 +
 + WARN_ON(release == NULL);
 + if (atomic_add_unless(kref-refcount, -1, 1))
 + return 0;
 + spin_lock_irqsave(lock, flags);
 + if (atomic_dec_and_test(kref-refcount)) {
 + release(kref);
 + local_irq_restore(flags);
 + return 1;
 + }
 + spin_unlock_irqrestore(lock, flags);
 + return 0;
 +}
 +
  static inline int kref_put_mutex(struct kref *kref,
void (*release)(struct kref *kref),
struct mutex *lock)


--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/