mirror of
https://github.com/SCST-project/scst.git
synced 2026-07-20 06:52:18 +00:00
Patch from Alexey Obitotskiy <alexeyo1@open-e.com> with 2 fixes and cleanups implementing initiator-oriented access control in sysfs interface.
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1437 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
+19
-2
@@ -1106,6 +1106,11 @@ struct scst_tgt {
|
||||
|
||||
struct scst_acg *default_acg; /* The default acg for this target. */
|
||||
|
||||
/*
|
||||
* Device ACG groups
|
||||
*/
|
||||
struct list_head acg_list;
|
||||
|
||||
/*
|
||||
* Maximum SG table size. Needed here, since different cards on the
|
||||
* same target template can have different SG table limitations.
|
||||
@@ -1966,8 +1971,8 @@ struct scst_acg {
|
||||
/* List of attached acn's, protected by scst_mutex */
|
||||
struct list_head acn_list;
|
||||
|
||||
/* List entry in scst_acg_list */
|
||||
struct list_head scst_acg_list_entry;
|
||||
/* List entry in tgt acg_list */
|
||||
struct list_head acg_list_entry;
|
||||
|
||||
/* Name of this acg */
|
||||
const char *acg_name;
|
||||
@@ -1976,6 +1981,15 @@ struct scst_acg {
|
||||
/* The pointer to the /proc directory entry */
|
||||
struct proc_dir_entry *acg_proc_root;
|
||||
#endif
|
||||
|
||||
unsigned int acg_kobj_initialized:1;
|
||||
unsigned int in_tgt_acg_list:1;
|
||||
|
||||
/* kobject for this structure */
|
||||
struct kobject acg_kobj;
|
||||
|
||||
struct kobject *luns_kobj;
|
||||
struct kobject *initiators_kobj;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1987,6 +2001,9 @@ struct scst_acn {
|
||||
const char *name;
|
||||
/* List entry in acg->acn_list */
|
||||
struct list_head acn_list_entry;
|
||||
|
||||
/* sysfs file attributes */
|
||||
struct kobj_attribute *acn_attr;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+141
-29
@@ -1808,6 +1808,14 @@ void scst_free_tgt(struct scst_tgt *tgt)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (tgt->default_acg != NULL)
|
||||
scst_free_acg(tgt->default_acg);
|
||||
|
||||
kfree(tgt->tgt_name);
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
kfree(tgt->default_group_name);
|
||||
#endif
|
||||
|
||||
kfree(tgt);
|
||||
|
||||
TRACE_EXIT();
|
||||
@@ -1930,6 +1938,11 @@ void scst_acg_dev_destroy(struct scst_acg_dev *acg_dev)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifndef CONFIG_SCST_PROC
|
||||
if ((acg_dev->dev != NULL) && acg_dev->dev->dev_kobj_initialized)
|
||||
kobject_put(&acg_dev->dev->dev_kobj);
|
||||
#endif
|
||||
|
||||
kmem_cache_free(scst_acgd_cachep, acg_dev);
|
||||
|
||||
TRACE_EXIT();
|
||||
@@ -1957,7 +1970,8 @@ static void scst_free_acg_dev(struct scst_acg_dev *acg_dev)
|
||||
}
|
||||
|
||||
/* The activity supposed to be suspended and scst_mutex held */
|
||||
struct scst_acg *scst_alloc_add_acg(const char *acg_name)
|
||||
struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt,
|
||||
const char *acg_name)
|
||||
{
|
||||
struct scst_acg *acg;
|
||||
|
||||
@@ -1965,43 +1979,78 @@ struct scst_acg *scst_alloc_add_acg(const char *acg_name)
|
||||
|
||||
acg = kzalloc(sizeof(*acg), GFP_KERNEL);
|
||||
if (acg == NULL) {
|
||||
TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of acg failed");
|
||||
PRINT_ERROR("%s", "Allocation of acg failed");
|
||||
goto out;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&acg->acg_dev_list);
|
||||
INIT_LIST_HEAD(&acg->acg_sess_list);
|
||||
INIT_LIST_HEAD(&acg->acn_list);
|
||||
acg->acg_name = acg_name;
|
||||
acg->acg_name = kstrdup(acg_name, GFP_KERNEL);
|
||||
if (acg->acg_name == NULL) {
|
||||
PRINT_ERROR("%s", "Allocation of acg_name failed");
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
TRACE_DBG("Adding acg %s to scst_acg_list", acg_name);
|
||||
list_add_tail(&acg->scst_acg_list_entry, &scst_acg_list);
|
||||
list_add_tail(&acg->acg_list_entry, &scst_acg_list);
|
||||
|
||||
scst_check_reassign_sessions();
|
||||
#else
|
||||
if (tgt != NULL) {
|
||||
TRACE_DBG("Adding acg '%s' to device '%s' acg_list", acg_name,
|
||||
tgt->tgt_name);
|
||||
list_add_tail(&acg->acg_list_entry, &tgt->acg_list);
|
||||
acg->in_tgt_acg_list = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
out:
|
||||
TRACE_EXIT_HRES(acg);
|
||||
return acg;
|
||||
|
||||
out_free:
|
||||
kfree(acg);
|
||||
acg = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
void scst_free_acg(struct scst_acg *acg)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
sBUG_ON(!list_empty(&acg->acg_sess_list));
|
||||
sBUG_ON(!list_empty(&acg->acg_dev_list));
|
||||
sBUG_ON(!list_empty(&acg->acn_list));
|
||||
|
||||
kfree(acg->acg_name);
|
||||
kfree(acg);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* The activity supposed to be suspended and scst_mutex held */
|
||||
int scst_destroy_acg(struct scst_acg *acg)
|
||||
void scst_clear_acg(struct scst_acg *acg)
|
||||
{
|
||||
struct scst_acn *n, *nn;
|
||||
struct scst_acg_dev *acg_dev, *acg_dev_tmp;
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (!list_empty(&acg->acg_sess_list)) {
|
||||
PRINT_ERROR("%s: acg_sess_list is not empty!", __func__);
|
||||
res = -EBUSY;
|
||||
goto out;
|
||||
TRACE_DBG("Clearing acg %s from list", acg->acg_name);
|
||||
|
||||
sBUG_ON(!list_empty(&acg->acg_sess_list));
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
list_del(&acg->acg_list_entry);
|
||||
#else
|
||||
if (acg->in_tgt_acg_list) {
|
||||
TRACE_DBG("Removing acg %s from list", acg->acg_name);
|
||||
list_del(&acg->acg_list_entry);
|
||||
acg->in_tgt_acg_list = 0;
|
||||
}
|
||||
|
||||
TRACE_DBG("Removing acg %s from scst_acg_list", acg->acg_name);
|
||||
list_del(&acg->scst_acg_list_entry);
|
||||
|
||||
#endif
|
||||
/* Freeing acg_devs */
|
||||
list_for_each_entry_safe(acg_dev, acg_dev_tmp, &acg->acg_dev_list,
|
||||
acg_dev_list_entry) {
|
||||
@@ -2018,16 +2067,49 @@ int scst_destroy_acg(struct scst_acg *acg)
|
||||
/* Freeing names */
|
||||
list_for_each_entry_safe(n, nn, &acg->acn_list,
|
||||
acn_list_entry) {
|
||||
list_del(&n->acn_list_entry);
|
||||
kfree(n->name);
|
||||
kfree(n);
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
scst_acg_remove_acn(n);
|
||||
if (list_is_last(&n->acn_list_entry, &acg->acn_list))
|
||||
scst_check_reassign_sessions();
|
||||
#else
|
||||
scst_acn_sysfs_del(acg, n,
|
||||
list_is_last(&n->acn_list_entry, &acg->acn_list));
|
||||
#endif
|
||||
}
|
||||
INIT_LIST_HEAD(&acg->acn_list);
|
||||
|
||||
kfree(acg);
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* The activity supposed to be suspended and scst_mutex held */
|
||||
void scst_destroy_acg(struct scst_acg *acg)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
scst_clear_acg(acg);
|
||||
scst_free_acg(acg);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
/* The activity supposed to be suspended and scst_mutex held */
|
||||
struct scst_acg *scst_tgt_find_acg(struct scst_tgt *tgt, const char *name)
|
||||
{
|
||||
struct scst_acg *acg, *acg_ret = NULL;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
list_for_each_entry(acg, &tgt->acg_list, acg_list_entry) {
|
||||
if (strcmp(acg->acg_name, name) == 0) {
|
||||
acg_ret = acg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
return acg_ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2483,35 +2565,64 @@ int scst_acg_add_name(struct scst_acg *acg, const char *name)
|
||||
strcpy(nm, name);
|
||||
n->name = nm;
|
||||
|
||||
res = scst_create_acn_sysfs(acg, n);
|
||||
if (res != 0)
|
||||
goto out_free_nm;
|
||||
|
||||
list_add_tail(&n->acn_list_entry, &acg->acn_list);
|
||||
|
||||
out:
|
||||
if (res == 0) {
|
||||
PRINT_INFO("Added name %s to group %s", name, acg->acg_name);
|
||||
PRINT_INFO("Added name '%s' to group '%s'", name, acg->acg_name);
|
||||
scst_check_reassign_sessions();
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_free_nm:
|
||||
kfree(nm);
|
||||
|
||||
out_free:
|
||||
kfree(n);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The activity supposed to be suspended and scst_mutex held */
|
||||
struct scst_acn *scst_acg_find_name(struct scst_acg *acg, const char *name)
|
||||
{
|
||||
struct scst_acn *n;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("Trying to find name '%s'", name);
|
||||
|
||||
list_for_each_entry(n, &acg->acn_list, acn_list_entry) {
|
||||
if (strcmp(n->name, name) == 0) {
|
||||
TRACE_DBG("%s", "Found");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
n = NULL;
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return n;
|
||||
}
|
||||
|
||||
/* scst_mutex supposed to be held */
|
||||
void __scst_acg_remove_acn(struct scst_acn *n)
|
||||
void scst_acg_remove_acn(struct scst_acn *acn)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
list_del(&n->acn_list_entry);
|
||||
kfree(n->name);
|
||||
kfree(n);
|
||||
list_del(&acn->acn_list_entry);
|
||||
kfree(acn->name);
|
||||
kfree(acn);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
/* The activity supposed to be suspended and scst_mutex held */
|
||||
int scst_acg_remove_name(struct scst_acg *acg, const char *name, bool reassign)
|
||||
{
|
||||
@@ -2522,24 +2633,25 @@ int scst_acg_remove_name(struct scst_acg *acg, const char *name, bool reassign)
|
||||
|
||||
list_for_each_entry(n, &acg->acn_list, acn_list_entry) {
|
||||
if (strcmp(n->name, name) == 0) {
|
||||
__scst_acg_remove_acn(n);
|
||||
scst_acg_remove_acn(n);
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
PRINT_INFO("Removed name %s from group %s", name,
|
||||
PRINT_INFO("Removed name '%s' from group '%s'", name,
|
||||
acg->acg_name);
|
||||
if (reassign)
|
||||
scst_check_reassign_sessions();
|
||||
} else
|
||||
PRINT_ERROR("Unable to find name %s in group %s", name,
|
||||
PRINT_ERROR("Unable to find name '%s' in group '%s'", name,
|
||||
acg->acg_name);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct scst_cmd *scst_create_prepare_internal_cmd(
|
||||
struct scst_cmd *orig_cmd, int bufsize)
|
||||
|
||||
+16
-16
@@ -102,8 +102,8 @@ struct kmem_cache *scst_tgtd_cachep;
|
||||
struct kmem_cache *scst_sess_cachep;
|
||||
struct kmem_cache *scst_acgd_cachep;
|
||||
|
||||
struct list_head scst_acg_list;
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
struct list_head scst_acg_list;
|
||||
struct scst_acg *scst_default_acg;
|
||||
#endif
|
||||
|
||||
@@ -416,14 +416,16 @@ struct scst_tgt *scst_register(struct scst_tgt_template *vtt,
|
||||
SCST_DEFAULT_TGT_NAME_SUFFIX, tgt_num++);
|
||||
}
|
||||
|
||||
tgt->default_acg = scst_alloc_add_acg(tgt->tgt_name);
|
||||
tgt->default_acg = scst_alloc_add_acg(NULL, tgt->tgt_name);
|
||||
if (tgt->default_acg == NULL)
|
||||
goto out_free_tgt_name;
|
||||
|
||||
INIT_LIST_HEAD(&tgt->acg_list);
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
rc = scst_build_proc_target_entries(tgt);
|
||||
if (rc < 0)
|
||||
goto out_free_acg;
|
||||
goto out_clear_acg;
|
||||
#endif
|
||||
|
||||
rc = scst_create_tgt_sysfs(tgt);
|
||||
@@ -431,7 +433,7 @@ struct scst_tgt *scst_register(struct scst_tgt_template *vtt,
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
goto out_clean_proc;
|
||||
#else
|
||||
goto out_free_acg;
|
||||
goto out_clear_acg;
|
||||
#endif
|
||||
|
||||
list_add_tail(&tgt->tgt_list_entry, &vtt->tgt_list);
|
||||
@@ -451,8 +453,8 @@ out_clean_proc:
|
||||
scst_cleanup_proc_target_entries(tgt);
|
||||
#endif
|
||||
|
||||
out_free_acg:
|
||||
scst_destroy_acg(tgt->default_acg);
|
||||
out_clear_acg:
|
||||
scst_clear_acg(tgt->default_acg);
|
||||
|
||||
out_free_tgt_name:
|
||||
kfree(tgt->tgt_name);
|
||||
@@ -492,6 +494,7 @@ void scst_unregister(struct scst_tgt *tgt)
|
||||
{
|
||||
struct scst_session *sess;
|
||||
struct scst_tgt_template *vtt = tgt->tgtt;
|
||||
struct scst_acg *acg, *acg_tmp;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -534,16 +537,11 @@ again:
|
||||
mutex_unlock(&scst_mutex);
|
||||
scst_resume_activity();
|
||||
|
||||
/*
|
||||
* It should be before freeing of tgt_name, because acg_name
|
||||
* points to it.
|
||||
*/
|
||||
scst_destroy_acg(tgt->default_acg);
|
||||
scst_clear_acg(tgt->default_acg);
|
||||
|
||||
kfree(tgt->tgt_name);
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
kfree(tgt->default_group_name);
|
||||
#endif
|
||||
list_for_each_entry_safe(acg, acg_tmp, &tgt->acg_list, acg_list_entry) {
|
||||
scst_acg_sysfs_put(acg);
|
||||
}
|
||||
|
||||
del_timer_sync(&tgt->retry_timer);
|
||||
|
||||
@@ -1904,7 +1902,9 @@ static int __init init_scst(void)
|
||||
INIT_LIST_HEAD(&scst_dev_list);
|
||||
INIT_LIST_HEAD(&scst_dev_type_list);
|
||||
spin_lock_init(&scst_main_lock);
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
INIT_LIST_HEAD(&scst_acg_list);
|
||||
#endif
|
||||
spin_lock_init(&scst_init_lock);
|
||||
init_waitqueue_head(&scst_init_cmd_list_waitQ);
|
||||
INIT_LIST_HEAD(&scst_init_cmd_list);
|
||||
@@ -2048,7 +2048,7 @@ static int __init init_scst(void)
|
||||
goto out_sysfs_cleanup;
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
scst_default_acg = scst_alloc_add_acg(SCST_DEFAULT_ACG_NAME);
|
||||
scst_default_acg = scst_alloc_add_acg(NULL, SCST_DEFAULT_ACG_NAME);
|
||||
if (scst_default_acg == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto out_destroy_sgv_pool;
|
||||
|
||||
+34
-4
@@ -150,8 +150,8 @@ extern struct list_head scst_dev_list;
|
||||
extern struct list_head scst_dev_type_list;
|
||||
extern wait_queue_head_t scst_dev_cmd_waitQ;
|
||||
|
||||
extern struct list_head scst_acg_list;
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
extern struct list_head scst_acg_list;
|
||||
extern struct scst_acg *scst_default_acg;
|
||||
#endif
|
||||
|
||||
@@ -303,8 +303,13 @@ void scst_free_tgt(struct scst_tgt *tgt);
|
||||
int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev);
|
||||
void scst_free_device(struct scst_device *dev);
|
||||
|
||||
struct scst_acg *scst_alloc_add_acg(const char *acg_name);
|
||||
int scst_destroy_acg(struct scst_acg *acg);
|
||||
struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt,
|
||||
const char *acg_name);
|
||||
void scst_clear_acg(struct scst_acg *acg);
|
||||
void scst_destroy_acg(struct scst_acg *acg);
|
||||
void scst_free_acg(struct scst_acg *acg);
|
||||
|
||||
struct scst_acg *scst_tgt_find_acg(struct scst_tgt *tgt, const char *name);
|
||||
|
||||
struct scst_acg *scst_find_acg(const struct scst_session *sess);
|
||||
|
||||
@@ -322,8 +327,17 @@ int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev,
|
||||
void scst_acg_dev_destroy(struct scst_acg_dev *acg_dev);
|
||||
|
||||
int scst_acg_add_name(struct scst_acg *acg, const char *name);
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
int scst_acg_remove_name(struct scst_acg *acg, const char *name, bool reassign);
|
||||
void __scst_acg_remove_acn(struct scst_acn *n);
|
||||
#endif
|
||||
void scst_acg_remove_acn(struct scst_acn *acn);
|
||||
struct scst_acn *scst_acg_find_name(struct scst_acg *acg, const char *name);
|
||||
|
||||
/* The activity supposed to be suspended and scst_mutex held */
|
||||
static inline bool scst_acg_sess_is_empty(struct scst_acg *acg)
|
||||
{
|
||||
return list_empty(&acg->acg_sess_list);
|
||||
}
|
||||
|
||||
int scst_prepare_request_sense(struct scst_cmd *orig_cmd);
|
||||
int scst_finish_internal_cmd(struct scst_cmd *cmd);
|
||||
@@ -490,6 +504,17 @@ static inline int scst_create_devt_dev_sysfs(struct scst_device *dev)
|
||||
}
|
||||
static inline void scst_devt_dev_sysfs_put(struct scst_device *dev) { }
|
||||
|
||||
static inline int scst_create_acn_sysfs(struct scst_acg *acg,
|
||||
struct scst_acn *acn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void scst_acn_sysfs_del(struct scst_acg *acg,
|
||||
struct scst_acn *acn, bool reassign) { }
|
||||
|
||||
static inline void scst_acg_sysfs_put(struct scst_acg *acg) { }
|
||||
|
||||
#else /* CONFIG_SCST_PROC */
|
||||
|
||||
int scst_sysfs_init(void);
|
||||
@@ -509,6 +534,11 @@ int scst_create_device_sysfs(struct scst_device *dev);
|
||||
void scst_device_sysfs_put(struct scst_device *dev);
|
||||
int scst_create_devt_dev_sysfs(struct scst_device *dev);
|
||||
void scst_devt_dev_sysfs_put(struct scst_device *dev);
|
||||
int scst_create_acn_sysfs(struct scst_acg *acg, struct scst_acn *acn);
|
||||
void scst_acn_sysfs_del(struct scst_acg *acg, struct scst_acn *acn,
|
||||
bool reassign);
|
||||
|
||||
void scst_acg_sysfs_put(struct scst_acg *acg);
|
||||
|
||||
#endif /* CONFIG_SCST_PROC */
|
||||
|
||||
|
||||
+18
-16
@@ -437,7 +437,7 @@ static int lat_info_show(struct seq_file *seq, void *v)
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
|
||||
list_for_each_entry(acg, &scst_acg_list, acg_list_entry) {
|
||||
bool header_printed = false;
|
||||
|
||||
list_for_each_entry(sess, &acg->acg_sess_list,
|
||||
@@ -688,7 +688,7 @@ static ssize_t scst_proc_scsi_tgt_gen_write_lat(struct file *file,
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
|
||||
list_for_each_entry(acg, &scst_acg_list, acg_list_entry) {
|
||||
list_for_each_entry(sess, &acg->acg_sess_list,
|
||||
acg_sess_list_entry) {
|
||||
PRINT_INFO("Zeroing latency statistics for initiator "
|
||||
@@ -881,7 +881,7 @@ static int scst_proc_group_add(const char *p)
|
||||
}
|
||||
strncpy(name, p, len);
|
||||
|
||||
acg = scst_alloc_add_acg(name);
|
||||
acg = scst_alloc_add_acg(NULL, name);
|
||||
if (acg == NULL) {
|
||||
PRINT_ERROR("scst_alloc_add_acg() (name %s) failed", name);
|
||||
goto out_free;
|
||||
@@ -909,22 +909,22 @@ out_nomem:
|
||||
/* The activity supposed to be suspended and scst_mutex held */
|
||||
static int scst_proc_del_free_acg(struct scst_acg *acg, int remove_proc)
|
||||
{
|
||||
const char *name;
|
||||
struct proc_dir_entry *acg_proc_root = acg->acg_proc_root;
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (acg != scst_default_acg) {
|
||||
name = acg->acg_name;
|
||||
res = scst_destroy_acg(acg);
|
||||
if (res == 0) {
|
||||
if (remove_proc)
|
||||
scst_proc_del_acg_tree(acg_proc_root, name);
|
||||
kfree(name);
|
||||
if (!scst_acg_sess_is_empty(acg)) {
|
||||
PRINT_ERROR("%s", "Session is not empty");
|
||||
res = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
if (remove_proc)
|
||||
scst_proc_del_acg_tree(acg_proc_root, acg->acg_name);
|
||||
scst_destroy_acg(acg);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
@@ -1009,7 +1009,7 @@ static void scst_proc_cleanup_groups(void)
|
||||
|
||||
/* remove all groups (dir & entries) */
|
||||
list_for_each_entry_safe(acg, acg_tmp, &scst_acg_list,
|
||||
scst_acg_list_entry) {
|
||||
acg_list_entry) {
|
||||
scst_proc_del_free_acg(acg, 1);
|
||||
}
|
||||
|
||||
@@ -1635,7 +1635,7 @@ static ssize_t scst_proc_scsi_tgt_gen_write(struct file *file,
|
||||
goto out_up_free;
|
||||
}
|
||||
|
||||
list_for_each_entry(a, &scst_acg_list, scst_acg_list_entry) {
|
||||
list_for_each_entry(a, &scst_acg_list, acg_list_entry) {
|
||||
if (strcmp(a->acg_name, p) == 0) {
|
||||
TRACE_DBG("group (acg) %p %s found",
|
||||
a, a->acg_name);
|
||||
@@ -2172,7 +2172,7 @@ static ssize_t scst_proc_groups_names_write(struct file *file,
|
||||
goto out_free_unlock;
|
||||
}
|
||||
}
|
||||
list_for_each_entry(a, &scst_acg_list, scst_acg_list_entry) {
|
||||
list_for_each_entry(a, &scst_acg_list, acg_list_entry) {
|
||||
if (strcmp(a->acg_name, p) == 0) {
|
||||
TRACE_DBG("group (acg) %p %s found",
|
||||
a, a->acg_name);
|
||||
@@ -2189,12 +2189,14 @@ static ssize_t scst_proc_groups_names_write(struct file *file,
|
||||
if (rc != 0)
|
||||
goto out_free_unlock;
|
||||
rc = scst_acg_add_name(new_acg, name);
|
||||
if (rc != 0)
|
||||
scst_acg_add_name(acg, name);
|
||||
break;
|
||||
}
|
||||
case SCST_PROC_ACTION_CLEAR:
|
||||
list_for_each_entry_safe(n, nn, &acg->acn_list,
|
||||
acn_list_entry) {
|
||||
__scst_acg_remove_acn(n);
|
||||
scst_acg_remove_acn(n);
|
||||
}
|
||||
scst_check_reassign_sessions();
|
||||
break;
|
||||
@@ -2327,7 +2329,7 @@ static int scst_sessions_info_show(struct seq_file *seq, void *v)
|
||||
"Target name", "Initiator name",
|
||||
"Group name", "Active/All Commands Count");
|
||||
|
||||
list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
|
||||
list_for_each_entry(acg, &scst_acg_list, acg_list_entry) {
|
||||
list_for_each_entry(sess, &acg->acg_sess_list,
|
||||
acg_sess_list_entry) {
|
||||
int active_cmds = 0, t;
|
||||
|
||||
+756
-79
@@ -109,6 +109,26 @@ static ssize_t scst_luns_mgmt_show(struct kobject *kobj,
|
||||
static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t scst_ini_group_mgmt_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf);
|
||||
static ssize_t scst_ini_group_mgmt_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t scst_acg_luns_mgmt_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf);
|
||||
static ssize_t scst_acg_luns_mgmt_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t scst_acg_ini_mgmt_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf);
|
||||
static ssize_t scst_acg_ini_mgmt_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
static ssize_t scst_acn_file_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf);
|
||||
|
||||
static void scst_sysfs_release(struct kobject *kobj)
|
||||
{
|
||||
@@ -288,7 +308,7 @@ static void scst_tgt_release(struct kobject *kobj)
|
||||
}
|
||||
|
||||
static ssize_t scst_tgt_attr_show(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf)
|
||||
char *buf)
|
||||
{
|
||||
int res;
|
||||
struct kobj_attribute *kobj_attr;
|
||||
@@ -311,8 +331,8 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t scst_tgt_attr_store(struct kobject *kobj, struct attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t scst_tgt_attr_store(struct kobject *kobj,
|
||||
struct attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int res;
|
||||
struct kobj_attribute *kobj_attr;
|
||||
@@ -345,10 +365,41 @@ static struct kobj_type tgt_ktype = {
|
||||
.release = scst_tgt_release,
|
||||
};
|
||||
|
||||
static void scst_acg_release(struct kobject *kobj)
|
||||
{
|
||||
struct scst_acg *acg;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
acg = container_of(kobj, struct scst_acg, acg_kobj);
|
||||
|
||||
scst_destroy_acg(acg);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static struct kobj_type acg_ktype = {
|
||||
.sysfs_ops = &scst_sysfs_ops,
|
||||
.release = scst_acg_release,
|
||||
};
|
||||
|
||||
static struct kobj_attribute scst_luns_mgmt =
|
||||
__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_luns_mgmt_show,
|
||||
scst_luns_mgmt_store);
|
||||
|
||||
static struct kobj_attribute scst_acg_luns_mgmt =
|
||||
__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_acg_luns_mgmt_show,
|
||||
scst_acg_luns_mgmt_store);
|
||||
|
||||
static struct kobj_attribute scst_acg_ini_mgmt =
|
||||
__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_acg_ini_mgmt_show,
|
||||
scst_acg_ini_mgmt_store);
|
||||
|
||||
static struct kobj_attribute scst_ini_group_mgmt =
|
||||
__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_ini_group_mgmt_show,
|
||||
scst_ini_group_mgmt_store);
|
||||
|
||||
static ssize_t scst_tgt_enable_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
@@ -458,6 +509,14 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt)
|
||||
goto out_nomem;
|
||||
}
|
||||
|
||||
retval = sysfs_create_file(tgt->tgt_ini_grp_kobj,
|
||||
&scst_ini_group_mgmt.attr);
|
||||
if (retval != 0) {
|
||||
PRINT_ERROR("Can't add tgt attr %s for tgt %s",
|
||||
scst_ini_group_mgmt.attr.name, tgt->tgt_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pattr = tgt->tgtt->tgt_attrs;
|
||||
if (pattr != NULL) {
|
||||
while (*pattr != NULL) {
|
||||
@@ -506,8 +565,6 @@ void scst_tgt_sysfs_put(struct scst_tgt *tgt)
|
||||
kobject_del(tgt->tgt_sess_kobj);
|
||||
kobject_put(tgt->tgt_sess_kobj);
|
||||
|
||||
sysfs_remove_file(tgt->tgt_luns_kobj, &scst_luns_mgmt.attr);
|
||||
|
||||
kobject_del(tgt->tgt_luns_kobj);
|
||||
kobject_put(tgt->tgt_luns_kobj);
|
||||
|
||||
@@ -752,10 +809,9 @@ void scst_device_sysfs_put(struct scst_device *dev)
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (dev->dev_kobj_initialized) {
|
||||
if (dev->dev_exp_kobj != NULL) {
|
||||
kobject_del(dev->dev_exp_kobj);
|
||||
kobject_put(dev->dev_exp_kobj);
|
||||
}
|
||||
kobject_del(dev->dev_exp_kobj);
|
||||
kobject_put(dev->dev_exp_kobj);
|
||||
|
||||
kobject_del(&dev->dev_kobj);
|
||||
|
||||
down_write(&dev->dev_attr_rwsem);
|
||||
@@ -1083,6 +1139,8 @@ int scst_create_acg_dev_sysfs(struct scst_acg *acg, unsigned int virt_lun,
|
||||
snprintf(str, sizeof(str), "export%u",
|
||||
acg_dev->dev->dev_exported_lun_num++);
|
||||
|
||||
kobject_get(&acg_dev->dev->dev_kobj);
|
||||
|
||||
acg_dev->acg_dev_kobj_initialized = 1;
|
||||
|
||||
retval = kobject_init_and_add(&acg_dev->acg_dev_kobj, &acg_dev_ktype,
|
||||
@@ -1115,39 +1173,22 @@ out:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t scst_luns_mgmt_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
static char *help = "Usage: echo \"add|del H:C:I:L lun [READ_ONLY]\" "
|
||||
">mgmt\n"
|
||||
" echo \"add|del VNAME lun [READ_ONLY]\" "
|
||||
">mgmt\n";
|
||||
|
||||
return sprintf(buf, help);
|
||||
}
|
||||
|
||||
static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
static ssize_t __scst_luns_mgmt_store(struct scst_acg *acg,
|
||||
struct kobject *kobj, const char *buf, size_t count)
|
||||
{
|
||||
int res, virt = 0, read_only = 0, action;
|
||||
char *buffer, *p, *e = NULL;
|
||||
unsigned int host, channel = 0, id = 0, lun = 0, virt_lun;
|
||||
struct scst_acg *acg;
|
||||
struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp;
|
||||
struct scst_device *d, *dev = NULL;
|
||||
struct scst_tgt *tgt;
|
||||
|
||||
#define SCST_LUN_ACTION_ADD 1
|
||||
#define SCST_LUN_ACTION_DEL 2
|
||||
#define SCST_LUN_ACTION_REPLACE 3
|
||||
#define SCST_LUN_ACTION_CLEAR 4
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
tgt = container_of(kobj->parent, struct scst_tgt, tgt_kobj);
|
||||
acg = tgt->default_acg;
|
||||
|
||||
buffer = kzalloc(count+1, GFP_KERNEL);
|
||||
if (buffer == NULL) {
|
||||
res = -ENOMEM;
|
||||
@@ -1170,18 +1211,15 @@ static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
|
||||
} else if (!strncasecmp("replace", p, 7)) {
|
||||
p += 7;
|
||||
action = SCST_LUN_ACTION_REPLACE;
|
||||
} else if (!strncasecmp("clear", p, 5)) {
|
||||
p += 5;
|
||||
action = SCST_LUN_ACTION_CLEAR;
|
||||
} else {
|
||||
PRINT_ERROR("Unknown action \"%s\"", p);
|
||||
res = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (!isspace(*p)) {
|
||||
PRINT_ERROR("%s", "Syntax error");
|
||||
res = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
res = scst_suspend_activity(true);
|
||||
if (res != 0)
|
||||
goto out_free;
|
||||
@@ -1191,53 +1229,61 @@ static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
|
||||
goto out_free_resume;
|
||||
}
|
||||
|
||||
while (isspace(*p) && *p != '\0')
|
||||
p++;
|
||||
e = p; /* save p */
|
||||
host = simple_strtoul(p, &p, 0);
|
||||
if (*p == ':') {
|
||||
channel = simple_strtoul(p + 1, &p, 0);
|
||||
id = simple_strtoul(p + 1, &p, 0);
|
||||
lun = simple_strtoul(p + 1, &p, 0);
|
||||
e = p;
|
||||
} else {
|
||||
virt++;
|
||||
p = e; /* restore p */
|
||||
while (!isspace(*e) && *e != '\0')
|
||||
e++;
|
||||
*e = '\0';
|
||||
}
|
||||
if (action != SCST_LUN_ACTION_CLEAR) {
|
||||
if (!isspace(*p)) {
|
||||
PRINT_ERROR("%s", "Syntax error");
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
|
||||
list_for_each_entry(d, &scst_dev_list, dev_list_entry) {
|
||||
if (virt) {
|
||||
if (d->virt_id && !strcmp(d->virt_name, p)) {
|
||||
dev = d;
|
||||
TRACE_DBG("Virt device %p (%s) found",
|
||||
dev, p);
|
||||
break;
|
||||
}
|
||||
while (isspace(*p) && *p != '\0')
|
||||
p++;
|
||||
e = p; /* save p */
|
||||
host = simple_strtoul(p, &p, 0);
|
||||
if (*p == ':') {
|
||||
channel = simple_strtoul(p + 1, &p, 0);
|
||||
id = simple_strtoul(p + 1, &p, 0);
|
||||
lun = simple_strtoul(p + 1, &p, 0);
|
||||
e = p;
|
||||
} else {
|
||||
if (d->scsi_dev &&
|
||||
d->scsi_dev->host->host_no == host &&
|
||||
d->scsi_dev->channel == channel &&
|
||||
d->scsi_dev->id == id &&
|
||||
d->scsi_dev->lun == lun) {
|
||||
dev = d;
|
||||
TRACE_DBG("Dev %p (%d:%d:%d:%d) found",
|
||||
dev, host, channel, id, lun);
|
||||
break;
|
||||
virt++;
|
||||
p = e; /* restore p */
|
||||
while (!isspace(*e) && *e != '\0')
|
||||
e++;
|
||||
*e = '\0';
|
||||
}
|
||||
|
||||
list_for_each_entry(d, &scst_dev_list, dev_list_entry) {
|
||||
if (virt) {
|
||||
if (d->virt_id && !strcmp(d->virt_name, p)) {
|
||||
dev = d;
|
||||
TRACE_DBG("Virt device %p (%s) found",
|
||||
dev, p);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (d->scsi_dev &&
|
||||
d->scsi_dev->host->host_no == host &&
|
||||
d->scsi_dev->channel == channel &&
|
||||
d->scsi_dev->id == id &&
|
||||
d->scsi_dev->lun == lun) {
|
||||
dev = d;
|
||||
TRACE_DBG("Dev %p (%d:%d:%d:%d) found",
|
||||
dev, host, channel, id, lun);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dev == NULL) {
|
||||
if (virt) {
|
||||
PRINT_ERROR("Virt device %s not found", p);
|
||||
} else {
|
||||
PRINT_ERROR("Device %d:%d:%d:%d not found",
|
||||
host, channel, id, lun);
|
||||
if (dev == NULL) {
|
||||
if (virt) {
|
||||
PRINT_ERROR("Virt device '%s' not found", p);
|
||||
} else {
|
||||
PRINT_ERROR("Device %d:%d:%d:%d not found",
|
||||
host, channel, id, lun);
|
||||
}
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
@@ -1306,7 +1352,7 @@ static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
|
||||
struct scst_tgt_dev *tgt_dev;
|
||||
|
||||
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
|
||||
dev_tgt_dev_list_entry) {
|
||||
dev_tgt_dev_list_entry) {
|
||||
if ((tgt_dev->acg_dev->acg == acg) &&
|
||||
(tgt_dev->lun == virt_lun)) {
|
||||
TRACE_MGMT_DBG("INQUIRY DATA HAS CHANGED"
|
||||
@@ -1324,6 +1370,19 @@ static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
|
||||
if (res != 0)
|
||||
goto out_free_up;
|
||||
break;
|
||||
case SCST_LUN_ACTION_CLEAR:
|
||||
PRINT_INFO("Removed all devices from group %s",
|
||||
acg->acg_name);
|
||||
list_for_each_entry_safe(acg_dev, acg_dev_tmp,
|
||||
&acg->acg_dev_list,
|
||||
acg_dev_list_entry) {
|
||||
res = scst_acg_remove_dev(acg, acg_dev->dev,
|
||||
list_is_last(&acg_dev->acg_dev_list_entry,
|
||||
&acg->acg_dev_list));
|
||||
if (res)
|
||||
goto out_free_up;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
res = count;
|
||||
@@ -1348,6 +1407,624 @@ out_remove_acg_dev:
|
||||
#undef SCST_LUN_ACTION_ADD
|
||||
#undef SCST_LUN_ACTION_DEL
|
||||
#undef SCST_LUN_ACTION_REPLACE
|
||||
#undef SCST_LUN_ACTION_CLEAR
|
||||
}
|
||||
|
||||
static ssize_t scst_luns_mgmt_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
static char *help = "Usage: echo \"add|del H:C:I:L lun [READ_ONLY]\" "
|
||||
">mgmt\n"
|
||||
" echo \"add|del VNAME lun [READ_ONLY]\" "
|
||||
">mgmt\n"
|
||||
" echo \"replace H:C:I:L lun [READ_ONLY]\" "
|
||||
">mgmt\n"
|
||||
" echo \"replace VNAME lun [READ_ONLY]\" "
|
||||
">mgmt\n"
|
||||
" echo \"clear\" "
|
||||
">mgmt\n";
|
||||
|
||||
return sprintf(buf, help);
|
||||
}
|
||||
|
||||
static ssize_t scst_luns_mgmt_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int res;
|
||||
struct scst_acg *acg;
|
||||
struct scst_tgt *tgt;
|
||||
|
||||
tgt = container_of(kobj->parent, struct scst_tgt, tgt_kobj);
|
||||
acg = tgt->default_acg;
|
||||
res = __scst_luns_mgmt_store(acg, kobj, buf, count);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int scst_create_acg_sysfs(struct scst_tgt *tgt,
|
||||
struct scst_acg *acg)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
acg->acg_kobj_initialized = 1;
|
||||
|
||||
retval = kobject_init_and_add(&acg->acg_kobj, &acg_ktype,
|
||||
tgt->tgt_ini_grp_kobj, acg->acg_name);
|
||||
if (retval != 0) {
|
||||
PRINT_ERROR("Can't add acg '%s' to sysfs", acg->acg_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
acg->luns_kobj = kobject_create_and_add("luns", &acg->acg_kobj);
|
||||
if (acg->luns_kobj == NULL) {
|
||||
PRINT_ERROR("Can't create luns kobj for tgt %s",
|
||||
tgt->tgt_name);
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = sysfs_create_file(acg->luns_kobj, &scst_acg_luns_mgmt.attr);
|
||||
if (retval != 0) {
|
||||
PRINT_ERROR("Can't add tgt attr %s for tgt %s",
|
||||
scst_acg_luns_mgmt.attr.name, tgt->tgt_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
acg->initiators_kobj = kobject_create_and_add("initiators",
|
||||
&acg->acg_kobj);
|
||||
if (acg->initiators_kobj == NULL) {
|
||||
PRINT_ERROR("Can't create initiators kobj for tgt %s",
|
||||
tgt->tgt_name);
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = sysfs_create_file(acg->initiators_kobj,
|
||||
&scst_acg_ini_mgmt.attr);
|
||||
if (retval != 0) {
|
||||
PRINT_ERROR("Can't add tgt attr %s for tgt %s",
|
||||
scst_acg_ini_mgmt.attr.name, tgt->tgt_name);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
TRACE_EXIT_RES(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
void scst_acg_sysfs_put(struct scst_acg *acg)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (acg->acg_kobj_initialized) {
|
||||
scst_clear_acg(acg);
|
||||
|
||||
kobject_del(acg->luns_kobj);
|
||||
kobject_put(acg->luns_kobj);
|
||||
|
||||
kobject_del(acg->initiators_kobj);
|
||||
kobject_put(acg->initiators_kobj);
|
||||
|
||||
kobject_del(&acg->acg_kobj);
|
||||
kobject_put(&acg->acg_kobj);
|
||||
} else
|
||||
scst_destroy_acg(acg);
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static ssize_t scst_ini_group_mgmt_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
static char *help = "Usage: echo \"create GROUP_NAME\" "
|
||||
">mgmt\n"
|
||||
" echo \"del GROUP_NAME\" "
|
||||
">mgmt\n";
|
||||
|
||||
return sprintf(buf, help);
|
||||
}
|
||||
|
||||
static ssize_t scst_ini_group_mgmt_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int res, action;
|
||||
int len;
|
||||
char *name;
|
||||
char *buffer, *p, *e = NULL;
|
||||
struct scst_acg *a, *acg = NULL;
|
||||
struct scst_tgt *tgt;
|
||||
|
||||
#define SCST_INI_GROUP_ACTION_CREATE 1
|
||||
#define SCST_INI_GROUP_ACTION_DEL 2
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
tgt = container_of(kobj->parent, struct scst_tgt, tgt_kobj);
|
||||
|
||||
buffer = kzalloc(count+1, GFP_KERNEL);
|
||||
if (buffer == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(buffer, buf, count);
|
||||
buffer[count] = '\0';
|
||||
p = buffer;
|
||||
|
||||
p = buffer;
|
||||
if (p[strlen(p) - 1] == '\n')
|
||||
p[strlen(p) - 1] = '\0';
|
||||
if (strncasecmp("create ", p, 7) == 0) {
|
||||
p += 7;
|
||||
action = SCST_INI_GROUP_ACTION_CREATE;
|
||||
} else if (strncasecmp("del ", p, 4) == 0) {
|
||||
p += 4;
|
||||
action = SCST_INI_GROUP_ACTION_DEL;
|
||||
} else {
|
||||
PRINT_ERROR("Unknown action \"%s\"", p);
|
||||
res = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
res = scst_suspend_activity(true);
|
||||
if (res != 0)
|
||||
goto out_free;
|
||||
|
||||
if (mutex_lock_interruptible(&scst_mutex) != 0) {
|
||||
res = -EINTR;
|
||||
goto out_free_resume;
|
||||
}
|
||||
|
||||
while (isspace(*p) && *p != '\0')
|
||||
p++;
|
||||
e = p;
|
||||
while (!isspace(*e) && *e != '\0')
|
||||
e++;
|
||||
*e = '\0';
|
||||
|
||||
if (p[0] == '\0') {
|
||||
PRINT_ERROR("%s", "Group name required");
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
|
||||
list_for_each_entry(a, &tgt->acg_list, acg_list_entry) {
|
||||
if (strcmp(a->acg_name, p) == 0) {
|
||||
TRACE_DBG("group (acg) %p %s found",
|
||||
a, a->acg_name);
|
||||
acg = a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case SCST_INI_GROUP_ACTION_CREATE:
|
||||
TRACE_DBG("Creating group '%s'", p);
|
||||
if (acg != NULL) {
|
||||
PRINT_ERROR("acg name %s exist", p);
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
|
||||
len = strlen(p) + 1;
|
||||
name = kmalloc(len, GFP_KERNEL);
|
||||
if (name == NULL) {
|
||||
PRINT_ERROR("%s", "Allocation of name failed");
|
||||
res = -ENOMEM;
|
||||
goto out_free_up;
|
||||
}
|
||||
strncpy(name, p, len);
|
||||
|
||||
acg = scst_alloc_add_acg(tgt, name);
|
||||
kfree(name);
|
||||
if (acg == NULL)
|
||||
goto out_free_up;
|
||||
|
||||
res = scst_create_acg_sysfs(tgt, acg);
|
||||
if (res != 0)
|
||||
goto out_free_acg;
|
||||
break;
|
||||
case SCST_INI_GROUP_ACTION_DEL:
|
||||
TRACE_DBG("Deleting group '%s'", p);
|
||||
if (acg == NULL) {
|
||||
PRINT_ERROR("Group %s not found", p);
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
if (!scst_acg_sess_is_empty(acg)) {
|
||||
PRINT_ERROR("Group %s is not empty", acg->acg_name);
|
||||
res = -EBUSY;
|
||||
goto out_free_up;
|
||||
}
|
||||
scst_acg_sysfs_put(acg);
|
||||
break;
|
||||
}
|
||||
|
||||
res = count;
|
||||
|
||||
out_free_up:
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
out_free_resume:
|
||||
scst_resume_activity();
|
||||
|
||||
out_free:
|
||||
kfree(buffer);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
out_free_acg:
|
||||
scst_acg_sysfs_put(acg);
|
||||
goto out_free_up;
|
||||
|
||||
#undef SCST_LUN_ACTION_CREATE
|
||||
#undef SCST_LUN_ACTION_DEL
|
||||
}
|
||||
|
||||
int scst_create_acn_sysfs(struct scst_acg *acg, struct scst_acn *acn)
|
||||
{
|
||||
int retval = 0;
|
||||
int len;
|
||||
struct kobj_attribute *attr = NULL;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
acn->acn_attr = NULL;
|
||||
|
||||
attr = kzalloc(sizeof(struct kobj_attribute), GFP_KERNEL);
|
||||
if (attr == NULL) {
|
||||
PRINT_ERROR("Unable to allocate attributes for initiator '%s'",
|
||||
acn->name);
|
||||
retval = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = strlen(acn->name) + 1;
|
||||
attr->attr.name = kzalloc(len, GFP_KERNEL);
|
||||
if (attr->attr.name == NULL) {
|
||||
PRINT_ERROR("Unable to allocate attributes for initiator '%s'",
|
||||
acn->name);
|
||||
retval = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
strncpy((char *)attr->attr.name, acn->name, len);
|
||||
|
||||
attr->attr.owner = THIS_MODULE;
|
||||
attr->attr.mode = S_IRUGO;
|
||||
attr->show = scst_acn_file_show;
|
||||
attr->store = NULL;
|
||||
|
||||
retval = sysfs_create_file(acg->initiators_kobj, &attr->attr);
|
||||
if (retval != 0) {
|
||||
PRINT_ERROR("Unable to allocate initiator '%s' for group '%s'",
|
||||
acn->name, acg->acg_name);
|
||||
kfree(attr->attr.name);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
acn->acn_attr = attr;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(retval);
|
||||
return retval;
|
||||
|
||||
out_free:
|
||||
kfree(attr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
void scst_acn_sysfs_del(struct scst_acg *acg, struct scst_acn *acn,
|
||||
bool reassign)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (acn->acn_attr != NULL) {
|
||||
sysfs_remove_file(acg->initiators_kobj,
|
||||
&acn->acn_attr->attr);
|
||||
kfree(acn->acn_attr->attr.name);
|
||||
kfree(acn->acn_attr);
|
||||
}
|
||||
scst_acg_remove_acn(acn);
|
||||
if (reassign)
|
||||
scst_check_reassign_sessions();
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static ssize_t scst_acn_file_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, "%s\n",
|
||||
attr->attr.name);
|
||||
}
|
||||
|
||||
static ssize_t scst_acg_luns_mgmt_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
static char *help = "Usage: echo \"add|del H:C:I:L lun [READ_ONLY]\" "
|
||||
">mgmt\n"
|
||||
" echo \"add|del VNAME lun [READ_ONLY]\" "
|
||||
">mgmt\n"
|
||||
" echo \"replace H:C:I:L lun [READ_ONLY]\" "
|
||||
">mgmt\n"
|
||||
" echo \"replace VNAME lun [READ_ONLY]\" "
|
||||
">mgmt\n"
|
||||
" echo \"clear\" "
|
||||
">mgmt\n";
|
||||
|
||||
return sprintf(buf, help);
|
||||
}
|
||||
|
||||
static ssize_t scst_acg_luns_mgmt_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int res;
|
||||
struct scst_acg *acg;
|
||||
|
||||
acg = container_of(kobj->parent, struct scst_acg, acg_kobj);
|
||||
res = __scst_luns_mgmt_store(acg, kobj, buf, count);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t scst_acg_ini_mgmt_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
static char *help = "Usage: echo \"add INITIATOR_NAME\" "
|
||||
">mgmt\n"
|
||||
" echo \"del INITIATOR_NAME\" "
|
||||
">mgmt\n"
|
||||
" echo \"move INITIATOR_NAME DEST_GROUP_NAME\" "
|
||||
">mgmt\n"
|
||||
" echo \"clear\" "
|
||||
">mgmt\n";
|
||||
|
||||
return sprintf(buf, help);
|
||||
}
|
||||
|
||||
static ssize_t scst_acg_ini_mgmt_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int res, action;
|
||||
char *buffer, *p, *e = NULL;
|
||||
char *name = NULL, *group = NULL;
|
||||
struct scst_acg *acg = NULL, *acg_dest = NULL;
|
||||
struct scst_tgt *tgt = NULL;
|
||||
struct scst_acn *acn = NULL, *acn_tmp;
|
||||
|
||||
#define SCST_ACG_ACTION_INI_ADD 1
|
||||
#define SCST_ACG_ACTION_INI_DEL 2
|
||||
#define SCST_ACG_ACTION_INI_CLEAR 3
|
||||
#define SCST_ACG_ACTION_INI_MOVE 4
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
acg = container_of(kobj->parent, struct scst_acg, acg_kobj);
|
||||
|
||||
buffer = kzalloc(count+1, GFP_KERNEL);
|
||||
if (buffer == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(buffer, buf, count);
|
||||
buffer[count] = '\0';
|
||||
p = buffer;
|
||||
|
||||
p = buffer;
|
||||
if (p[strlen(p) - 1] == '\n')
|
||||
p[strlen(p) - 1] = '\0';
|
||||
|
||||
if (strncasecmp("add", p, 3) == 0) {
|
||||
p += 3;
|
||||
action = SCST_ACG_ACTION_INI_ADD;
|
||||
} else if (strncasecmp("del", p, 3) == 0) {
|
||||
p += 3;
|
||||
action = SCST_ACG_ACTION_INI_DEL;
|
||||
} else if (strncasecmp("clear", p, 5) == 0) {
|
||||
p += 5;
|
||||
action = SCST_ACG_ACTION_INI_CLEAR;
|
||||
} else if (strncasecmp("move", p, 4) == 0) {
|
||||
p += 4;
|
||||
action = SCST_ACG_ACTION_INI_MOVE;
|
||||
} else {
|
||||
PRINT_ERROR("Unknown action \"%s\"", p);
|
||||
res = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (action != SCST_ACG_ACTION_INI_CLEAR)
|
||||
if (!isspace(*p)) {
|
||||
PRINT_ERROR("%s", "Syntax error");
|
||||
res = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
res = scst_suspend_activity(true);
|
||||
if (res != 0)
|
||||
goto out_free;
|
||||
|
||||
if (mutex_lock_interruptible(&scst_mutex) != 0) {
|
||||
res = -EINTR;
|
||||
goto out_free_resume;
|
||||
}
|
||||
|
||||
if (action != SCST_ACG_ACTION_INI_CLEAR)
|
||||
while (isspace(*p) && *p != '\0')
|
||||
p++;
|
||||
|
||||
switch (action) {
|
||||
case SCST_ACG_ACTION_INI_ADD:
|
||||
e = p;
|
||||
while (!isspace(*e) && *e != '\0')
|
||||
e++;
|
||||
*e = '\0';
|
||||
name = p;
|
||||
|
||||
if (name[0] == '\0') {
|
||||
PRINT_ERROR("%s", "Invalid initiator name");
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
|
||||
res = scst_acg_add_name(acg, name);
|
||||
if (res != 0)
|
||||
goto out_free_up;
|
||||
break;
|
||||
case SCST_ACG_ACTION_INI_DEL:
|
||||
e = p;
|
||||
while (!isspace(*e) && *e != '\0')
|
||||
e++;
|
||||
*e = '\0';
|
||||
name = p;
|
||||
|
||||
if (name[0] == '\0') {
|
||||
PRINT_ERROR("%s", "Invalid initiator name");
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
|
||||
acn = scst_acg_find_name(acg, name);
|
||||
if (acn == NULL) {
|
||||
PRINT_ERROR("Unable to find "
|
||||
"initiator '%s' in group '%s'",
|
||||
name, acg->acg_name);
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
scst_acn_sysfs_del(acg, acn, true);
|
||||
break;
|
||||
case SCST_ACG_ACTION_INI_CLEAR:
|
||||
list_for_each_entry_safe(acn, acn_tmp, &acg->acn_list,
|
||||
acn_list_entry) {
|
||||
scst_acn_sysfs_del(acg, acn, false);
|
||||
}
|
||||
scst_check_reassign_sessions();
|
||||
break;
|
||||
case SCST_ACG_ACTION_INI_MOVE:
|
||||
e = p;
|
||||
while (!isspace(*e) && *e != '\0')
|
||||
e++;
|
||||
if (*e == '\0') {
|
||||
PRINT_ERROR("%s", "Too few parameters");
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
*e = '\0';
|
||||
name = p;
|
||||
|
||||
if (name[0] == '\0') {
|
||||
PRINT_ERROR("%s", "Invalid initiator name");
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
|
||||
e++;
|
||||
p = e;
|
||||
while (!isspace(*e) && *e != '\0')
|
||||
e++;
|
||||
*e = '\0';
|
||||
group = p;
|
||||
|
||||
if (group[0] == '\0') {
|
||||
PRINT_ERROR("%s", "Invalid group name");
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
|
||||
TRACE_DBG("Move initiator '%s' to group '%s'",
|
||||
name, group);
|
||||
|
||||
/*
|
||||
* Better get tgt from hierarchy tgt_kobj -> tgt_ini_grp_kobj ->
|
||||
* acg_kobj -> initiators_kobj than have direct pointer to tgt
|
||||
* in struct acg and have a headache to care about its possible
|
||||
* wrong dereference on the destruction time.
|
||||
*/
|
||||
{
|
||||
struct kobject *k;
|
||||
|
||||
/* acg_kobj */
|
||||
k = kobj->parent;
|
||||
if (k == NULL) {
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
/* tgt_ini_grp_kobj */
|
||||
k = k->parent;
|
||||
if (k == NULL) {
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
/* tgt_kobj */
|
||||
k = k->parent;
|
||||
if (k == NULL) {
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
|
||||
tgt = container_of(k, struct scst_tgt, tgt_kobj);
|
||||
}
|
||||
|
||||
acn = scst_acg_find_name(acg, name);
|
||||
if (acn == NULL) {
|
||||
PRINT_ERROR("Unable to find "
|
||||
"initiator '%s' in group '%s'",
|
||||
name, acg->acg_name);
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
acg_dest = scst_tgt_find_acg(tgt, group);
|
||||
if (acg_dest == NULL) {
|
||||
PRINT_ERROR("Unable to find group '%s' in target '%s'",
|
||||
group, tgt->tgt_name);
|
||||
res = -EINVAL;
|
||||
goto out_free_up;
|
||||
}
|
||||
if (scst_acg_find_name(acg_dest, name) != NULL) {
|
||||
PRINT_ERROR("Initiator '%s' already exists in group '%s'",
|
||||
name, acg_dest->acg_name);
|
||||
res = -EEXIST;
|
||||
goto out_free_up;
|
||||
}
|
||||
scst_acn_sysfs_del(acg, acn, false);
|
||||
|
||||
res = scst_acg_add_name(acg_dest, name);
|
||||
if (res != 0)
|
||||
goto out_free_up;
|
||||
break;
|
||||
}
|
||||
|
||||
res = count;
|
||||
|
||||
out_free_up:
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
out_free_resume:
|
||||
scst_resume_activity();
|
||||
|
||||
out_free:
|
||||
kfree(buffer);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
#undef SCST_ACG_ACTION_INI_ADD
|
||||
#undef SCST_ACG_ACTION_INI_DEL
|
||||
#undef SCST_ACG_ACTION_INI_CLEAR
|
||||
#undef SCST_ACG_ACTION_INI_MOVE
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1660,7 +2337,7 @@ static int scst_write_trace(const char *buf, size_t length,
|
||||
p++;
|
||||
res = strict_strtoul(p, 0, &level);
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("Invalud trace value \"%s\"", p);
|
||||
PRINT_ERROR("Invalid trace value \"%s\"", p);
|
||||
res = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
+40
-11
@@ -5332,6 +5332,8 @@ static bool wildcmp(const char *wild, const char *string)
|
||||
return !*wild;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
|
||||
/* scst_mutex supposed to be held */
|
||||
static struct scst_acg *scst_find_acg_by_name_wild(const char *initiator_name)
|
||||
{
|
||||
@@ -5340,7 +5342,7 @@ static struct scst_acg *scst_find_acg_by_name_wild(const char *initiator_name)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
|
||||
list_for_each_entry(acg, &scst_acg_list, acg_list_entry) {
|
||||
list_for_each_entry(n, &acg->acn_list, acn_list_entry) {
|
||||
if (wildcmp(n->name, initiator_name)) {
|
||||
TRACE_DBG("Access control group %s found",
|
||||
@@ -5356,7 +5358,6 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
/* scst_mutex supposed to be held */
|
||||
static struct scst_acg *scst_find_acg_by_name(const char *acg_name)
|
||||
{
|
||||
@@ -5364,7 +5365,7 @@ static struct scst_acg *scst_find_acg_by_name(const char *acg_name)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
list_for_each_entry(acg, &scst_acg_list, scst_acg_list_entry) {
|
||||
list_for_each_entry(acg, &scst_acg_list, acg_list_entry) {
|
||||
if (strcmp(acg->acg_name, acg_name) == 0) {
|
||||
TRACE_DBG("Access control group %s found",
|
||||
acg->acg_name);
|
||||
@@ -5377,27 +5378,55 @@ out:
|
||||
TRACE_EXIT_HRES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
#else /* CONFIG_SCST_PROC */
|
||||
|
||||
/* scst_mutex supposed to be held */
|
||||
static struct scst_acg *scst_find_tgt_acg_by_name_wild(struct scst_tgt *tgt,
|
||||
const char *initiator_name)
|
||||
{
|
||||
struct scst_acg *acg, *res = NULL;
|
||||
struct scst_acn *n;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (initiator_name == NULL)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(acg, &tgt->acg_list, acg_list_entry) {
|
||||
list_for_each_entry(n, &acg->acn_list, acn_list_entry) {
|
||||
if (wildcmp(n->name, initiator_name)) {
|
||||
TRACE_DBG("Access control group %s found",
|
||||
acg->acg_name);
|
||||
res = acg;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_HRES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SCST_PROC */
|
||||
|
||||
/* Must be called under scst_mitex */
|
||||
/* Must be called under scst_mutex */
|
||||
struct scst_acg *scst_find_acg(const struct scst_session *sess)
|
||||
{
|
||||
struct scst_acg *acg = NULL;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
if (sess->initiator_name)
|
||||
acg = scst_find_acg_by_name_wild(sess->initiator_name);
|
||||
#ifdef CONFIG_SCST_PROC
|
||||
if ((acg == NULL) && (sess->tgt->default_group_name != NULL))
|
||||
acg = scst_find_acg_by_name(sess->tgt->default_group_name);
|
||||
if (acg == NULL) {
|
||||
if (list_empty(&sess->tgt->default_acg->acg_dev_list))
|
||||
acg = scst_default_acg;
|
||||
else
|
||||
acg = sess->tgt->default_acg;
|
||||
}
|
||||
if (acg == NULL)
|
||||
acg = scst_default_acg;
|
||||
#else
|
||||
acg = scst_find_tgt_acg_by_name_wild(sess->tgt, sess->initiator_name);
|
||||
if (acg == NULL)
|
||||
acg = sess->tgt->default_acg;
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user