[stgt] [PATCH] Put locking around each entry in cmd_hash_list
Andy Grover
agrover at redhat.com
Thu Sep 22 00:38:45 CEST 2011
Both the receiving thread and the bs worker threads access the hash of
lists at it_nexus->cmd_hash_list. It may be the case that this is causing
list corruption. Lock each individual list in the hash with a mutex.
Using a recursive mutex, since abort_task_set both needs to hold the mutex,
and __cmd_done (called from abort_cmd) also is calling cmd_hlist_remove,
which also takes the mutex.
Signed-off-by: Andy Grover <agrover at redhat.com>
---
Hi Kiefer, does this make a difference? Thanks -- Andy
usr/scsi_cmnd.h | 3 +++
usr/target.c | 46 ++++++++++++++++++++++++++++++++++++++--------
usr/target.h | 7 ++++++-
3 files changed, 47 insertions(+), 9 deletions(-)
diff --git a/usr/scsi_cmnd.h b/usr/scsi_cmnd.h
index c764c4e..cd9272e 100644
--- a/usr/scsi_cmnd.h
+++ b/usr/scsi_cmnd.h
@@ -49,6 +49,9 @@ struct scsi_cmd {
struct it_nexus *it_nexus;
struct it_nexus_lu_info *itn_lu_info;
+
+ /* backptr to what cmd_hash_entry we're on so we can lock properly */
+ struct cmd_hash_entry *cur_hash_entry;
};
#define scsi_cmnd_accessor(field, type) \
diff --git a/usr/target.c b/usr/target.c
index 42443d0..ac26a99 100644
--- a/usr/target.c
+++ b/usr/target.c
@@ -29,6 +29,7 @@
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
+#include <pthread.h>
#include "list.h"
#include "util.h"
@@ -269,6 +270,7 @@ int it_nexus_create(int tid, uint64_t itn_id, int host_no, char *info)
struct scsi_lu *lu;
struct it_nexus_lu_info *itn_lu;
struct timeval tv;
+ pthread_mutexattr_t mutex_attr;
dprintf("%d %" PRIu64 " %d\n", tid, itn_id, host_no);
/* for reserve/release code */
@@ -308,8 +310,15 @@ int it_nexus_create(int tid, uint64_t itn_id, int host_no, char *info)
&itn->it_nexus_lu_info_list);
}
- for (i = 0; i < ARRAY_SIZE(itn->cmd_hash_list); i++)
- INIT_LIST_HEAD(&itn->cmd_hash_list[i]);
+ pthread_mutexattr_init(&mutex_attr);
+ pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
+
+ for (i = 0; i < ARRAY_SIZE(itn->cmd_hash_list); i++) {
+ INIT_LIST_HEAD(&itn->cmd_hash_list[i].head);
+ pthread_mutex_init(&itn->cmd_hash_list[i].lock, &mutex_attr);
+ }
+
+ pthread_mutexattr_destroy(&mutex_attr);
list_add_tail(&itn->nexus_siblings, &target->it_nexus_list);
@@ -324,6 +333,7 @@ int it_nexus_destroy(int tid, uint64_t itn_id)
int i;
struct it_nexus *itn;
struct scsi_lu *lu;
+ struct cmd_hash_entry *entry;
dprintf("%d %" PRIu64 "\n", tid, itn_id);
@@ -331,9 +341,18 @@ int it_nexus_destroy(int tid, uint64_t itn_id)
if (!itn)
return -ENOENT;
- for (i = 0; i < ARRAY_SIZE(itn->cmd_hash_list); i++)
- if (!list_empty(&itn->cmd_hash_list[i]))
+ for (i = 0; i < ARRAY_SIZE(itn->cmd_hash_list); i++) {
+ entry = &itn->cmd_hash_list[i];
+ pthread_mutex_lock(&entry->lock);
+ if (!list_empty(&entry->head)) {
+ pthread_mutex_unlock(&entry->lock);
return -EBUSY;
+ }
+ pthread_mutex_unlock(&entry->lock);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(itn->cmd_hash_list); i++)
+ pthread_mutex_destroy(&itn->cmd_hash_list[i].lock);
list_for_each_entry(lu, &itn->nexus_target->device_list, device_siblings)
device_release(tid, itn_id, lu->lun, 0);
@@ -357,13 +376,22 @@ static struct scsi_lu *device_lookup(struct target *target, uint64_t lun)
static void cmd_hlist_insert(struct it_nexus *itn, struct scsi_cmd *cmd)
{
- struct list_head *list = &itn->cmd_hash_list[hashfn(cmd->tag)];
- list_add(&cmd->c_hlist, list);
+ struct cmd_hash_entry *entry = &itn->cmd_hash_list[hashfn(cmd->tag)];
+
+ pthread_mutex_lock(&entry->lock);
+ cmd->cur_hash_entry = entry;
+ list_add(&cmd->c_hlist, &entry->head);
+ pthread_mutex_unlock(&entry->lock);
}
static void cmd_hlist_remove(struct scsi_cmd *cmd)
{
+ struct cmd_hash_entry *entry = cmd->cur_hash_entry;
+
+ pthread_mutex_lock(&entry->lock);
+ cmd->cur_hash_entry = NULL;
list_del(&cmd->c_hlist);
+ pthread_mutex_unlock(&entry->lock);
}
static void tgt_cmd_queue_init(struct tgt_cmd_queue *q)
@@ -1116,8 +1144,9 @@ static int abort_task_set(struct mgmt_req *mreq, struct target* target,
list_for_each_entry(itn, &target->it_nexus_list, nexus_siblings) {
for (i = 0; i < ARRAY_SIZE(itn->cmd_hash_list); i++) {
- struct list_head *list = &itn->cmd_hash_list[i];
- list_for_each_entry_safe(cmd, tmp, list, c_hlist) {
+ struct cmd_hash_entry *entry = &itn->cmd_hash_list[i];
+ pthread_mutex_lock(&entry->lock);
+ list_for_each_entry_safe(cmd, tmp, &entry->head, c_hlist) {
if ((all && itn->itn_id == itn_id) ||
(cmd->tag == tag && itn->itn_id == itn_id) ||
(lun && !memcmp(cmd->lun, lun, sizeof(cmd->lun)))) {
@@ -1127,6 +1156,7 @@ static int abort_task_set(struct mgmt_req *mreq, struct target* target,
count++;
}
}
+ pthread_mutex_unlock(&entry->lock);
}
}
return count;
diff --git a/usr/target.h b/usr/target.h
index f1e51f3..23e149d 100644
--- a/usr/target.h
+++ b/usr/target.h
@@ -48,11 +48,16 @@ struct target {
struct tgt_account account;
};
+struct cmd_hash_entry {
+ struct list_head head;
+ pthread_mutex_t lock;
+};
+
struct it_nexus {
uint64_t itn_id;
long ctime;
- struct list_head cmd_hash_list[1 << HASH_ORDER];
+ struct cmd_hash_entry cmd_hash_list[1 << HASH_ORDER];
struct target *nexus_target;
--
1.7.1
--
To unsubscribe from this list: send the line "unsubscribe stgt" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
More information about the stgt
mailing list