Patch from Alexey Obitotskiy <alexeyo1@open-e.com> with 2 fixes and cleanups implemen...
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Fri, 8 Jan 2010 16:11:13 +0000 (16:11 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Fri, 8 Jan 2010 16:11:13 +0000 (16:11 +0000)
git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@1437 d57e44dd-8a1f-0410-8b47-8ef2f437770f

scst/include/scst.h
scst/src/scst_lib.c
scst/src/scst_main.c
scst/src/scst_priv.h
scst/src/scst_proc.c
scst/src/scst_sysfs.c
scst/src/scst_targ.c

index 66c2d4b..7ad9d04 100644 (file)
@@ -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;
 };
 
 /*
index df978af..98b3e20 100644 (file)
@@ -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("Removing acg %s from scst_acg_list", acg->acg_name);
-       list_del(&acg->scst_acg_list_entry);
+       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;
+       }
+#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)
index f647b50..ea0746e 100644 (file)
@@ -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;
index 0c3ff6a..77c9015 100644 (file)
@@ -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 */
 
index e3c2b2b..ab060d5 100644 (file)
@@ -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;
index e3b3779..e88eb76 100644 (file)
@@ -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;
                }
index 00f2092..f4b30bc 100644 (file)
@@ -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