mirror of
https://github.com/SCST-project/scst.git
synced 2026-08-17 20:56:24 +00:00
- SGV caching changed to be more space efficient
- pre_unreg_sess() added to struct scst_dev_type - Minor fixes and cleanups git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@111 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -719,6 +719,15 @@ struct scst_dev_type
|
||||
*/
|
||||
int (*attach_tgt) (struct scst_tgt_dev *tgt_dev);
|
||||
|
||||
/*
|
||||
* Called when a session, corresponding to a tgt_dev, is about to be
|
||||
* unregistered and the tgt_dev - detached. Supposed to be used to
|
||||
* clean out "stalled" commands, which otherwise could prevent SCST
|
||||
* from entering into the suspended activity state and, so,
|
||||
* unregistering the device.
|
||||
*/
|
||||
void (*pre_unreg_sess) (struct scst_tgt_dev *tgt_dev);
|
||||
|
||||
/* Called when tgt_dev (session) is detaching from the dev handler */
|
||||
void (*detach_tgt) (struct scst_tgt_dev *tgt_dev);
|
||||
|
||||
@@ -851,6 +860,9 @@ struct scst_session
|
||||
/* Used if scst_unregister_session() called in wait mode */
|
||||
struct completion *shutdown_compl;
|
||||
|
||||
/* Used to push some unregister_session() works out of IRQ */
|
||||
struct work_struct unreg_work;
|
||||
|
||||
/*
|
||||
* Functions and data for user callbacks from scst_register_session()
|
||||
* and scst_unregister_session()
|
||||
|
||||
@@ -653,9 +653,9 @@ static int vdisk_do_job(struct scst_cmd *cmd)
|
||||
case WRITE_12:
|
||||
case WRITE_16:
|
||||
fua = (cdb[1] & 0x8);
|
||||
if (cdb[1] & 0x8) {
|
||||
TRACE(TRACE_ORDER, "FUA(%d): loff=%Ld, "
|
||||
"data_len=%Ld", fua, (uint64_t)loff,
|
||||
if (fua) {
|
||||
TRACE(TRACE_ORDER, "FUA: loff=%Ld, "
|
||||
"data_len=%Ld", (uint64_t)loff,
|
||||
(uint64_t)data_len);
|
||||
}
|
||||
break;
|
||||
|
||||
+139
-50
@@ -41,6 +41,15 @@
|
||||
* of the existing SLAB code.
|
||||
*/
|
||||
|
||||
/* Chosen to have one page per slab for all orders */
|
||||
#ifdef CONFIG_DEBUG_SLAB
|
||||
#define SGV_MAX_LOCAL_SLAB_ORDER 4
|
||||
#else
|
||||
#define SGV_MAX_LOCAL_SLAB_ORDER 5
|
||||
#endif
|
||||
|
||||
static int sgv_max_local_order, sgv_max_trans_order;
|
||||
|
||||
atomic_t sgv_other_total_alloc;
|
||||
|
||||
DECLARE_MUTEX(scst_sgv_pool_mutex);
|
||||
@@ -229,20 +238,55 @@ out_no_mem:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static inline struct scatterlist *sgv_alloc_sg_entries(int pages_to_alloc,
|
||||
unsigned long gfp_mask)
|
||||
static int sgv_alloc_sg_entries(struct sgv_pool_obj *obj,
|
||||
int pages_to_alloc, int order, unsigned long gfp_mask)
|
||||
{
|
||||
int esz;
|
||||
struct scatterlist *res;
|
||||
int sz, tsz = 0;
|
||||
int res = 0;
|
||||
|
||||
esz = pages_to_alloc * sizeof(*res);
|
||||
res = (struct scatterlist*)kzalloc(esz, gfp_mask);
|
||||
if (unlikely(res == NULL)) {
|
||||
TRACE_ENTRY();
|
||||
|
||||
sz = pages_to_alloc * sizeof(obj->sg_entries[0]);
|
||||
|
||||
obj->sg_entries = (struct scatterlist*)kzalloc(sz, gfp_mask);
|
||||
if (unlikely(obj->sg_entries == NULL)) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "Allocation of sgv_pool_obj "
|
||||
"SG vector failed (size %d)", esz);
|
||||
"SG vector failed (size %d)", sz);
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (obj->owner_pool->clustered) {
|
||||
if (order <= sgv_max_trans_order) {
|
||||
obj->trans_tbl = (struct trans_tbl_ent*)obj->sg_entries_data;
|
||||
/*
|
||||
* No need to clear trans_tbl, if needed, it will be
|
||||
* fully rewritten in scst_alloc_sg_entries()
|
||||
*/
|
||||
} else {
|
||||
tsz = pages_to_alloc * sizeof(obj->trans_tbl[0]);
|
||||
obj->trans_tbl = (struct trans_tbl_ent*)kzalloc(tsz, gfp_mask);
|
||||
if (unlikely(obj->trans_tbl == NULL)) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "Allocation of trans_tbl "
|
||||
"failed (size %d)", tsz);
|
||||
res = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_MEM("pages_to_alloc %d, order %d, sz %d, tsz %d, obj %p, "
|
||||
"sg_entries %p, trans_tbl %p", pages_to_alloc, order,
|
||||
sz, tsz, obj, obj->sg_entries, obj->trans_tbl);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_free:
|
||||
kfree(obj->sg_entries);
|
||||
obj->sg_entries = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
|
||||
@@ -254,7 +298,6 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
|
||||
struct scatterlist *res;
|
||||
int pages_to_alloc;
|
||||
struct kmem_cache *cache;
|
||||
struct trans_tbl_ent *trans_tbl;
|
||||
int no_cached = flags & SCST_POOL_ALLOC_NO_CACHED;
|
||||
|
||||
sBUG_ON(size == 0);
|
||||
@@ -268,13 +311,10 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
|
||||
if (*sgv != NULL) {
|
||||
obj = *sgv;
|
||||
TRACE_MEM("Supplied sgv_obj %p", obj);
|
||||
pages_to_alloc = obj->sg_count;
|
||||
pages_to_alloc = (1 << order);
|
||||
cache = obj->owner_cache;
|
||||
trans_tbl = obj->trans_tbl;
|
||||
EXTRACHECKS_BUG_ON(obj->sg_entries != NULL);
|
||||
obj->sg_entries = sgv_alloc_sg_entries(pages_to_alloc, gfp_mask);
|
||||
if (unlikely(obj->sg_entries == NULL))
|
||||
goto out_fail_free;
|
||||
EXTRACHECKS_BUG_ON(cache != pool->caches[order]);
|
||||
EXTRACHECKS_BUG_ON(obj->sg_count != 0);
|
||||
goto alloc;
|
||||
}
|
||||
|
||||
@@ -287,30 +327,51 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
|
||||
"sgv_pool_obj failed (size %d)", size);
|
||||
goto out_fail;
|
||||
}
|
||||
if (obj->sg_entries != NULL) {
|
||||
if (obj->sg_count != 0) {
|
||||
TRACE_MEM("Cached sgv_obj %p", obj);
|
||||
EXTRACHECKS_BUG_ON(obj->owner_cache != cache);
|
||||
atomic_inc(&pool->acc.hit_alloc);
|
||||
atomic_inc(&pool->cache_acc[order].hit_alloc);
|
||||
goto success;
|
||||
}
|
||||
obj->owner_cache = cache;
|
||||
pages_to_alloc = (1 << order);
|
||||
if (flags & SCST_POOL_NO_ALLOC_ON_CACHE_MISS) {
|
||||
if (flags & SCST_POOL_RETURN_OBJ_ON_ALLOC_FAIL)
|
||||
goto out_return;
|
||||
else
|
||||
if (!(flags & SCST_POOL_RETURN_OBJ_ON_ALLOC_FAIL))
|
||||
goto out_fail_free;
|
||||
}
|
||||
TRACE_MEM("Brand new sgv_obj %p", obj);
|
||||
obj->sg_entries = sgv_alloc_sg_entries(pages_to_alloc, gfp_mask);
|
||||
if (unlikely(obj->sg_entries == NULL))
|
||||
goto out_fail_free;
|
||||
trans_tbl = obj->trans_tbl;
|
||||
/*
|
||||
* No need to clear trans_tbl, if needed, it will be fully
|
||||
* rewritten in scst_alloc_sg_entries()
|
||||
*/
|
||||
obj->owner_cache = cache;
|
||||
obj->owner_pool = pool;
|
||||
if (order <= sgv_max_local_order) {
|
||||
obj->sg_entries = obj->sg_entries_data;
|
||||
TRACE_MEM("sg_entries %p", obj->sg_entries);
|
||||
memset(obj->sg_entries, 0,
|
||||
pages_to_alloc*sizeof(obj->sg_entries[0]));
|
||||
if (pool->clustered) {
|
||||
obj->trans_tbl = (struct trans_tbl_ent*)
|
||||
(obj->sg_entries + pages_to_alloc);
|
||||
TRACE_MEM("trans_tbl %p", obj->trans_tbl);
|
||||
/* We want to have all the data on the same page */
|
||||
EXTRACHECKS_BUG_ON(((unsigned long)obj->sg_entries & PAGE_MASK) !=
|
||||
((unsigned long)&obj->trans_tbl[pages_to_alloc-1] & PAGE_MASK));
|
||||
/*
|
||||
* No need to clear trans_tbl, if needed, it will
|
||||
* be fully rewritten in scst_alloc_sg_entries()
|
||||
*/
|
||||
} else {
|
||||
/* We want to have all the data on the same page */
|
||||
EXTRACHECKS_BUG_ON(((unsigned long)obj->sg_entries & PAGE_MASK) !=
|
||||
((unsigned long)&obj->sg_entries[pages_to_alloc-1] & PAGE_MASK));
|
||||
}
|
||||
} else {
|
||||
if (unlikely(sgv_alloc_sg_entries(obj, pages_to_alloc,
|
||||
order, gfp_mask) != 0))
|
||||
goto out_fail_free;
|
||||
}
|
||||
|
||||
if ((flags & SCST_POOL_NO_ALLOC_ON_CACHE_MISS) &&
|
||||
(flags & SCST_POOL_RETURN_OBJ_ON_ALLOC_FAIL))
|
||||
goto out_return;
|
||||
} else {
|
||||
int sz;
|
||||
pages_to_alloc = pages;
|
||||
@@ -324,24 +385,22 @@ struct scatterlist *sgv_pool_alloc(struct sgv_pool *pool, unsigned int size,
|
||||
"sgv_pool_obj failed (size %d)", size);
|
||||
goto out_fail;
|
||||
}
|
||||
obj->sg_entries = (struct scatterlist*)obj->trans_tbl;
|
||||
trans_tbl = NULL;
|
||||
obj->owner_pool = pool;
|
||||
obj->sg_entries = obj->sg_entries_data;
|
||||
TRACE_MEM("Big or no_cached sgv_obj %p (size %d)", obj, sz);
|
||||
}
|
||||
|
||||
obj->allocator_priv = priv;
|
||||
obj->owner_pool = pool;
|
||||
|
||||
alloc:
|
||||
obj->sg_count = scst_alloc_sg_entries(obj->sg_entries,
|
||||
pages_to_alloc, gfp_mask, pool->clustered, trans_tbl,
|
||||
pages_to_alloc, gfp_mask, pool->clustered, obj->trans_tbl,
|
||||
&pool->alloc_fns, priv);
|
||||
if (unlikely(obj->sg_count <= 0)) {
|
||||
if ((flags & SCST_POOL_RETURN_OBJ_ON_ALLOC_FAIL) && cache) {
|
||||
kfree(obj->sg_entries);
|
||||
obj->sg_entries = NULL;
|
||||
obj->sg_count = 0;
|
||||
if ((flags & SCST_POOL_RETURN_OBJ_ON_ALLOC_FAIL) && cache)
|
||||
goto out_return1;
|
||||
} else
|
||||
else
|
||||
goto out_fail_free_sg_entries;
|
||||
}
|
||||
|
||||
@@ -353,7 +412,7 @@ success:
|
||||
if (pool->clustered)
|
||||
cnt = obj->trans_tbl[pages-1].sg_num;
|
||||
else
|
||||
cnt = obj->sg_count;
|
||||
cnt = pages;
|
||||
sg = cnt-1;
|
||||
obj->orig_sg = sg;
|
||||
obj->orig_length = obj->sg_entries[sg].length;
|
||||
@@ -389,7 +448,6 @@ out_return:
|
||||
|
||||
out_return1:
|
||||
*sgv = obj;
|
||||
obj->sg_count = pages_to_alloc;
|
||||
TRACE_MEM("Returning failed sgv_obj %p (count %d)", obj, *count);
|
||||
|
||||
out_return2:
|
||||
@@ -398,7 +456,12 @@ out_return2:
|
||||
goto out;
|
||||
|
||||
out_fail_free_sg_entries:
|
||||
if (cache) {
|
||||
if (obj->sg_entries != obj->sg_entries_data) {
|
||||
if (obj->trans_tbl != (struct trans_tbl_ent*)obj->sg_entries_data) {
|
||||
/* kfree() handles NULL parameter */
|
||||
kfree(obj->trans_tbl);
|
||||
obj->trans_tbl = NULL;
|
||||
}
|
||||
kfree(obj->sg_entries);
|
||||
obj->sg_entries = NULL;
|
||||
}
|
||||
@@ -428,8 +491,7 @@ void sgv_pool_free(struct sgv_pool_obj *sgv)
|
||||
"sg_count %d, allocator_priv %p", sgv, sgv->owner_cache,
|
||||
sgv->sg_entries, sgv->sg_count, sgv->allocator_priv);
|
||||
if (sgv->owner_cache != NULL) {
|
||||
if (likely(sgv->sg_entries != NULL))
|
||||
sgv->sg_entries[sgv->orig_sg].length = sgv->orig_length;
|
||||
sgv->sg_entries[sgv->orig_sg].length = sgv->orig_length;
|
||||
kmem_cache_free(sgv->owner_cache, sgv);
|
||||
} else {
|
||||
sgv->owner_pool->alloc_fns.free_pages_fn(sgv->sg_entries,
|
||||
@@ -455,9 +517,16 @@ static void sgv_ctor(void *data, struct kmem_cache *c, unsigned long flags)
|
||||
static void sgv_dtor(void *data, struct kmem_cache *k, unsigned long f)
|
||||
{
|
||||
struct sgv_pool_obj *obj = data;
|
||||
if (obj->sg_entries) {
|
||||
if (obj->sg_count != 0) {
|
||||
obj->owner_pool->alloc_fns.free_pages_fn(obj->sg_entries,
|
||||
obj->sg_count, obj->allocator_priv);
|
||||
}
|
||||
if (obj->sg_entries != obj->sg_entries_data) {
|
||||
if (obj->trans_tbl != (struct trans_tbl_ent*)obj->sg_entries_data) {
|
||||
/* kfree() handles NULL parameter */
|
||||
kfree(obj->trans_tbl);
|
||||
obj->trans_tbl = NULL;
|
||||
}
|
||||
kfree(obj->sg_entries);
|
||||
}
|
||||
return;
|
||||
@@ -522,23 +591,31 @@ int sgv_pool_init(struct sgv_pool *pool, const char *name, int clustered)
|
||||
pool->alloc_fns.alloc_pages_fn = scst_alloc_sys_pages;
|
||||
pool->alloc_fns.free_pages_fn = scst_free_sys_sg_entries;
|
||||
|
||||
TRACE_MEM("name %s, sizeof(*obj)=%zd, clustered=%d, "
|
||||
"sizeof(obj->trans_tbl[0])=%zd", name, sizeof(*obj), clustered,
|
||||
sizeof(obj->trans_tbl[0]));
|
||||
TRACE_MEM("name %s, sizeof(*obj)=%zd, clustered=%d", name, sizeof(*obj),
|
||||
clustered);
|
||||
|
||||
strncpy(pool->name, name, sizeof(pool->name)-1);
|
||||
pool->name[sizeof(pool->name)-1] = '\0';
|
||||
|
||||
for(i = 0; i < SGV_POOL_ELEMENTS; i++) {
|
||||
int size, pages;
|
||||
int size;
|
||||
|
||||
atomic_set(&pool->cache_acc[i].total_alloc, 0);
|
||||
atomic_set(&pool->cache_acc[i].hit_alloc, 0);
|
||||
|
||||
pages = 1 << i;
|
||||
size = sizeof(*obj) + pages *
|
||||
(clustered ? sizeof(obj->trans_tbl[0]) : 0);
|
||||
TRACE_MEM("pages=%d, size=%d", pages, size);
|
||||
/*
|
||||
* We need one page per SLAB. That's hackish, but is there
|
||||
* any other choice?
|
||||
*/
|
||||
if (i <= SGV_MAX_LOCAL_SLAB_ORDER) {
|
||||
int pages = 1 << i;
|
||||
size = sizeof(*obj) + pages *
|
||||
(sizeof(obj->sg_entries[0]) +
|
||||
(clustered ? sizeof(obj->trans_tbl[0]) : 0));
|
||||
} else
|
||||
size = PAGE_SIZE - 96;
|
||||
|
||||
TRACE_MEM("pages=%d, size=%d", 1 << i, size);
|
||||
|
||||
scnprintf(pool->cache_names[i], sizeof(pool->cache_names[i]),
|
||||
"%s-%luK", name, (PAGE_SIZE >> 10) << i);
|
||||
@@ -643,6 +720,18 @@ int scst_sgv_pools_init(struct scst_sgv_pools *pools)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
sgv_max_local_order = get_order(
|
||||
((((PAGE_SIZE - sizeof(struct sgv_pool_obj)) /
|
||||
(sizeof(struct trans_tbl_ent) + sizeof(struct scatterlist))) *
|
||||
PAGE_SIZE) & PAGE_MASK));
|
||||
|
||||
sgv_max_trans_order = get_order(
|
||||
((((PAGE_SIZE - sizeof(struct sgv_pool_obj)) /
|
||||
(sizeof(struct trans_tbl_ent))) * PAGE_SIZE) & PAGE_MASK));
|
||||
|
||||
TRACE_MEM("sgv_max_local_order %d, sgv_max_trans_order %d",
|
||||
sgv_max_local_order, sgv_max_trans_order);
|
||||
|
||||
atomic_set(&sgv_other_total_alloc, 0);
|
||||
|
||||
res = sgv_pool_init(&pools->norm, "sgv", 0);
|
||||
|
||||
+3
-2
@@ -41,9 +41,10 @@ struct sgv_pool_obj
|
||||
int orig_sg;
|
||||
int orig_length;
|
||||
int sg_count;
|
||||
struct scatterlist *sg_entries;
|
||||
void *allocator_priv;
|
||||
struct trans_tbl_ent trans_tbl[0];
|
||||
struct trans_tbl_ent *trans_tbl;
|
||||
struct scatterlist *sg_entries;
|
||||
struct scatterlist sg_entries_data[0];
|
||||
};
|
||||
|
||||
struct sgv_pool_acc
|
||||
|
||||
+51
-9
@@ -3536,13 +3536,13 @@ static int scst_abort_all_nexus_loss_sess(struct scst_mgmt_cmd *mcmd,
|
||||
__scst_block_dev(dev);
|
||||
spin_unlock_bh(&dev->dev_lock);
|
||||
|
||||
rc = scst_call_dev_task_mgmt_fn(mcmd, tgt_dev, 0);
|
||||
if ((rc < 0) && (mcmd->status == SCST_MGMT_STATUS_SUCCESS))
|
||||
mcmd->status = rc;
|
||||
|
||||
__scst_abort_task_set(mcmd, tgt_dev, !nexus_loss, 1);
|
||||
if (nexus_loss)
|
||||
scst_reset_tgt_dev(tgt_dev, 1);
|
||||
|
||||
rc = scst_call_dev_task_mgmt_fn(mcmd, tgt_dev, 0);
|
||||
if ((rc < 0) && (mcmd->status == SCST_MGMT_STATUS_SUCCESS))
|
||||
mcmd->status = rc;
|
||||
}
|
||||
up(&scst_mutex);
|
||||
|
||||
@@ -3586,14 +3586,14 @@ static int scst_abort_all_nexus_loss_tgt(struct scst_mgmt_cmd *mcmd,
|
||||
{
|
||||
int rc;
|
||||
|
||||
__scst_abort_task_set(mcmd, tgt_dev, !nexus_loss, 1);
|
||||
if (nexus_loss)
|
||||
scst_reset_tgt_dev(tgt_dev, 1);
|
||||
|
||||
rc = scst_call_dev_task_mgmt_fn(mcmd, tgt_dev, 0);
|
||||
if ((rc < 0) &&
|
||||
(mcmd->status == SCST_MGMT_STATUS_SUCCESS))
|
||||
mcmd->status = rc;
|
||||
|
||||
__scst_abort_task_set(mcmd, tgt_dev, !nexus_loss, 1);
|
||||
if (nexus_loss)
|
||||
scst_reset_tgt_dev(tgt_dev, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4197,6 +4197,42 @@ out_free:
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
|
||||
static void scst_unreg_work_fn(void *p)
|
||||
#else
|
||||
static void scst_unreg_work_fn(struct work_struct *work)
|
||||
#endif
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
|
||||
struct scst_session *sess = (struct scst_session*)p;
|
||||
#else
|
||||
struct scst_session *sess = container_of(work, struct scst_session,
|
||||
unreg_work);
|
||||
#endif
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
down(&scst_mutex);
|
||||
list_for_each_entry(tgt_dev, &sess->sess_tgt_dev_list,
|
||||
sess_tgt_dev_list_entry) {
|
||||
struct scst_dev_type *handler = tgt_dev->dev->handler;
|
||||
if (handler && handler->pre_unreg_sess) {
|
||||
TRACE_DBG("Calling dev handler's pre_unreg_sess(%p)",
|
||||
tgt_dev);
|
||||
handler->pre_unreg_sess(tgt_dev);
|
||||
TRACE_DBG("%s", "Dev handler's pre_unreg_sess() "
|
||||
"returned");
|
||||
}
|
||||
}
|
||||
up(&scst_mutex);
|
||||
|
||||
scst_sess_put(sess);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must not been called in parallel with scst_rx_cmd() or
|
||||
* scst_rx_mgmt_fn_*() for the same sess
|
||||
@@ -4235,7 +4271,13 @@ void scst_unregister_session(struct scst_session *sess, int wait,
|
||||
|
||||
tm_dbg_task_mgmt("UNREGISTER SESSION", 1);
|
||||
|
||||
scst_sess_put(sess);
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
|
||||
INIT_WORK(&sess->unreg_work, scst_unreg_work_fn, sess);
|
||||
#else
|
||||
INIT_WORK(&sess->unreg_work, scst_unreg_work_fn);
|
||||
#endif
|
||||
|
||||
schedule_work(&sess->unreg_work);
|
||||
|
||||
if (wait) {
|
||||
TRACE_DBG("Waiting for session %p to complete", sess);
|
||||
|
||||
Reference in New Issue
Block a user