From 82b04b2a00da7e15089e360653a0545b23b71d83 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 21 Oct 2009 16:49:52 +0000 Subject: [PATCH] Merge of the sysfs branch. To enable the sysfs interface you should run "make disable_proc". To switch back on the procfs interface you should run "make disable_proc". The sysfs intarface is mostly finished. Only initiators-oriented access control not implemented and not all target drivers are updated. Only qla2x00t has been fully updated. git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1229 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- Makefile | 78 +- iscsi-full_perf.patch | 16 - iscsi-release.patch | 17 - iscsi-scst/Makefile | 43 +- iscsi-scst/kernel/config.c | 14 +- iscsi-scst/kernel/conn.c | 4 + iscsi-scst/kernel/iscsi.c | 8 + iscsi-scst/kernel/iscsi.h | 6 +- iscsi-scst/kernel/session.c | 4 + qla2x00t-full_perf.patch | 16 - qla2x00t-release.patch | 17 - qla2x00t/qla2x00-target/Makefile | 44 +- qla2x00t/qla2x00-target/qla2x00t.c | 508 ++++-- qla2x00t/qla2x00-target/qla2x00t.h | 4 +- qla2x00t/qla2x_tgt.h | 2 + qla2x00t/qla2x_tgt_def.h | 10 +- qla2x00t/qla_attr.c | 10 +- qla2x00t/qla_def.h | 12 +- qla2x00t/qla_isr.c | 4 - qla2x00t/qla_os.c | 69 +- scst-full_perf.patch | 39 - scst-release.patch | 43 - scst/Makefile | 30 +- scst/README | 45 +- scst/README_in-tree | 23 +- scst/include/scst.h | 257 ++- scst/include/scst_const.h | 10 + scst/src/Makefile | 60 +- scst/src/dev_handlers/Makefile | 34 +- scst/src/dev_handlers/scst_cdrom.c | 36 +- scst/src/dev_handlers/scst_changer.c | 37 +- scst/src/dev_handlers/scst_dev_handler.h | 27 +- scst/src/dev_handlers/scst_disk.c | 89 +- scst/src/dev_handlers/scst_modisk.c | 89 +- scst/src/dev_handlers/scst_processor.c | 41 +- scst/src/dev_handlers/scst_raid.c | 40 +- scst/src/dev_handlers/scst_tape.c | 93 +- scst/src/dev_handlers/scst_user.c | 57 +- scst/src/dev_handlers/scst_vdisk.c | 2048 ++++++++++++++++------ scst/src/scst_lib.c | 156 +- scst/src/scst_main.c | 338 +++- scst/src/scst_mem.c | 156 +- scst/src/scst_mem.h | 28 +- scst/src/scst_priv.h | 110 +- scst/src/scst_proc.c | 60 +- scst/src/scst_sysfs.c | 1747 ++++++++++++++++++ scst/src/scst_targ.c | 25 +- usr-full_perf.patch | 16 - usr-release.patch | 17 - usr/fileio/Makefile | 37 +- 50 files changed, 5314 insertions(+), 1360 deletions(-) delete mode 100644 iscsi-full_perf.patch delete mode 100644 iscsi-release.patch delete mode 100644 qla2x00t-full_perf.patch delete mode 100644 qla2x00t-release.patch delete mode 100644 scst-full_perf.patch delete mode 100644 scst-release.patch create mode 100644 scst/src/scst_sysfs.c delete mode 100644 usr-full_perf.patch delete mode 100644 usr-release.patch diff --git a/Makefile b/Makefile index 1464aa28c..95133383d 100644 --- a/Makefile +++ b/Makefile @@ -301,38 +301,63 @@ mvsas_clean: mvsas_extraclean: cd $(MVSAS_DIR) && $(MAKE) extraclean - debug2perf: - echo "Changing current debug state from full debug to full performance" - patch -p0 /dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ $(KMOD)/Makefile + @cmp $(KMOD)/Makefile $(KMOD)/Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ $(KMOD)/Makefile + @cmp $(KMOD)/Makefile $(KMOD)/Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + rm $(KMOD)/Makefile.aa + +release2debug: + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(KMOD)/Makefile + @cmp $(KMOD)/Makefile $(KMOD)/Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ $(KMOD)/Makefile + @cmp $(KMOD)/Makefile $(KMOD)/Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ $(KMOD)/Makefile + @cmp $(KMOD)/Makefile $(KMOD)/Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + rm $(KMOD)/Makefile.aa + +debug2perf: + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(KMOD)/Makefile + @cmp $(KMOD)/Makefile $(KMOD)/Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ $(KMOD)/Makefile + @cmp $(KMOD)/Makefile $(KMOD)/Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + rm $(KMOD)/Makefile.aa + +perf2debug: + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(KMOD)/Makefile + @cmp $(KMOD)/Makefile $(KMOD)/Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ $(KMOD)/Makefile + @cmp $(KMOD)/Makefile $(KMOD)/Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm $(KMOD)/Makefile.aa; echo "sed failed"; false; fi + rm $(KMOD)/Makefile.aa + +disable_proc: + +enable_proc: + +.PHONY: all mods progs install clean extraclean debug2release release2debug debug2perf perf2debug disable_proc enable_proc diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index 0bae263af..e2795973f 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -13,10 +13,12 @@ * GNU General Public License for more details. */ -#include - #include "iscsi.h" +#ifdef CONFIG_SCST_PROC + +#include + #define ISCSI_PROC_VERSION_NAME "version" #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) @@ -25,7 +27,7 @@ #include -static struct scst_proc_log iscsi_proc_local_trace_tbl[] = +static struct scst_trace_log iscsi_local_trace_tbl[] = { { TRACE_D_READ, "d_read" }, { TRACE_D_WRITE, "d_write" }, @@ -44,7 +46,7 @@ static int iscsi_log_info_show(struct seq_file *seq, void *v) TRACE_ENTRY(); res = scst_proc_log_entry_read(seq, trace_flag, - iscsi_proc_local_trace_tbl); + iscsi_local_trace_tbl); TRACE_EXIT_RES(res); return res; @@ -58,7 +60,7 @@ static ssize_t iscsi_proc_log_entry_write(struct file *file, TRACE_ENTRY(); res = scst_proc_log_entry_write(file, buf, length, &trace_flag, - ISCSI_DEFAULT_LOG_FLAGS, iscsi_proc_local_trace_tbl); + ISCSI_DEFAULT_LOG_FLAGS, iscsi_local_trace_tbl); TRACE_EXIT_RES(res); return res; @@ -231,6 +233,8 @@ err: goto out; } +#endif /* CONFIG_SCST_PROC */ + /* target_mutex supposed to be locked */ static int add_conn(struct iscsi_target *target, void __user *ptr) { diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index 21ec29c7a..583f3eb5c 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -21,6 +21,8 @@ #include "iscsi.h" #include "digest.h" +#ifdef CONFIG_SCST_PROC + static void print_conn_state(char *p, size_t size, struct iscsi_conn *conn) { int printed = 0; @@ -111,6 +113,8 @@ void conn_info_show(struct seq_file *seq, struct iscsi_session *session) } } +#endif /* CONFIG_SCST_PROC */ + /* target_mutex supposed to be locked */ struct iscsi_conn *conn_lookup(struct iscsi_session *session, u16 cid) { diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index ff6b17c54..2cca0983f 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -3256,9 +3256,11 @@ static int __init iscsi_init(void) iscsi_template_registered = 1; +#ifdef CONFIG_SCST_PROC err = iscsi_procfs_init(); if (err < 0) goto out_reg_tmpl; +#endif num = max((int)num_online_cpus(), 2); @@ -3274,10 +3276,14 @@ out: return err; out_thr: +#ifdef CONFIG_SCST_PROC iscsi_procfs_exit(); +#endif iscsi_stop_threads(); +#ifdef CONFIG_SCST_PROC out_reg_tmpl: +#endif scst_unregister_target_template(&iscsi_template); out_kmem: @@ -3305,7 +3311,9 @@ static void __exit iscsi_exit(void) unregister_chrdev(ctr_major, ctr_name); +#ifdef CONFIG_SCST_PROC iscsi_procfs_exit(); +#endif event_exit(); kmem_cache_destroy(iscsi_cmnd_cache); diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 131cc8d23..eac117891 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -18,7 +18,6 @@ #define __ISCSI_H__ #include -#include #include #include #include @@ -439,7 +438,10 @@ extern void __mark_conn_closed(struct iscsi_conn *, int); extern void mark_conn_closed(struct iscsi_conn *); extern void iscsi_make_conn_wr_active(struct iscsi_conn *); + +#ifdef CONFIG_SCST_PROC extern void conn_info_show(struct seq_file *, struct iscsi_session *); +#endif /* nthread.c */ extern int iscsi_send(struct iscsi_conn *conn); @@ -463,8 +465,10 @@ extern void target_del_all(void); extern const struct seq_operations iscsi_seq_op; /* config.c */ +#ifdef CONFIG_SCST_PROC extern int iscsi_procfs_init(void); extern void iscsi_procfs_exit(void); +#endif /* session.c */ extern const struct file_operations session_seq_fops; diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index 35bcc2171..42ee3788b 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -311,6 +311,8 @@ int session_del(struct iscsi_target *target, u64 sid) return session_free(session, true); } +#ifdef CONFIG_SCST_PROC + /* target_mutex supposed to be locked */ static void iscsi_session_info_show(struct seq_file *seq, struct iscsi_target *target) @@ -345,3 +347,5 @@ const struct file_operations session_seq_fops = { .llseek = seq_lseek, .release = seq_release, }; + +#endif /* CONFIG_SCST_PROC */ diff --git a/qla2x00t-full_perf.patch b/qla2x00t-full_perf.patch deleted file mode 100644 index befe6fc3b..000000000 --- a/qla2x00t-full_perf.patch +++ /dev/null @@ -1,16 +0,0 @@ -Index: qla2x00t/qla2x00-target/Makefile -=================================================================== ---- qla2x00t/qla2x00-target/Makefile (revision 545) -+++ qla2x00t/qla2x00-target/Makefile (working copy) -@@ -37,9 +37,9 @@ INSTALL_DIR := /lib/modules/$(shell unam - - EXTRA_CFLAGS += -W -Wno-unused-parameter -Wno-missing-field-initializers - --EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS -+#EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS - #EXTRA_CFLAGS += -DCONFIG_SCST_TRACING --EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -+#EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g - #EXTRA_CFLAGS += -DCONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD - - ifeq ($(KVER),) diff --git a/qla2x00t-release.patch b/qla2x00t-release.patch deleted file mode 100644 index 117fa1d8d..000000000 --- a/qla2x00t-release.patch +++ /dev/null @@ -1,17 +0,0 @@ -Index: qla2x00t/qla2x00-target/Makefile -=================================================================== ---- qla2x00t/qla2x00-target/Makefile (revision 545) -+++ qla2x00t/qla2x00-target/Makefile (working copy) -@@ -37,9 +37,9 @@ INSTALL_DIR := /lib/modules/$(shell unam - - EXTRA_CFLAGS += -W -Wno-unused-parameter -Wno-missing-field-initializers - --EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS --#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING --EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -+#EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS -+EXTRA_CFLAGS += -DCONFIG_SCST_TRACING -+#EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g - #EXTRA_CFLAGS += -DCONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD - - ifeq ($(KVER),) diff --git a/qla2x00t/qla2x00-target/Makefile b/qla2x00t/qla2x00-target/Makefile index b5c351d21..87fe80443 100644 --- a/qla2x00t/qla2x00-target/Makefile +++ b/qla2x00t/qla2x00-target/Makefile @@ -103,4 +103,46 @@ clean: extraclean: clean -.PHONY: all tgt install uninstall clean extraclean +debug2release: + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +release2debug: + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +debug2perf: + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +perf2debug: + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +disable_proc: + sed -c -i.aa s/"^#define CONFIG_SCST_PROC"/"\/* #define CONFIG_SCST_PROC *\/"/ ../qla_attr.c + @cmp ../qla_attr.c ../qla_attr.c.aa >/dev/null; if [[ $$? = 0 ]]; then rm ../qla_attr.c.aa; echo "sed failed"; false; fi + rm ../qla_attr.c.aa + +enable_proc: + sed -c -i.aa s/"\/\* #define CONFIG_SCST_PROC \*\/"/"#define CONFIG_SCST_PROC"/ ../qla_attr.c + @cmp ../qla_attr.c ../qla_attr.c.aa >/dev/null; if [[ $$? = 0 ]]; then rm ../qla_attr.c.aa; echo "sed failed"; false; fi + rm ../qla_attr.c.aa + +.PHONY: all tgt install uninstall clean extraclean debug2perf debug2release perf2debug release2debug disable_proc enable_proc diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 913d22e17..387b5bd24 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include @@ -46,7 +45,7 @@ * including rings related fields, * * - Or access to target mode variables from struct q2t_tgt doesn't - * cross those functions boundaries, except tgt_shutdown, which + * cross those functions boundaries, except tgt_stop, which * additionally protected by irq_cmd_count. */ @@ -65,7 +64,7 @@ # endif #endif -static int q2x_target_detect(struct scst_tgt_template *templ); +static int q2t_target_detect(struct scst_tgt_template *templ); static int q2t_target_release(struct scst_tgt *scst_tgt); static int q2x_xmit_response(struct scst_cmd *scst_cmd); static int __q24_xmit_response(struct q2t_cmd *cmd, int xmit_type); @@ -96,10 +95,46 @@ static void q2t_clear_tgt_db(struct q2t_tgt *tgt, bool local_only); static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd); static int q2t_unreg_sess(struct q2t_sess *sess); +/** SYSFS **/ + +static ssize_t q2t_version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); + +struct kobj_attribute q2t_version_attr = + __ATTR(version, S_IRUGO, q2t_version_show, NULL); + +static const struct attribute *q2t_attrs[] = { + &q2t_version_attr.attr, + NULL, +}; + +static ssize_t q2t_show_expl_conf_enabled(struct kobject *kobj, + struct kobj_attribute *attr, char *buffer); +static ssize_t q2t_store_expl_conf_enabled(struct kobject *kobj, + struct kobj_attribute *attr, const char *buffer, size_t size); + +struct kobj_attribute q2t_expl_conf_attr = + __ATTR(explicit_confirmation, S_IRUGO|S_IWUSR, + q2t_show_expl_conf_enabled, q2t_store_expl_conf_enabled); + +static const struct attribute *q2t_tgt_attrs[] = { + &q2t_expl_conf_attr.attr, + NULL, +}; + +static ssize_t q2t_enable_tgt(struct scst_tgt *tgt, const char *buf, + size_t size); +static bool q2t_is_tgt_enabled(struct scst_tgt *tgt); + /* * Global Variables */ +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +#define trace_flag q2t_trace_flag +static unsigned long q2t_trace_flag = Q2T_DEFAULT_LOG_FLAGS; +#endif + static struct scst_tgt_template tgt2x_template = { .name = "qla2x00tgt", .sg_tablesize = 0, @@ -112,19 +147,22 @@ static struct scst_tgt_template tgt2x_template = { .rdy_to_xfer_atomic = 1, #endif .max_hw_pending_time = Q2T_MAX_HW_PENDING_TIME, - .detect = q2x_target_detect, + .detect = q2t_target_detect, .release = q2t_target_release, .xmit_response = q2x_xmit_response, .rdy_to_xfer = q2t_rdy_to_xfer, .on_free_cmd = q2t_on_free_cmd, .task_mgmt_fn_done = q2t_task_mgmt_fn_done, .on_hw_pending_cmd_timeout = q2t_on_hw_pending_cmd_timeout, -}; - + .enable_tgt = q2t_enable_tgt, + .is_tgt_enabled = q2t_is_tgt_enabled, + .tgtt_attrs = q2t_attrs, + .tgt_attrs = q2t_tgt_attrs, #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) -#define trace_flag q2t_trace_flag -static unsigned long q2t_trace_flag = Q2T_DEFAULT_LOG_FLAGS; + .default_trace_flags = Q2T_DEFAULT_LOG_FLAGS, + .trace_flags = &trace_flag, #endif +}; static struct kmem_cache *q2t_cmd_cachep; static struct kmem_cache *q2t_mgmt_cmd_cachep; @@ -224,7 +262,7 @@ static inline int q2t_issue_marker(scsi_qla_host_t *ha, int ha_locked) * Registers with initiator driver (but target mode isn't enabled till * it's turned on via sysfs) */ -static int q2x_target_detect(struct scst_tgt_template *templ) +static int q2t_target_detect(struct scst_tgt_template *tgtt) { int res; struct qla_tgt_data t = { @@ -253,6 +291,8 @@ static int q2x_target_detect(struct scst_tgt_template *templ) goto out; } + qla2xxx_add_targets(); + PRINT_INFO("%s", "Target mode driver for QLogic 2x00 controller " "registered successfully"); @@ -622,7 +662,7 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport) if ((tgt == NULL) || (fcport->port_type != FCT_INITIATOR)) goto out_unlock; - if (tgt->tgt_shutdown) + if (tgt->tgt_stop) goto out_unlock; spin_lock_irq(&ha->hardware_lock); @@ -686,7 +726,7 @@ static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport) dev_loss_tmo = ha->port_down_retry_count + 5; - if (tgt->tgt_shutdown) + if (tgt->tgt_stop) goto out_unlock; spin_lock_irq(&ha->hardware_lock); @@ -747,15 +787,16 @@ static inline int test_tgt_sess_count(struct q2t_tgt *tgt) return res; } -/* Must be called under read locked q2t_unreg_rwsem */ -static int q2t_target_release(struct scst_tgt *scst_tgt) +/* Must be called under tgt_host_action_mutex */ +static void q2t_target_stop(struct scst_tgt *scst_tgt) { - int res = 0; struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); scsi_qla_host_t *ha = tgt->ha; TRACE_ENTRY(); + TRACE_DBG("Stopping target for host %ld(%p)", ha->host_no, ha); + /* * Mutex needed to sync with q2t_fc_port_[added,deleted]. * Lock is needed, because we still can get an incoming packet. @@ -763,7 +804,7 @@ static int q2t_target_release(struct scst_tgt *scst_tgt) mutex_lock(&ha->tgt_mutex); spin_lock_irq(&ha->hardware_lock); - tgt->tgt_shutdown = 1; + tgt->tgt_stop = 1; q2t_clear_tgt_db(tgt, false); spin_unlock_irq(&ha->hardware_lock); mutex_unlock(&ha->tgt_mutex); @@ -786,7 +827,7 @@ static int q2t_target_release(struct scst_tgt *scst_tgt) wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); /* Big hammer */ - if (!ha->host_shutting_down) + if (!ha->host_shutting_down && qla_tgt_mode_enabled(ha)) qla2x00_disable_tgt_mode(ha); /* Wait for sessions to clear out (just in case) */ @@ -802,17 +843,35 @@ static int q2t_target_release(struct scst_tgt *scst_tgt) udelay(2); spin_lock_irq(&ha->hardware_lock); } - scst_tgt_set_tgt_priv(scst_tgt, NULL); ha->tgt = NULL; spin_unlock_irq(&ha->hardware_lock); mutex_unlock(&ha->tgt_mutex); + TRACE_MGMT_DBG("Stop of tgt %p finished", tgt); + + TRACE_EXIT(); + return; +} + +/* Must be called under tgt_host_action_mutex */ +static int q2t_target_release(struct scst_tgt *scst_tgt) +{ + struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); + scsi_qla_host_t *ha = tgt->ha; + + TRACE_ENTRY(); + + q2t_target_stop(scst_tgt); + + ha->q2t_tgt = NULL; + scst_tgt_set_tgt_priv(scst_tgt, NULL); + TRACE_MGMT_DBG("Release of tgt %p finished", tgt); kfree(tgt); - TRACE_EXIT_RES(res); - return res; + TRACE_EXIT(); + return 0; } /* @@ -1238,7 +1297,7 @@ out: uint32_t q2t_convert_to_fc_tm_status(int scst_mstatus) { - int res; + uint32_t res; switch (scst_mstatus) { case SCST_MGMT_STATUS_SUCCESS: @@ -3039,7 +3098,7 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio) TRACE_ENTRY(); - if (unlikely(tgt->tgt_shutdown)) { + if (unlikely(tgt->tgt_stop)) { TRACE_MGMT_DBG("New command while device %p is shutting " "down", tgt); res = -EFAULT; @@ -4176,7 +4235,7 @@ static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio) atio, atio->entry_type, atio->entry_count); /* - * In tgt_shutdown mode we also should allow all requests to pass. + * In tgt_stop mode we also should allow all requests to pass. * Otherwise, some commands can stuck. */ @@ -4278,7 +4337,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt) pkt->handle); /* - * In tgt_shutdown mode we also should allow all requests to pass. + * In tgt_stop mode we also should allow all requests to pass. * Otherwise, some commands can stuck. */ @@ -4523,7 +4582,7 @@ static void q2t_async_event(uint16_t code, scsi_qla_host_t *ha, } /* - * In tgt_shutdown mode we also should allow all requests to pass. + * In tgt_stop mode we also should allow all requests to pass. * Otherwise, some commands can stuck. */ @@ -4696,7 +4755,7 @@ static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha, atio_t *atio) if (rc != QLA_SUCCESS) { PRINT_ERROR("Failed to retrieve fcport information " "-- get_port_database() returned %x " - "(loop_id=0x%04x)\n", rc, loop_id); + "(loop_id=0x%04x)", rc, loop_id); goto out_free_fcport; } @@ -4726,7 +4785,7 @@ static int q2t_exec_sess_work(struct q2t_tgt *tgt, mutex_lock(&ha->tgt_mutex); spin_lock_irq(&ha->hardware_lock); - if (tgt->tgt_shutdown) + if (tgt->tgt_stop) goto send; if (IS_FWI2_CAPABLE(ha)) { @@ -4751,7 +4810,7 @@ static int q2t_exec_sess_work(struct q2t_tgt *tgt, } send: - if (!tgt->tm_to_unknown && !tgt->tgt_shutdown && (sess != NULL)) { + if (!tgt->tm_to_unknown && !tgt->tgt_stop && (sess != NULL)) { TRACE_MGMT_DBG("Sending work cmd %p to SCST", cmd); res = q2t_do_send_cmd_to_scst(ha, cmd, sess); } else { @@ -4891,11 +4950,135 @@ out_unlock: return; } -/* No lock held on entry, process context */ +/* Must be called under tgt_host_action_mutex */ +static int q2t_add_target(scsi_qla_host_t *ha) +{ + int res, rc; + char *wwn; + int sg_tablesize; + struct q2t_tgt *tgt; + + TRACE_ENTRY(); + + TRACE_DBG("Registering target for host %ld(%p)", ha->host_no, ha); + + sBUG_ON((ha->q2t_tgt != NULL) || (ha->tgt != NULL)); + + tgt = kzalloc(sizeof(*tgt), GFP_KERNEL); + if (tgt == NULL) { + TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt " + "failed"); + res = -ENOMEM; + goto out; + } + + tgt->ha = ha; + init_waitqueue_head(&tgt->waitQ); + INIT_LIST_HEAD(&tgt->sess_list); + INIT_LIST_HEAD(&tgt->del_sess_list); + init_timer(&tgt->sess_del_timer); + tgt->sess_del_timer.data = (unsigned long)tgt; + tgt->sess_del_timer.function = q2t_del_sess_timer_fn; + spin_lock_init(&tgt->sess_work_lock); + INIT_WORK(&tgt->sess_work, q2t_sess_work_fn); + INIT_LIST_HEAD(&tgt->sess_works_list); + spin_lock_init(&tgt->srr_lock); + INIT_LIST_HEAD(&tgt->srr_ctio_list); + INIT_LIST_HEAD(&tgt->srr_imm_list); + INIT_WORK(&tgt->srr_work, q2t_handle_srr_work); + + ha->q2t_tgt = tgt; + + if (q2t_get_target_name(ha, &wwn) != 0) + goto out_free; + + tgt->scst_tgt = scst_register(&tgt2x_template, wwn); + + kfree(wwn); + + if (!tgt->scst_tgt) { + PRINT_ERROR("qla2x00tgt(%ld): scst_register() " + "failed for host %ld(%p)", ha->instance, + ha->host_no, ha); + res = -ENOMEM; + goto out_free; + } + + if (IS_FWI2_CAPABLE(ha)) { + PRINT_INFO("qla2400tgt(%ld): using 64 Bit PCI " + "addressing", ha->instance); + tgt->tgt_enable_64bit_addr = 1; + /* 3 is reserved */ + sg_tablesize = + QLA_MAX_SG_24XX(ha->request_q_length - 3); + tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND_24XX; + tgt->datasegs_per_cont = DATASEGS_PER_CONT_24XX; + } else { + if (ha->flags.enable_64bit_addressing) { + PRINT_INFO("qla2x00tgt(%ld): 64 Bit PCI " + "addressing enabled", ha->instance); + tgt->tgt_enable_64bit_addr = 1; + /* 3 is reserved */ + sg_tablesize = + QLA_MAX_SG64(ha->request_q_length - 3); + tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND64; + tgt->datasegs_per_cont = DATASEGS_PER_CONT64; + } else { + PRINT_INFO("qla2x00tgt(%ld): Using 32 Bit " + "PCI addressing", ha->instance); + sg_tablesize = + QLA_MAX_SG32(ha->request_q_length - 3); + tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND32; + tgt->datasegs_per_cont = DATASEGS_PER_CONT32; + } + } + +#ifndef CONFIG_SCST_PROC + rc = sysfs_create_link(scst_sysfs_get_tgt_kobj(tgt->scst_tgt), + &ha->host->shost_dev.kobj, "host"); + if (rc != 0) + PRINT_ERROR("Unable to create \"host\" link for target " + "%s", scst_get_tgt_name(tgt->scst_tgt)); +#endif + + scst_tgt_set_sg_tablesize(tgt->scst_tgt, sg_tablesize); + scst_tgt_set_tgt_priv(tgt->scst_tgt, tgt); + +out: + TRACE_EXIT_RES(res); + return res; + +out_free: + ha->q2t_tgt = NULL; + kfree(tgt); + goto out; +} + +/* Must be called under tgt_host_action_mutex */ +static int q2t_remove_target(scsi_qla_host_t *ha) +{ + TRACE_ENTRY(); + + if ((ha->q2t_tgt == NULL) || (ha->tgt != NULL)) { + PRINT_ERROR("qla2x00tgt(%ld): Can't remove " + "existing target", ha->instance); + } + + TRACE_DBG("Unregistering target for host %ld(%p)", ha->host_no, ha); + scst_unregister(ha->tgt->scst_tgt); + /* + * Free of tgt happens via callback q2t_target_release + * called from scst_unregister, so we shouldn't touch + * it again. + */ + + TRACE_EXIT(); + return 0; +} + static int q2t_host_action(scsi_qla_host_t *ha, - qla2x_tgt_host_action_t action) + qla2x_tgt_host_action_t action) { - struct q2t_tgt *tgt = NULL; int res = 0; TRACE_ENTRY(); @@ -4903,14 +5086,21 @@ static int q2t_host_action(scsi_qla_host_t *ha, sBUG_ON(ha == NULL); /* To sync with q2t_exit() */ - down_read(&q2t_unreg_rwsem); + if (down_read_trylock(&q2t_unreg_rwsem) == 0) + goto out; + + mutex_lock(&ha->tgt_host_action_mutex); switch (action) { + case ADD_TARGET: + res = q2t_add_target(ha); + break; + case REMOVE_TARGET: + res = q2t_remove_target(ha); + break; case ENABLE_TARGET_MODE: { fc_port_t *fcport; - char *wwn; - int sg_tablesize; if (qla_tgt_mode_enabled(ha)) { PRINT_INFO("qla2x00tgt(%ld): Target mode already " @@ -4918,103 +5108,22 @@ static int q2t_host_action(scsi_qla_host_t *ha, break; } + if ((ha->q2t_tgt == NULL) || (ha->tgt != NULL)) { + PRINT_ERROR("qla2x00tgt(%ld): Can't enable target mode " + "for not existing target", ha->instance); + break; + } + PRINT_INFO("qla2x00tgt(%ld): Enabling target mode", ha->instance); - if (ha->tgt != NULL) - goto do_enable; - - tgt = kzalloc(sizeof(*tgt), GFP_KERNEL); - if (tgt == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt " - "failed"); - res = -ENOMEM; - break; - } - - tgt->ha = ha; - init_waitqueue_head(&tgt->waitQ); - INIT_LIST_HEAD(&tgt->sess_list); - INIT_LIST_HEAD(&tgt->del_sess_list); - init_timer(&tgt->sess_del_timer); - tgt->sess_del_timer.data = (unsigned long)tgt; - tgt->sess_del_timer.function = q2t_del_sess_timer_fn; - spin_lock_init(&tgt->sess_work_lock); - INIT_WORK(&tgt->sess_work, q2t_sess_work_fn); - INIT_LIST_HEAD(&tgt->sess_works_list); - spin_lock_init(&tgt->srr_lock); - INIT_LIST_HEAD(&tgt->srr_ctio_list); - INIT_LIST_HEAD(&tgt->srr_imm_list); - INIT_WORK(&tgt->srr_work, q2t_handle_srr_work); - - if (q2t_get_target_name(ha, &wwn) != 0) { - kfree(tgt); - break; - } - - mutex_lock(&ha->tgt_mutex); - - tgt->scst_tgt = scst_register(&tgt2x_template, wwn); - - /* - * scst_tgt protected by tgt_mutex from not get freed before - * been initialized - */ - - kfree(wwn); - - if (!tgt->scst_tgt) { - PRINT_ERROR("qla2x00tgt(%ld): scst_register() " - "failed for host %ld(%p)", ha->instance, - ha->host_no, ha); - kfree(tgt); - res = -ENOMEM; - break; - } - - if (IS_FWI2_CAPABLE(ha)) { - PRINT_INFO("qla2400tgt(%ld): using 64 Bit PCI " - "addressing", ha->instance); - tgt->tgt_enable_64bit_addr = 1; - /* 3 is reserved */ - sg_tablesize = - QLA_MAX_SG_24XX(ha->request_q_length - 3); - tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND_24XX; - tgt->datasegs_per_cont = DATASEGS_PER_CONT_24XX; - } else { - if (ha->flags.enable_64bit_addressing) { - PRINT_INFO("qla2x00tgt(%ld): 64 Bit PCI " - "addressing enabled", ha->instance); - tgt->tgt_enable_64bit_addr = 1; - /* 3 is reserved */ - sg_tablesize = - QLA_MAX_SG64(ha->request_q_length - 3); - tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND64; - tgt->datasegs_per_cont = DATASEGS_PER_CONT64; - } else { - PRINT_INFO("qla2x00tgt(%ld): Using 32 Bit " - "PCI addressing", ha->instance); - sg_tablesize = - QLA_MAX_SG32(ha->request_q_length - 3); - tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND32; - tgt->datasegs_per_cont = DATASEGS_PER_CONT32; - } - } - - scst_tgt_set_sg_tablesize(tgt->scst_tgt, sg_tablesize); - scst_tgt_set_tgt_priv(tgt->scst_tgt, tgt); - spin_lock_irq(&ha->hardware_lock); - ha->tgt = tgt; + ha->tgt = ha->q2t_tgt; + ha->tgt->tgt_stop = 0; spin_unlock_irq(&ha->hardware_lock); - - mutex_unlock(&ha->tgt_mutex); - list_for_each_entry_rcu(fcport, &ha->fcports, list) { q2t_fc_port_added(ha, fcport); } - -do_enable: TRACE_DBG("Enable tgt mode for host %ld(%ld,%p)", ha->host_no, ha->instance, ha); qla2x00_enable_tgt_mode(ha); @@ -5033,14 +5142,7 @@ do_enable: sBUG_ON(ha->tgt == NULL); - TRACE_DBG("Unregistering target for host %ld(%p)", - ha->host_no, ha); - scst_unregister(ha->tgt->scst_tgt); - /* - * Free of tgt happens via callback q2t_target_release - * called from scst_unregister, so we shouldn't touch - * it again. - */ + q2t_target_stop(ha->tgt->scst_tgt); break; default: @@ -5050,12 +5152,137 @@ do_enable: break; } - up_read(&q2t_unreg_rwsem); + mutex_unlock(&ha->tgt_host_action_mutex); + up_read(&q2t_unreg_rwsem); +out: TRACE_EXIT_RES(res); return res; } +static ssize_t q2t_enable_tgt(struct scst_tgt *scst_tgt, const char *buffer, + size_t size) +{ + struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); + scsi_qla_host_t *ha = tgt->ha; + int res = 0; + + switch (buffer[0]) { + case '0' : + res = q2t_host_action(ha, DISABLE_TARGET_MODE); + break; + case '1' : + res = q2t_host_action(ha, ENABLE_TARGET_MODE); + break; + default: + PRINT_ERROR("%s: Requested action not understood: %s", + __func__, buffer); + res = -EINVAL; + goto out; + } + + if (res == 0) + res = size; + + if ((size > 1) && (buffer[1] == 'r')) { + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + qla2x00_wait_for_hba_online(ha); + } + +out: + return res; +} + +static bool q2t_is_tgt_enabled(struct scst_tgt *scst_tgt) +{ + struct q2t_tgt *tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); + scsi_qla_host_t *ha = tgt->ha; + + return qla_tgt_mode_enabled(ha); +} + +static ssize_t q2t_show_expl_conf_enabled(struct kobject *kobj, + struct kobj_attribute *attr, char *buffer) +{ + struct scst_tgt *scst_tgt; + struct q2t_tgt *tgt; + scsi_qla_host_t *ha; + ssize_t size; + + scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); + ha = tgt->ha; + + size = scnprintf(buffer, PAGE_SIZE, "%d\n", ha->enable_explicit_conf); + + return size; +} + +static ssize_t q2t_store_expl_conf_enabled(struct kobject *kobj, + struct kobj_attribute *attr, const char *buffer, size_t size) +{ + struct scst_tgt *scst_tgt; + struct q2t_tgt *tgt; + scsi_qla_host_t *ha; + unsigned long flags; + + scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + tgt = (struct q2t_tgt *)scst_tgt_get_tgt_priv(scst_tgt); + ha = tgt->ha; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + switch (buffer[0]) { + case '0' : + ha->enable_explicit_conf = 0; + PRINT_INFO("qla2xxx(%ld): explicit conformations disabled", + ha->instance); + break; + case '1' : + ha->enable_explicit_conf = 1; + PRINT_INFO("qla2xxx(%ld): explicit conformations enabled", + ha->instance); + break; + default: +#if defined(QL_DEBUG_LEVEL_9) || defined(QL_DEBUG_LEVEL_11) + PRINT_INFO("%s: Requested action not understood: %s", + __func__, buffer); +#endif + break; + } + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return size; +} + +static ssize_t q2t_version_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + sprintf(buf, "%s\n", Q2T_VERSION_STRING); + +#ifdef CONFIG_SCST_EXTRACHECKS + strcat(buf, "EXTRACHECKS\n"); +#endif + +#ifdef CONFIG_SCST_TRACING + strcat(buf, "TRACING\n"); +#endif + +#ifdef CONFIG_SCST_DEBUG + strcat(buf, "DEBUG\n"); +#endif + +#ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD + strcat(buf, "QLA_TGT_DEBUG_WORK_IN_THREAD\n"); +#endif + + TRACE_EXIT(); + return strlen(buf); +} + +#ifdef CONFIG_SCST_PROC + #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) #define Q2T_PROC_LOG_ENTRY_NAME "trace_level" @@ -5194,6 +5421,8 @@ static void q2t_proc_log_entry_clean(struct scst_tgt_template *templ) return; } +#endif /* CONFIG_SCST_PROC */ + static int __init q2t_init(void) { int res = 0; @@ -5233,15 +5462,19 @@ static int __init q2t_init(void) * called via scst_register_target_template() */ +#ifdef CONFIG_SCST_PROC res = q2t_proc_log_entry_build(&tgt2x_template); if (res < 0) goto out_unreg_target2x; +#endif out: TRACE_EXIT_RES(res); return res; +#ifdef CONFIG_SCST_PROC out_unreg_target2x: +#endif scst_unregister_target_template(&tgt2x_template); qla2xxx_tgt_unregister_driver(); @@ -5266,17 +5499,26 @@ static void __exit q2t_exit(void) /* To sync with q2t_host_action() */ down_write(&q2t_unreg_rwsem); +#ifdef CONFIG_SCST_PROC q2t_proc_log_entry_clean(&tgt2x_template); +#endif + scst_unregister_target_template(&tgt2x_template); - up_write(&q2t_unreg_rwsem); - + /* + * Now we have everywhere target mode disabled and no possibilities + * to call us through sysfs, so we can safely remove all the references + * to our functions. + */ qla2xxx_tgt_unregister_driver(); mempool_destroy(q2t_mgmt_cmd_mempool); kmem_cache_destroy(q2t_mgmt_cmd_cachep); kmem_cache_destroy(q2t_cmd_cachep); + /* Let's make lockdep happy */ + up_write(&q2t_unreg_rwsem); + TRACE_EXIT(); return; } diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h index c54782c8d..0fc79a603 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.h +++ b/qla2x00t/qla2x00-target/qla2x00t.h @@ -134,7 +134,7 @@ struct q2t_tgt { * Protected by tgt_mutex AND hardware_lock for writing and tgt_mutex * OR hardware_lock for reading. */ - unsigned long tgt_shutdown; /* the driver is being released */ + unsigned long tgt_stop; /* the driver is being stopped */ /* Count of sessions refering q2t_tgt. Protected by hardware_lock. */ int sess_count; @@ -162,6 +162,8 @@ struct q2t_tgt { struct list_head srr_ctio_list; struct list_head srr_imm_list; struct work_struct srr_work; + + struct list_head tgt_list_entry; }; /* diff --git a/qla2x00t/qla2x_tgt.h b/qla2x00t/qla2x_tgt.h index b43e8fd8a..df7ed7248 100644 --- a/qla2x00t/qla2x_tgt.h +++ b/qla2x00t/qla2x_tgt.h @@ -129,6 +129,8 @@ qla2x00_send_enable_lun(scsi_qla_host_t *ha, bool enable) } } +extern void qla2xxx_add_targets(void); + #endif /* CONFIG_SCSI_QLA2XXX_TARGET */ #endif /* __QLA2X_TGT_H */ diff --git a/qla2x00t/qla2x_tgt_def.h b/qla2x00t/qla2x_tgt_def.h index d3fdfea9d..2eb5d3f63 100644 --- a/qla2x00t/qla2x_tgt_def.h +++ b/qla2x00t/qla2x_tgt_def.h @@ -44,13 +44,13 @@ * Must be changed on any change in any initiator visible interfaces or * data in the target add-on */ -#define QLA2X_TARGET_MAGIC 264 +#define QLA2X_TARGET_MAGIC 265 /* * Must be changed on any change in any target visible interfaces or * data in the initiator */ -#define QLA2X_INITIATOR_MAGIC 57217 +#define QLA2X_INITIATOR_MAGIC 57218 #define QLA2X00_COMMAND_COUNT_INIT 250 #define QLA2X00_IMMED_NOTIFY_COUNT_INIT 250 @@ -689,8 +689,10 @@ typedef struct { \********************************************************************/ typedef enum { - DISABLE_TARGET_MODE = 0, - ENABLE_TARGET_MODE = 1 + ADD_TARGET = 0, + REMOVE_TARGET, + DISABLE_TARGET_MODE, + ENABLE_TARGET_MODE, } qla2x_tgt_host_action_t; /* Changing it don't forget to change QLA2X_TARGET_MAGIC! */ diff --git a/qla2x00t/qla_attr.c b/qla2x00t/qla_attr.c index 06441e6a8..d649c1561 100644 --- a/qla2x00t/qla_attr.c +++ b/qla2x00t/qla_attr.c @@ -96,6 +96,10 @@ static DEVICE_ATTR(class2_enabled, #ifdef CONFIG_SCSI_QLA2XXX_TARGET +#define CONFIG_SCST_PROC + +#ifdef CONFIG_SCST_PROC + static ssize_t qla2x00_show_tgt_enabled(struct device *dev, struct device_attribute *attr, char *buffer) @@ -208,6 +212,8 @@ static DEVICE_ATTR(explicit_conform_enabled, qla2x00_show_expl_conf_enabled, qla2x00_store_expl_conf_enabled); +#endif /* CONFIG_SCST_PROC */ + static ssize_t qla2x00_show_resource_counts(struct device *dev, struct device_attribute *attr, char *buffer) @@ -377,7 +383,7 @@ out_free: gid = (struct gid_list_info *)id_iter; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { size += scnprintf(buffer+size, max_size-size, - " %02x %02x%02x%02x\n", + "%02x %02x%02x%02x\n", gid->loop_id_2100, gid->domain, gid->area, @@ -1332,8 +1338,10 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_optrom_fw_version, &dev_attr_class2_enabled, #ifdef CONFIG_SCSI_QLA2XXX_TARGET +#ifdef CONFIG_SCST_PROC &dev_attr_target_mode_enabled, &dev_attr_explicit_conform_enabled, +#endif &dev_attr_resource_counts, &dev_attr_port_database, #endif diff --git a/qla2x00t/qla_def.h b/qla2x00t/qla_def.h index f36eb0404..03a500198 100644 --- a/qla2x00t/qla_def.h +++ b/qla2x00t/qla_def.h @@ -2447,8 +2447,7 @@ typedef struct scsi_qla_host { * accessed from many contexts, including concurrently. Seems, the * original author of this code thought that if anything never deleted * from this list, it is always safe to travel over it without any - * protection. Obviously, this isn't true. (The whole decision to keep - * stale entries forever is quite questionable as well.) We will fix + * protection. Obviously, this isn't true. We will fix * that making access to fcports list using RCU functions. This isn't * a complete solution, because it isn't clear if it's safe if some * function going over this list misses just added entry, but definitely @@ -2628,6 +2627,13 @@ typedef struct scsi_qla_host { #ifdef CONFIG_SCSI_QLA2XXX_TARGET struct mutex tgt_mutex; + struct mutex tgt_host_action_mutex; + + /* Protected by tgt_host_action_mutex */ + struct q2t_tgt *q2t_tgt; + + struct list_head ha_list_entry; + int saved_set; uint16_t saved_exchange_count; uint32_t saved_firmware_options_1; @@ -2635,7 +2641,7 @@ typedef struct scsi_qla_host { uint32_t saved_firmware_options_3; uint8_t saved_firmware_options[2]; uint8_t saved_add_firmware_options[2]; -#endif +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ struct scsi_qla_host *parent; /* holds pport */ unsigned long vp_flags; diff --git a/qla2x00t/qla_isr.c b/qla2x00t/qla_isr.c index 660d79c51..3a33a4691 100644 --- a/qla2x00t/qla_isr.c +++ b/qla2x00t/qla_isr.c @@ -8,10 +8,6 @@ #include "qla_def.h" #include "qla2x_tgt.h" -#ifdef CONFIG_SCSI_QLA2XXX_TARGET -struct qla_tgt_data qla_target; -#endif - #include #include diff --git a/qla2x00t/qla_os.c b/qla2x00t/qla_os.c index 849b4968a..b02f4236b 100644 --- a/qla2x00t/qla_os.c +++ b/qla2x00t/qla_os.c @@ -25,6 +25,19 @@ */ char qla2x00_version_str[40]; +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +/* + * Target mode add-on's callbacks + */ +struct qla_tgt_data qla_target; + +/* + * List of ha's and mutex protecting it. + */ +static LIST_HEAD(qla_ha_list); +static DEFINE_MUTEX(qla_ha_list_mutex); +#endif + /* * SRB allocation cache */ @@ -591,6 +604,7 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha) return (return_status); } +EXPORT_SYMBOL(qla2x00_wait_for_hba_online); /* * qla2x00_wait_for_loop_ready @@ -1544,6 +1558,23 @@ qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time) return atomic_read(&ha->loop_state) == LOOP_READY; } +#ifdef CONFIG_SCSI_QLA2XXX_TARGET +void +qla2xxx_add_targets(void) +{ + scsi_qla_host_t *ha; + + mutex_lock(&qla_ha_list_mutex); + list_for_each_entry(ha, &qla_ha_list, ha_list_entry) { + if (qla_target.tgt_host_action != NULL) + qla_target.tgt_host_action(ha, ADD_TARGET); + } + mutex_unlock(&qla_ha_list_mutex); + return; +} +EXPORT_SYMBOL(qla2xxx_add_targets); +#endif /* CONFIG_SCSI_QLA2XXX_TARGET */ + /* * PCI driver interface */ @@ -1608,11 +1639,14 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->parent = NULL; ha->bars = bars; ha->mem_only = mem_only; + + spin_lock_init(&ha->hardware_lock); + #ifdef CONFIG_SCSI_QLA2XXX_TARGET mutex_init(&ha->tgt_mutex); + mutex_init(&ha->tgt_host_action_mutex); qla_clear_tgt_mode(ha); #endif - spin_lock_init(&ha->hardware_lock); /* Set ISP-type information. */ qla2x00_set_isp_flags(ha); @@ -1796,6 +1830,19 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->flags.enable_64bit_addressing ? '+': '-', ha->host_no, ha->isp_ops->fw_version_str(ha, fw_str)); +#ifdef CONFIG_SCSI_QLA2XXX_TARGET + if (qla_target.tgt_host_action != NULL) + qla_target.tgt_host_action(ha, ADD_TARGET); + + /* + * Must be after tgt_host_action() to not race with + * qla2xxx_add_targets(). + */ + mutex_lock(&qla_ha_list_mutex); + list_add_tail(&ha->ha_list_entry, &qla_ha_list); + mutex_unlock(&qla_ha_list_mutex); +#endif + return 0; probe_failed: @@ -1838,14 +1885,18 @@ qla2x00_remove_one(struct pci_dev *pdev) ha = pci_get_drvdata(pdev); #ifdef CONFIG_SCSI_QLA2XXX_TARGET - { - unsigned long flags; - spin_lock_irqsave(&ha->hardware_lock, flags); - ha->host_shutting_down = 1; - spin_unlock_irqrestore(&ha->hardware_lock, flags); - } - if (qla_target.tgt_host_action != NULL) - qla_target.tgt_host_action(ha, DISABLE_TARGET_MODE); + /* + * Must be before tgt_host_action() to not race with + * qla2xxx_add_targets(). + */ + mutex_lock(&qla_ha_list_mutex); + list_del(&ha->ha_list_entry); + mutex_unlock(&qla_ha_list_mutex); + + ha->host_shutting_down = 1; + + if (qla_target.tgt_host_action != NULL) + qla_target.tgt_host_action(ha, REMOVE_TARGET); #endif /* Necessary to prevent races with it */ diff --git a/scst-full_perf.patch b/scst-full_perf.patch deleted file mode 100644 index d5d19042a..000000000 --- a/scst-full_perf.patch +++ /dev/null @@ -1,39 +0,0 @@ -Index: scst/src/Makefile -=================================================================== ---- scst/src/Makefile (revision 544) -+++ scst/src/Makefile (working copy) -@@ -117,7 +117,7 @@ EXTRA_CFLAGS += -I$(SCST_INC_DIR) -Wextr - - #EXTRA_CFLAGS += -DCONFIG_SCST_STRICT_SERIALIZING - --EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS -+#EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS - - #EXTRA_CFLAGS += -DCONFIG_SCST_USE_EXPECTED_VALUES - #EXTRA_CFLAGS += -DALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ -@@ -127,7 +127,7 @@ EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECK - - #EXTRA_CFLAGS += -DCONFIG_SCST_TRACING - --EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -+#EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g - #EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG_TM -DCONFIG_SCST_TM_DBG_GO_OFFLINE - #EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG_RETRY - #EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG_OOM -Index: scst/src/dev_handlers/Makefile -=================================================================== ---- scst/src/dev_handlers/Makefile (revision 544) -+++ scst/src/dev_handlers/Makefile (working copy) -@@ -70,10 +70,10 @@ endif - EXTRA_CFLAGS += -I$(SUBDIRS) -I$(SCST_INC_DIR) -Wextra -Wno-unused-parameter \ - -Wno-missing-field-initializers - --EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS -+#EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS - - #EXTRA_CFLAGS += -DCONFIG_SCST_TRACING --EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -+#EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g - - clean: - rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend Modules.symvers \ diff --git a/scst-release.patch b/scst-release.patch deleted file mode 100644 index 68202887e..000000000 --- a/scst-release.patch +++ /dev/null @@ -1,43 +0,0 @@ -Index: scst/src/Makefile -=================================================================== ---- scst/src/Makefile (revision 544) -+++ scst/src/Makefile (working copy) -@@ -117,7 +117,7 @@ EXTRA_CFLAGS += -I$(SCST_INC_DIR) -Wextr - - #EXTRA_CFLAGS += -DCONFIG_SCST_STRICT_SERIALIZING - --EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS -+#EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS - - #EXTRA_CFLAGS += -DCONFIG_SCST_USE_EXPECTED_VALUES - #EXTRA_CFLAGS += -DALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ -@@ -125,9 +125,9 @@ EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECK - - #EXTRA_CFLAGS += -fno-inline - --#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING -+EXTRA_CFLAGS += -DCONFIG_SCST_TRACING - --EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -+#EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g - #EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG_TM -DCONFIG_SCST_TM_DBG_GO_OFFLINE - #EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG_RETRY - #EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG_OOM -Index: scst/src/dev_handlers/Makefile -=================================================================== ---- scst/src/dev_handlers/Makefile (revision 544) -+++ scst/src/dev_handlers/Makefile (working copy) -@@ -70,10 +70,10 @@ endif - EXTRA_CFLAGS += -I$(SUBDIRS) -I$(SCST_INC_DIR) -Wextra -Wno-unused-parameter \ - -Wno-missing-field-initializers - --EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS -+#EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS - --#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING --EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -+EXTRA_CFLAGS += -DCONFIG_SCST_TRACING -+#EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g - - clean: - rm -f *.o *.ko .*.cmd *.mod.c .*.d .depend Modules.symvers \ diff --git a/scst/Makefile b/scst/Makefile index e443058cb..4e5113d05 100644 --- a/scst/Makefile +++ b/scst/Makefile @@ -22,19 +22,37 @@ SCST_DIR=src -all: +all: cd $(SCST_DIR) && $(MAKE) $@ -install: +install: cd $(SCST_DIR) && $(MAKE) $@ -uninstall: +uninstall: cd $(SCST_DIR) && $(MAKE) $@ -clean: +clean: cd $(SCST_DIR) && $(MAKE) $@ -extraclean: +extraclean: + cd $(SCST_DIR) && $(MAKE) $@ + +debug2release: + cd $(SCST_DIR) && $(MAKE) $@ + +release2debug: + cd $(SCST_DIR) && $(MAKE) $@ + +debug2perf: + cd $(SCST_DIR) && $(MAKE) $@ + +perf2debug: + cd $(SCST_DIR) && $(MAKE) $@ + +disable_proc: + cd $(SCST_DIR) && $(MAKE) $@ + +enable_proc: cd $(SCST_DIR) && $(MAKE) $@ help: @@ -48,4 +66,4 @@ help: @echo " - be sure to compile qla against the correct initiator" @echo " driver. Read its README for details." -.PHONY: all install uninstall clean extraclean help +.PHONY: all install uninstall clean extraclean help debug2release release2debug debug2perf perf2debug disable_proc enable_proc diff --git a/scst/README b/scst/README index 8af0ae084..1bbdfd341 100644 --- a/scst/README +++ b/scst/README @@ -63,16 +63,18 @@ them are optional, so, if you don't need the corresponding functionality, you may not apply them. 1. scst_exec_req_fifo-2.6.X.patch. This patch is necessary for -pass-through dev handlers (scst_disk, scst_tape, etc.). Kernels <2.6.30 -need it, because in them scsi_do_req()/scsi_execute_async() work in LIFO -order, instead of expected and required FIFO. So, SCST needs new -functions scsi_do_req_fifo() or scsi_execute_async_fifo() to be added in -the kernel. Kernels >=2.6.30 need it, because in them there is no -functionality to execute commands with data in scatter-gather buffers. -This patch adds the necessary functionality to the kernel. You may not -patch your kernel if you don't need pass-through support. Alternatively, -on kernels <2.6.30 you can define CONFIG_SCST_STRICT_SERIALIZING compile -option during the compilation (see description below). +pass-through dev handlers, because in the mainstream kernels +scsi_do_req()/scsi_execute_async() work in LIFO order, instead of +expected and required FIFO. So SCST needs new functions +scsi_do_req_fifo() or scsi_execute_async_fifo() to be added in the +kernel. This patch does that. You may not patch the kernel if you don't +need the pass-through support. Alternatively, you can define +CONFIG_SCST_STRICT_SERIALIZING compile option during the compilation +(see description below). Unfortunately, the CONFIG_SCST_STRICT_SERIALIZING +trick doesn't work on kernels starting from 2.6.30, because those +kernels don't have the required functionality (scsi_execute_async()) +anymore. So, on them to have pass-through working you have to apply +scst_exec_req_fifo-2.6.X.patch. 2. io_context-2.6.X.patch. This patch exports some IO context management functions from the kernel. For performance reasons SCST queues commands @@ -257,13 +259,13 @@ in/out in Makefile: support supplying expected values. - CONFIG_SCST_DEBUG_TM - if defined, turns on task management functions - debugging, when on LUN 0 in the default access control group some of the - commands will be delayed for about 60 sec., so making the remote - initiator send TM functions, eg ABORT TASK and TARGET RESET. Also - define CONFIG_SCST_TM_DBG_GO_OFFLINE symbol in the Makefile if you - want that the device eventually become completely unresponsive, or - otherwise to circle around ABORTs and RESETs code. Needs CONFIG_SCST_DEBUG - turned on. + debugging, when on LUN 6 some of the commands will be delayed for + about 60 sec., so making the remote initiator send TM functions, eg + ABORT TASK and TARGET RESET. Also define + CONFIG_SCST_TM_DBG_GO_OFFLINE symbol in the Makefile if you want that + the device eventually become completely unresponsive, or otherwise to + circle around ABORTs and RESETs code. Needs CONFIG_SCST_DEBUG turned + on. - CONFIG_SCST_STRICT_SERIALIZING - if defined, makes SCST send all commands to underlying SCSI device synchronously, one after one. This makes task @@ -275,8 +277,9 @@ in/out in Makefile: commands reliably if they sent asynchronously to a stateful device. Turned off by default, turn it on if you use stateful device(s) and need as much error recovery reliability as possible. As a side effect - of CONFIG_SCST_STRICT_SERIALIZING, no kernel patching is necessary - for pass-through device handlers (scst_disk, etc.). + of CONFIG_SCST_STRICT_SERIALIZING, on kernels below 2.6.30 no kernel + patching is necessary for pass-through device handlers (scst_disk, + etc.). - CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ - if defined, it will be allowed to submit pass-through commands to real SCSI devices via the SCSI @@ -1121,4 +1124,8 @@ Thanks to: for UNH-iSCSI project (http://www.iol.unh.edu/consortiums/iscsi/index.html) on which interface between SCST core and target drivers was based. + * Daniel Debonzi for a big part of SCST sysfs tree + implementation + + Vladislav Bolkhovitin , http://scst.sourceforge.net diff --git a/scst/README_in-tree b/scst/README_in-tree index 00fae1764..318d48f87 100644 --- a/scst/README_in-tree +++ b/scst/README_in-tree @@ -180,13 +180,13 @@ your favorite kernel configuration Makefile target, e.g. "make xconfig": support supplying expected values. - CONFIG_SCST_DEBUG_TM - if defined, turns on task management functions - debugging, when on LUN 0 in the default access control group some of the - commands will be delayed for about 60 sec., so making the remote - initiator send TM functions, eg ABORT TASK and TARGET RESET. Also - define CONFIG_SCST_TM_DBG_GO_OFFLINE symbol in the Makefile if you - want that the device eventually become completely unresponsive, or - otherwise to circle around ABORTs and RESETs code. Needs CONFIG_SCST_DEBUG - turned on. + debugging, when on LUN 6 some of the commands will be delayed for + about 60 sec., so making the remote initiator send TM functions, eg + ABORT TASK and TARGET RESET. Also define + CONFIG_SCST_TM_DBG_GO_OFFLINE symbol in the Makefile if you want that + the device eventually become completely unresponsive, or otherwise to + circle around ABORTs and RESETs code. Needs CONFIG_SCST_DEBUG turned + on. - CONFIG_SCST_STRICT_SERIALIZING - if defined, makes SCST send all commands to underlying SCSI device synchronously, one after one. This makes task @@ -198,8 +198,9 @@ your favorite kernel configuration Makefile target, e.g. "make xconfig": commands reliably if they sent asynchronously to a stateful device. Turned off by default, turn it on if you use stateful device(s) and need as much error recovery reliability as possible. As a side effect - of CONFIG_SCST_STRICT_SERIALIZING, no kernel patching is necessary - for pass-through device handlers (scst_disk, etc.). + of CONFIG_SCST_STRICT_SERIALIZING, on kernels below 2.6.30 no kernel + patching is necessary for pass-through device handlers (scst_disk, + etc.). - CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ - if defined, it will be allowed to submit pass-through commands to real SCSI devices via the SCSI @@ -1030,4 +1031,8 @@ Thanks to: * Bart Van Assche for a lot of help + * Daniel Debonzi for a big part of SCST sysfs tree + implementation + + Vladislav Bolkhovitin , http://scst.sourceforge.net diff --git a/scst/include/scst.h b/scst/include/scst.h index 7027c38fb..2514515e6 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -25,9 +25,15 @@ #include #include #include -#include #include +#define CONFIG_SCST_PROC + +#ifdef CONFIG_SCST_PROC +#include +#include +#endif + #include #include #include @@ -52,8 +58,13 @@ typedef _Bool bool; * and FIO_REV in usr/fileio/common.h as well. */ #define SCST_VERSION(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + d) -#define SCST_VERSION_CODE SCST_VERSION(1, 0, 2, 0) -#define SCST_VERSION_STRING "1.0.2" +#define SCST_VERSION_CODE SCST_VERSION(2, 0, 0, 0) +#ifdef CONFIG_SCST_PROC +#define SCST_VERSION_STRING_SUFFIX "-procfs" +#else +#define SCST_VERSION_STRING_SUFFIX +#endif +#define SCST_VERSION_STRING "2.0.0-pre1" SCST_VERSION_STRING_SUFFIX #define SCST_INTERFACE_VERSION \ SCST_VERSION_STRING "$Revision$" SCST_CONST_VERSION @@ -455,11 +466,15 @@ enum scst_exec_context { #define SCST_TGT_DEV_CLUST_POOL 11 +#ifdef CONFIG_SCST_PROC + /************************************************************* ** Name of the entry in /proc *************************************************************/ #define SCST_PROC_ENTRY_NAME "scsi_tgt" +#endif + /************************************************************* ** Activities suspending timeout *************************************************************/ @@ -580,8 +595,10 @@ struct scst_tgt_template { unsigned xmit_response_atomic:1; unsigned rdy_to_xfer_atomic:1; +#ifdef CONFIG_SCST_PROC /* True, if the template doesn't need the entry in /proc */ unsigned no_proc_entry:1; +#endif /* * The maximum time in seconds cmd can stay inside the target @@ -784,6 +801,7 @@ struct scst_tgt_template { */ int (*report_aen) (struct scst_aen *aen); +#ifdef CONFIG_SCST_PROC /* * Those functions can be used to export the driver's statistics and * other infos to the world outside the kernel as well as to get some @@ -794,6 +812,26 @@ struct scst_tgt_template { int (*read_proc) (struct seq_file *seq, struct scst_tgt *tgt); int (*write_proc) (char *buffer, char **start, off_t offset, int length, int *eof, struct scst_tgt *tgt); +#endif + + /* + * This function allows to enable or disable particular target. + * A disabled target doesn't receive and process any SCSI commands. + * + * SHOULD HAVE to avoid race when there are connected initiators, + * while target not yet completed the initial configuration. In this + * case the too early connected initiators would see not those devices, + * which they intended to see. + */ + ssize_t (*enable_tgt) (struct scst_tgt *tgt, const char *buffer, + size_t size); + + /* + * This function shows if particular target is enabled or not. + * + * SHOULD HAVE, see above why. + */ + bool (*is_tgt_enabled) (struct scst_tgt *tgt); /* * Name of the template. Must be unique to identify @@ -809,7 +847,25 @@ struct scst_tgt_template { */ int threads_num; - /* Private, must be inited to 0 by memset() */ + /* Optional default log flags */ + const unsigned long default_trace_flags; + + /* Optional pointer to trace flags */ + unsigned long *trace_flags; + + /* Optional local trace table */ + struct scst_trace_log *trace_tbl; + + /* Optional local trace table help string */ + const char *trace_tbl_help; + + /* Optional sysfs attributes */ + const struct attribute **tgtt_attrs; + + /* Optional sysfs target attributes */ + const struct attribute **tgt_attrs; + + /** Private, must be inited to 0 by memset() **/ /* List of targets per template, protected by scst_mutex */ struct list_head tgt_list; @@ -817,11 +873,22 @@ struct scst_tgt_template { /* List entry of global templates list */ struct list_head scst_template_list_entry; +#ifdef CONFIG_SCST_PROC /* The pointer to the /proc directory entry */ struct proc_dir_entry *proc_tgt_root; +#endif + /* Set if tgtt_kobj was initialized */ + unsigned int tgtt_kobj_initialized:1; + + struct kobject tgtt_kobj; /* kobject for this struct */ + + struct completion tgtt_kobj_release_cmpl; + +#ifdef CONFIG_SCST_PROC /* Device number in /proc */ int proc_dev_num; +#endif }; struct scst_dev_type { @@ -836,8 +903,10 @@ struct scst_dev_type { unsigned exec_atomic:1; unsigned dev_done_atomic:1; +#ifdef CONFIG_SCST_PROC /* Set, if no /proc files should be automatically created by SCST */ unsigned no_proc:1; +#endif /* * Should be set, if exec() is synchronous. This is a hint to SCST core @@ -948,6 +1017,7 @@ struct scst_dev_type { /* Called when tgt_dev (session) is detaching from the dev handler */ void (*detach_tgt) (struct scst_tgt_dev *tgt_dev); +#ifdef CONFIG_SCST_PROC /* * Those functions can be used to export the handler's statistics and * other infos to the world outside the kernel as well as to get some @@ -958,6 +1028,7 @@ struct scst_dev_type { int (*read_proc) (struct seq_file *seq, struct scst_dev_type *dev_type); int (*write_proc) (char *buffer, char **start, off_t offset, int length, int *eof, struct scst_dev_type *dev_type); +#endif /* * Name of the dev handler. Must be unique. MUST HAVE. @@ -972,15 +1043,48 @@ struct scst_dev_type { */ int threads_num; + /* Optional default log flags */ + const unsigned long default_trace_flags; + + /* Optional pointer to trace flags */ + unsigned long *trace_flags; + + /* Optional local trace table */ + struct scst_trace_log *trace_tbl; + + /* Optional local trace table help string */ + const char *trace_tbl_help; + + /* Optional sysfs attributes */ + const struct attribute **devt_attrs; + + /* Optional sysfs device attributes */ + const struct attribute **dev_attrs; + + /* Pointer to dev handler's private data */ + void *devt_priv; + + /* Pointer to parent dev type in the sysfs hierarchy */ + struct scst_dev_type *parent; + struct module *module; - /* private: */ + /** Private, must be inited to 0 by memset() **/ /* list entry in scst_dev_type_list */ struct list_head dev_type_list_entry; +#ifdef CONFIG_SCST_PROC /* The pointer to the /proc directory entry */ struct proc_dir_entry *proc_dev_type_root; +#endif + + unsigned int devt_kobj_initialized:1; + + struct kobject devt_kobj; /* main handlers/driver */ + + /* To wait until devt_kobj released */ + struct completion devt_kobj_release_compl; }; struct scst_tgt { @@ -992,6 +1096,8 @@ struct scst_tgt { struct scst_tgt_template *tgtt; /* corresponding target template */ + struct scst_acg *default_acg; /* The default acg for this target. */ + /* * Maximum SG table size. Needed here, since different cards on the * same target template can have different SG table limitations. @@ -1017,11 +1123,26 @@ struct scst_tgt { /* Used to wait until session finished to unregister */ wait_queue_head_t unreg_waitQ; +#ifdef CONFIG_SCST_PROC /* Device number in /proc */ int proc_num; +#endif + /* Name of the target */ + char *tgt_name; + +#ifdef CONFIG_SCST_PROC /* Name on the default security group ("Default_target_name") */ char *default_group_name; +#endif + + /* Set if tgt_kobj was initialized */ + unsigned int tgt_kobj_initialized:1; + + struct kobject tgt_kobj; /* main targets/target kobject */ + struct kobject *tgt_sess_kobj; /* target/sessions/ */ + struct kobject *tgt_luns_kobj; /* target/luns/ */ + struct kobject *tgt_ini_grp_kobj; /* target/ini_groups/ */ }; /* Hash size and hash fn for hash based lun translation */ @@ -1144,6 +1265,11 @@ struct scst_session { /* Used if scst_unregister_session() called in wait mode */ struct completion *shutdown_compl; + /* Set if sess_kobj was initialized */ + unsigned int sess_kobj_initialized:1; + + struct kobject sess_kobj; /* kobject for this struct */ + /* * Functions and data for user callbacks from scst_register_session() * and scst_unregister_session() @@ -1636,7 +1762,7 @@ struct scst_device { int virt_id; /* Pointer to virtual device name, for convenience only */ - const char *virt_name; + char *virt_name; /* List entry in global devices list */ struct list_head dev_list_entry; @@ -1655,6 +1781,15 @@ struct scst_device { /* Device number */ int dev_num; + + /* Set if tgt_kobj was initialized */ + unsigned int dev_kobj_initialized:1; + + struct kobject dev_kobj; /* kobject for this struct */ + struct kobject *dev_exp_kobj; /* exported groups */ + + /* Export number in the dev's sysfs list. Protected by scst_mutex */ + int dev_exported_lun_num; }; /* @@ -1766,15 +1901,25 @@ struct scst_tgt_dev { */ struct scst_acg_dev { struct scst_device *dev; /* corresponding device */ - uint64_t lun; /* device's LUN in this acg */ - unsigned int rd_only:1; /* if != 0, then read only */ - struct scst_acg *acg; /* parent acg */ - /* list entry in dev->dev_acg_dev_list */ + uint64_t lun; /* device's LUN in this acg */ + + /* If set, the corresponding LU is read only */ + unsigned int rd_only:1; + + /* Set if acg_dev_kobj was initialized */ + unsigned int acg_dev_kobj_initialized:1; + + struct scst_acg *acg; /* parent acg */ + + /* List entry in dev->dev_acg_dev_list */ struct list_head dev_acg_dev_list_entry; - /* list entry in acg->acg_dev_list */ + /* List entry in acg->acg_dev_list */ struct list_head acg_dev_list_entry; + + /* kobject for this structure */ + struct kobject acg_dev_kobj; }; /* @@ -1797,8 +1942,10 @@ struct scst_acg { /* Name of this acg */ const char *acg_name; +#ifdef CONFIG_SCST_PROC /* The pointer to the /proc directory entry */ struct proc_dir_entry *acg_proc_root; +#endif }; /* @@ -1895,8 +2042,7 @@ void scst_unregister(struct scst_tgt *tgt); * this function will block until the session registration is completed. * initiator_name - remote initiator's name, any NULL-terminated string, * e.g. iSCSI name, which used as the key to found appropriate access - * control group. Could be NULL, then "default" group is used. - * The groups are set up via /proc interface. + * control group. Could be NULL, then the default target's LUNs are used. * data - any target driver supplied data * result_fn - pointer to the function that will be * asynchronously called when session initialization finishes. @@ -2852,7 +2998,7 @@ static inline struct scatterlist *sg_next(struct scatterlist *sg) return sg; } -#endif +#endif /* __BACKPORT_LINUX_SCATTERLIST_H_TO_2_6_23__ */ #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) */ @@ -3012,49 +3158,44 @@ int scst_get_cmd_abnormal_done_state(const struct scst_cmd *cmd); */ void scst_set_cmd_abnormal_done_state(struct scst_cmd *cmd); +struct scst_trace_log { + unsigned int val; + const char *token; +}; + +#ifdef CONFIG_SCST_PROC + /* * Returns target driver's root entry in SCST's /proc hierarchy. * The driver can create own files/directories here, which should * be deleted in the driver's release(). */ -static inline struct proc_dir_entry *scst_proc_get_tgt_root( - struct scst_tgt_template *vtt) -{ - return vtt->proc_tgt_root; -} +struct proc_dir_entry *scst_proc_get_tgt_root( + struct scst_tgt_template *vtt); /* * Returns device handler's root entry in SCST's /proc hierarchy. * The driver can create own files/directories here, which should * be deleted in the driver's detach()/release(). */ -static inline struct proc_dir_entry *scst_proc_get_dev_type_root( - struct scst_dev_type *dtt) -{ - return dtt->proc_dev_type_root; -} +struct proc_dir_entry *scst_proc_get_dev_type_root( + struct scst_dev_type *dtt); /** ** Two library functions and the structure to help the drivers ** that use scst_debug.* facilities manage "trace_level" /proc entry. ** The functions service "standard" log levels and allow to work ** with driver specific levels, which should be passed inside as - ** NULL-terminated array of struct scst_proc_log's, where: + ** NULL-terminated array of struct scst_trace_log's, where: ** - val - the level's numeric value ** - token - its string representation **/ -struct scst_proc_log { - unsigned int val; - const char *token; -}; - int scst_proc_log_entry_read(struct seq_file *seq, unsigned long log_level, - const struct scst_proc_log *tbl); - + const struct scst_trace_log *tbl); int scst_proc_log_entry_write(struct file *file, const char __user *buf, unsigned long length, unsigned long *log_level, - unsigned long default_level, const struct scst_proc_log *tbl); + unsigned long default_level, const struct scst_trace_log *tbl); /* * helper data structure and function to create proc entry. @@ -3080,6 +3221,56 @@ struct proc_dir_entry *scst_create_proc_entry(struct proc_dir_entry *root, .release = single_release, \ }, +#else /* CONFIG_SCST_PROC */ + +/* + * Returns target driver's root sysfs kobject. + * The driver can create own files/directories/links here. + */ +static inline struct kobject *scst_sysfs_get_tgtt_kobj( + struct scst_tgt_template *tgtt) +{ + return &tgtt->tgtt_kobj; +} + +/* + * Returns target's root sysfs kobject. + * The driver can create own files/directories/links here. + */ +static inline struct kobject *scst_sysfs_get_tgt_kobj( + struct scst_tgt *tgt) +{ + return &tgt->tgt_kobj; +} + +/* + * Returns device handler's root sysfs kobject. + * The driver can create own files/directories/links here. + */ +static inline struct kobject *scst_sysfs_get_devt_kobj( + struct scst_dev_type *devt) +{ + return &devt->devt_kobj; +} + +/* + * Returns device's root sysfs kobject. + * The driver can create own files/directories/links here. + */ +static inline struct kobject *scst_sysfs_get_dev_kobj( + struct scst_device *dev) +{ + return &dev->dev_kobj; +} + +#endif /* CONFIG_SCST_PROC */ + +/* Returns target name */ +static inline const char *scst_get_tgt_name(const struct scst_tgt *tgt) +{ + return tgt->tgt_name; +} + /* * Adds and deletes (stops) num of global SCST's threads. Returns 0 on * success, error code otherwise. diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index ab738f212..4cd87f137 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -131,6 +131,11 @@ enum scst_cmd_queue_type { *************************************************************/ #define SCST_DEFAULT_ACG_NAME "Default" +/************************************************************* + ** Default suffix for targets with NULL names + *************************************************************/ +#define SCST_DEFAULT_TGT_NAME_SUFFIX "_target_" + /************************************************************* ** Sense manipulation and examination *************************************************************/ @@ -331,4 +336,9 @@ enum scst_cmd_queue_type { #define SCST_MAX_OTHER_TIMEOUT (14000 * HZ) +/************************************************************* + ** Misc constants + *************************************************************/ +#define SCST_SYSFS_BLOCK_SIZE (PAGE_SIZE - 64) + #endif /* __SCST_CONST_H */ diff --git a/scst/src/Makefile b/scst/src/Makefile index 42b63e769..3ed9c02da 100644 --- a/scst/src/Makefile +++ b/scst/src/Makefile @@ -33,12 +33,12 @@ ifneq ($(PATCHLEVEL),) SCST_INC_DIR := $(SUBDIRS)/../include obj-m := scst.o -scst-objs := scst_main.o scst_targ.o scst_lib.o scst_mem.o scst_proc.o scst-y += scst_main.o scst-y += scst_targ.o scst-y += scst_lib.o scst-y += scst_proc.o +#scst-y += scst_sysfs.o scst-y += scst_mem.o scst-y += scst_debug.o obj-$(CONFIG_SCST) += scst.o dev_handlers/ @@ -149,4 +149,60 @@ clean: extraclean: clean -.PHONY: all install uninstall clean extraclean +debug2release: + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + cd $(DEV_HANDLERS_DIR) && $(MAKE) $@ + +release2debug: + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + cd $(DEV_HANDLERS_DIR) && $(MAKE) $@ + +debug2perf: + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + cd $(DEV_HANDLERS_DIR) && $(MAKE) $@ + +perf2debug: + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + cd $(DEV_HANDLERS_DIR) && $(MAKE) $@ + +disable_proc: + sed -c -i.aa s/"^#define CONFIG_SCST_PROC"/"\/* #define CONFIG_SCST_PROC *\/"/ ../include/scst.h + @cmp ../include/scst.h ../include/scst.h.aa >/dev/null; if [[ $$? = 0 ]]; then rm ../include/scst.h.aa; echo "sed failed"; false; fi + rm ../include/scst.h.aa + sed -c -i.aa s/"^scst-y += scst_proc.o"/"#scst\-y += scst_proc.o"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#scst\-y += scst_sysfs.o"/"scst\-y += scst_sysfs.o"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +enable_proc: + sed -c -i.aa s/"\/\* #define CONFIG_SCST_PROC \*\/"/"#define CONFIG_SCST_PROC"/ ../include/scst.h + @cmp ../include/scst.h ../include/scst.h.aa >/dev/null; if [[ $$? = 0 ]]; then rm ../include/scst.h.aa; echo "sed failed"; false; fi + rm ../include/scst.h.aa + sed -c -i.aa s/"#scst\-y += scst_proc.o"/"scst\-y += scst_proc.o"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^scst\-y += scst_sysfs.o"/"#scst\-y += scst_sysfs.o"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +.PHONY: all install uninstall clean extraclean debug2release release2debug debug2perf perf2debug disable_proc enable_proc diff --git a/scst/src/dev_handlers/Makefile b/scst/src/dev_handlers/Makefile index fd87ff80b..f7f76db05 100644 --- a/scst/src/dev_handlers/Makefile +++ b/scst/src/dev_handlers/Makefile @@ -82,4 +82,36 @@ clean: extraclean: clean -.PHONY: all install uninstall clean extraclean +debug2release: + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +release2debug: + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +debug2perf: + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +perf2debug: + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +.PHONY: all install uninstall clean extraclean debug2release release2debug debug2perf perf2debug diff --git a/scst/src/dev_handlers/scst_cdrom.c b/scst/src/dev_handlers/scst_cdrom.c index cef3f9f23..80f95ea65 100644 --- a/scst/src/dev_handlers/scst_cdrom.c +++ b/scst/src/dev_handlers/scst_cdrom.c @@ -28,17 +28,6 @@ #define CDROM_NAME "dev_cdrom" -#define CDROM_TYPE { \ - .name = CDROM_NAME, \ - .type = TYPE_ROM, \ - .parse_atomic = 1, \ - .dev_done_atomic = 1, \ - .attach = cdrom_attach, \ - .detach = cdrom_detach, \ - .parse = cdrom_parse, \ - .dev_done = cdrom_done, \ -} - #define CDROM_DEF_BLOCK_SHIFT 11 struct cdrom_params { @@ -50,7 +39,20 @@ static void cdrom_detach(struct scst_device *); static int cdrom_parse(struct scst_cmd *); static int cdrom_done(struct scst_cmd *); -static struct scst_dev_type cdrom_devtype = CDROM_TYPE; +static struct scst_dev_type cdrom_devtype = { + .name = CDROM_NAME, + .type = TYPE_ROM, + .parse_atomic = 1, + .dev_done_atomic = 1, + .attach = cdrom_attach, + .detach = cdrom_detach, + .parse = cdrom_parse, + .dev_done = cdrom_done, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; /************************************************************** * Function: cdrom_attach @@ -150,9 +152,7 @@ static int cdrom_attach(struct scst_device *dev) res = scst_obtain_device_parameters(dev); if (res != 0) { PRINT_ERROR("Failed to obtain control parameters for device " - "%d:%d:%d:%d", dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun); + "%s", dev->virt_name); goto out_free_buf; } @@ -274,23 +274,29 @@ static int __init cdrom_init(void) if (res < 0) goto out; +#ifdef CONFIG_SCST_PROC res = scst_dev_handler_build_std_proc(&cdrom_devtype); if (res != 0) goto out_err; +#endif out: TRACE_EXIT(); return res; +#ifdef CONFIG_SCST_PROC out_err: scst_unregister_dev_driver(&cdrom_devtype); goto out; +#endif } static void __exit cdrom_exit(void) { TRACE_ENTRY(); +#ifdef CONFIG_SCST_PROC scst_dev_handler_destroy_std_proc(&cdrom_devtype); +#endif scst_unregister_dev_driver(&cdrom_devtype); TRACE_EXIT(); return; diff --git a/scst/src/dev_handlers/scst_changer.c b/scst/src/dev_handlers/scst_changer.c index 5c5fdf82c..3d0b07277 100644 --- a/scst/src/dev_handlers/scst_changer.c +++ b/scst/src/dev_handlers/scst_changer.c @@ -27,17 +27,6 @@ #define CHANGER_NAME "dev_changer" -#define CHANGER_TYPE { \ - .name = CHANGER_NAME, \ - .type = TYPE_MEDIUM_CHANGER, \ - .parse_atomic = 1, \ -/* .dev_done_atomic = 1, */ \ - .attach = changer_attach, \ -/* .detach = changer_detach, */ \ - .parse = changer_parse, \ -/* .dev_done = changer_done */ \ -} - #define CHANGER_RETRIES 2 #define READ_CAP_LEN 8 @@ -46,7 +35,20 @@ static int changer_attach(struct scst_device *); static int changer_parse(struct scst_cmd *); /* static int changer_done(struct scst_cmd *); */ -static struct scst_dev_type changer_devtype = CHANGER_TYPE; +static struct scst_dev_type changer_devtype = { + .name = CHANGER_NAME, + .type = TYPE_MEDIUM_CHANGER, + .parse_atomic = 1, +/* .dev_done_atomic = 1, */ + .attach = changer_attach, +/* .detach = changer_detach, */ + .parse = changer_parse, +/* .dev_done = changer_done */ +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; /************************************************************** * Function: changer_attach @@ -101,9 +103,7 @@ static int changer_attach(struct scst_device *dev) res = scst_obtain_device_parameters(dev); if (res != 0) { PRINT_ERROR("Failed to obtain control parameters for device " - "%d:%d:%d:%d", dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun); + "%s", dev->virt_name); goto out; } @@ -202,23 +202,28 @@ static int __init changer_init(void) if (res < 0) goto out; +#ifdef CONFIG_SCST_PROC res = scst_dev_handler_build_std_proc(&changer_devtype); if (res != 0) goto out_err; +#endif out: TRACE_EXIT_RES(res); return res; - +#ifdef CONFIG_SCST_PROC out_err: scst_unregister_dev_driver(&changer_devtype); goto out; +#endif } static void __exit changer_exit(void) { TRACE_ENTRY(); +#ifdef CONFIG_SCST_PROC scst_dev_handler_destroy_std_proc(&changer_devtype); +#endif scst_unregister_dev_driver(&changer_devtype); TRACE_EXIT(); return; diff --git a/scst/src/dev_handlers/scst_dev_handler.h b/scst/src/dev_handlers/scst_dev_handler.h index d13608d21..58fce8650 100644 --- a/scst/src/dev_handlers/scst_dev_handler.h +++ b/scst/src/dev_handlers/scst_dev_handler.h @@ -2,8 +2,6 @@ #define __SCST_DEV_HANDLER_H #include -#include -#include #include #include "scst_debug.h" @@ -12,8 +10,6 @@ #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) -#define DEV_HANDLER_LOG_ENTRY_NAME "trace_level" - #ifdef CONFIG_SCST_DEBUG #define SCST_DEFAULT_DEV_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_PID | \ TRACE_LINE | TRACE_FUNCTION | TRACE_MGMT | TRACE_MGMT_MINOR | \ @@ -26,6 +22,10 @@ static unsigned long dh_trace_flag = SCST_DEFAULT_DEV_LOG_FLAGS; #define trace_flag dh_trace_flag +#ifdef CONFIG_SCST_PROC + +#define DEV_HANDLER_LOG_ENTRY_NAME "trace_level" + #ifndef trace_log_tbl #define trace_log_tbl NULL #endif @@ -57,8 +57,13 @@ static ssize_t scst_dev_handler_proc_log_entry_write(struct file *file, TRACE_EXIT_RES(res); return res; } + +#endif /* CONFIG_SCST_PROC */ + #endif /* defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */ +#ifdef CONFIG_SCST_PROC + static int scst_dev_handler_build_std_proc(struct scst_dev_type *dev_type) { int res = 0; @@ -69,14 +74,20 @@ static int scst_dev_handler_build_std_proc(struct scst_dev_type *dev_type) root = scst_proc_get_dev_type_root(dev_type); if (root) { - /* create the proc file entry for the device */ - dev_handler_log_proc_data.data = (void *)dev_type->name; + /* Create the proc file entry for the device */ + /* Workaround to keep /proc ABI intact */ + const char *name; + if (strcmp(dev_type->name, "vdisk_fileio") == 0) + name = "vdisk"; + else + name = dev_type->name; + dev_handler_log_proc_data.data = (void *)name; p = scst_create_proc_entry(root, DEV_HANDLER_LOG_ENTRY_NAME, &dev_handler_log_proc_data); if (p == NULL) { PRINT_ERROR("Not enough memory to register dev " "handler %s entry %s in /proc", - dev_type->name, DEV_HANDLER_LOG_ENTRY_NAME); + name, DEV_HANDLER_LOG_ENTRY_NAME); res = -ENOMEM; goto out; } @@ -110,4 +121,6 @@ static struct scst_proc_data dev_handler_log_proc_data = { }; #endif +#endif /* CONFIG_SCST_PROC */ + #endif /* __SCST_DEV_HANDLER_H */ diff --git a/scst/src/dev_handlers/scst_disk.c b/scst/src/dev_handlers/scst_disk.c index ce5ea2c1c..ad30507ca 100644 --- a/scst/src/dev_handlers/scst_disk.c +++ b/scst/src/dev_handlers/scst_disk.c @@ -33,31 +33,6 @@ # define DISK_NAME "dev_disk" # define DISK_PERF_NAME "dev_disk_perf" -#define DISK_TYPE { \ - .name = DISK_NAME, \ - .type = TYPE_DISK, \ - .parse_atomic = 1, \ - .dev_done_atomic = 1, \ - .exec_atomic = 1, \ - .attach = disk_attach, \ - .detach = disk_detach, \ - .parse = disk_parse, \ - .dev_done = disk_done, \ -} - -#define DISK_PERF_TYPE { \ - .name = DISK_PERF_NAME, \ - .type = TYPE_DISK, \ - .parse_atomic = 1, \ - .dev_done_atomic = 1, \ - .exec_atomic = 1, \ - .attach = disk_attach, \ - .detach = disk_detach, \ - .parse = disk_parse, \ - .dev_done = disk_done, \ - .exec = disk_exec, \ -} - #define DISK_DEF_BLOCK_SHIFT 9 struct disk_params { @@ -70,8 +45,38 @@ static int disk_parse(struct scst_cmd *cmd); static int disk_done(struct scst_cmd *cmd); static int disk_exec(struct scst_cmd *cmd); -static struct scst_dev_type disk_devtype = DISK_TYPE; -static struct scst_dev_type disk_devtype_perf = DISK_PERF_TYPE; +static struct scst_dev_type disk_devtype = { + .name = DISK_NAME, + .type = TYPE_DISK, + .parse_atomic = 1, + .dev_done_atomic = 1, + .exec_atomic = 1, + .attach = disk_attach, + .detach = disk_detach, + .parse = disk_parse, + .dev_done = disk_done, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; + +static struct scst_dev_type disk_devtype_perf = { + .name = DISK_PERF_NAME, + .type = TYPE_DISK, + .parse_atomic = 1, + .dev_done_atomic = 1, + .exec_atomic = 1, + .attach = disk_attach, + .detach = disk_detach, + .parse = disk_parse, + .dev_done = disk_done, + .exec = disk_exec, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; static int __init init_scst_disk_driver(void) { @@ -85,31 +90,35 @@ static int __init init_scst_disk_driver(void) if (res < 0) goto out; - res = scst_dev_handler_build_std_proc(&disk_devtype); - if (res != 0) - goto out_unreg1; - disk_devtype_perf.module = THIS_MODULE; res = scst_register_dev_driver(&disk_devtype_perf); if (res < 0) - goto out_unreg1_err1; + goto out_unreg; + +#ifdef CONFIG_SCST_PROC + res = scst_dev_handler_build_std_proc(&disk_devtype); + if (res != 0) + goto out_unreg1; res = scst_dev_handler_build_std_proc(&disk_devtype_perf); if (res != 0) goto out_unreg2; +#endif out: TRACE_EXIT_RES(res); return res; +#ifdef CONFIG_SCST_PROC out_unreg2: - scst_dev_handler_destroy_std_proc(&disk_devtype_perf); - -out_unreg1_err1: scst_dev_handler_destroy_std_proc(&disk_devtype); out_unreg1: + scst_unregister_dev_driver(&disk_devtype_perf); +#endif + +out_unreg: scst_unregister_dev_driver(&disk_devtype); goto out; } @@ -117,10 +126,14 @@ out_unreg1: static void __exit exit_scst_disk_driver(void) { TRACE_ENTRY(); + +#ifdef CONFIG_SCST_PROC scst_dev_handler_destroy_std_proc(&disk_devtype_perf); - scst_unregister_dev_driver(&disk_devtype_perf); scst_dev_handler_destroy_std_proc(&disk_devtype); +#endif + scst_unregister_dev_driver(&disk_devtype_perf); scst_unregister_dev_driver(&disk_devtype); + TRACE_EXIT(); return; } @@ -226,9 +239,7 @@ static int disk_attach(struct scst_device *dev) res = scst_obtain_device_parameters(dev); if (res != 0) { PRINT_ERROR("Failed to obtain control parameters for device " - "%d:%d:%d:%d", dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun); + "%s", dev->virt_name); goto out_free_buf; } diff --git a/scst/src/dev_handlers/scst_modisk.c b/scst/src/dev_handlers/scst_modisk.c index 222dba66d..551263544 100644 --- a/scst/src/dev_handlers/scst_modisk.c +++ b/scst/src/dev_handlers/scst_modisk.c @@ -33,31 +33,6 @@ # define MODISK_NAME "dev_modisk" # define MODISK_PERF_NAME "dev_modisk_perf" -#define MODISK_TYPE { \ - .name = MODISK_NAME, \ - .type = TYPE_MOD, \ - .parse_atomic = 1, \ - .dev_done_atomic = 1, \ - .exec_atomic = 1, \ - .attach = modisk_attach, \ - .detach = modisk_detach, \ - .parse = modisk_parse, \ - .dev_done = modisk_done, \ -} - -#define MODISK_PERF_TYPE { \ - .name = MODISK_PERF_NAME, \ - .type = TYPE_MOD, \ - .parse_atomic = 1, \ - .dev_done_atomic = 1, \ - .exec_atomic = 1, \ - .attach = modisk_attach, \ - .detach = modisk_detach, \ - .parse = modisk_parse, \ - .dev_done = modisk_done, \ - .exec = modisk_exec, \ -} - #define MODISK_DEF_BLOCK_SHIFT 10 struct modisk_params { @@ -70,8 +45,38 @@ static int modisk_parse(struct scst_cmd *); static int modisk_done(struct scst_cmd *); static int modisk_exec(struct scst_cmd *); -static struct scst_dev_type modisk_devtype = MODISK_TYPE; -static struct scst_dev_type modisk_devtype_perf = MODISK_PERF_TYPE; +static struct scst_dev_type modisk_devtype = { + .name = MODISK_NAME, + .type = TYPE_MOD, + .parse_atomic = 1, + .dev_done_atomic = 1, + .exec_atomic = 1, + .attach = modisk_attach, + .detach = modisk_detach, + .parse = modisk_parse, + .dev_done = modisk_done, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; + +static struct scst_dev_type modisk_devtype_perf = { + .name = MODISK_PERF_NAME, + .type = TYPE_MOD, + .parse_atomic = 1, + .dev_done_atomic = 1, + .exec_atomic = 1, + .attach = modisk_attach, + .detach = modisk_detach, + .parse = modisk_parse, + .dev_done = modisk_done, + .exec = modisk_exec, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; static int __init init_scst_modisk_driver(void) { @@ -85,31 +90,35 @@ static int __init init_scst_modisk_driver(void) if (res < 0) goto out; - res = scst_dev_handler_build_std_proc(&modisk_devtype); - if (res != 0) - goto out_unreg1; - modisk_devtype_perf.module = THIS_MODULE; res = scst_register_dev_driver(&modisk_devtype_perf); if (res < 0) - goto out_unreg1_err1; + goto out_unreg; + +#ifdef CONFIG_SCST_PROC + res = scst_dev_handler_build_std_proc(&modisk_devtype); + if (res != 0) + goto out_unreg1; res = scst_dev_handler_build_std_proc(&modisk_devtype_perf); if (res != 0) goto out_unreg2; +#endif out: TRACE_EXIT_RES(res); return res; +#ifdef CONFIG_SCST_PROC out_unreg2: - scst_dev_handler_destroy_std_proc(&modisk_devtype_perf); - -out_unreg1_err1: scst_dev_handler_destroy_std_proc(&modisk_devtype); out_unreg1: + scst_unregister_dev_driver(&modisk_devtype_perf); +#endif + +out_unreg: scst_unregister_dev_driver(&modisk_devtype); goto out; } @@ -117,10 +126,14 @@ out_unreg1: static void __exit exit_scst_modisk_driver(void) { TRACE_ENTRY(); + +#ifdef CONFIG_SCST_PROC scst_dev_handler_destroy_std_proc(&modisk_devtype_perf); - scst_unregister_dev_driver(&modisk_devtype_perf); scst_dev_handler_destroy_std_proc(&modisk_devtype); +#endif + scst_unregister_dev_driver(&modisk_devtype_perf); scst_unregister_dev_driver(&modisk_devtype); + TRACE_EXIT(); return; } @@ -242,9 +255,7 @@ static int modisk_attach(struct scst_device *dev) res = scst_obtain_device_parameters(dev); if (res != 0) { PRINT_ERROR("Failed to obtain control parameters for device " - "%d:%d:%d:%d: %x", dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun, res); + "%s: %x", dev->virt_name, res); goto out_free_buf; } diff --git a/scst/src/dev_handlers/scst_processor.c b/scst/src/dev_handlers/scst_processor.c index 2caf640a4..55d491157 100644 --- a/scst/src/dev_handlers/scst_processor.c +++ b/scst/src/dev_handlers/scst_processor.c @@ -27,26 +27,28 @@ #define PROCESSOR_NAME "dev_processor" -#define PROCESSOR_TYPE { \ - .name = PROCESSOR_NAME, \ - .type = TYPE_PROCESSOR, \ - .parse_atomic = 1, \ -/* .dev_done_atomic = 1,*/ \ - .attach = processor_attach, \ -/* .detach = processor_detach,*/ \ - .parse = processor_parse, \ -/* .dev_done = processor_done*/ \ -} - -#define PROCESSOR_RETRIES 2 -#define READ_CAP_LEN 8 +#define PROCESSOR_RETRIES 2 +#define READ_CAP_LEN 8 static int processor_attach(struct scst_device *); /*static void processor_detach(struct scst_device *);*/ static int processor_parse(struct scst_cmd *); /*static int processor_done(struct scst_cmd *);*/ -static struct scst_dev_type processor_devtype = PROCESSOR_TYPE; +static struct scst_dev_type processor_devtype = { + .name = PROCESSOR_NAME, + .type = TYPE_PROCESSOR, + .parse_atomic = 1, +/* .dev_done_atomic = 1,*/ + .attach = processor_attach, +/* .detach = processor_detach,*/ + .parse = processor_parse, +/* .dev_done = processor_done*/ +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; /************************************************************** * Function: processor_attach @@ -101,9 +103,7 @@ static int processor_attach(struct scst_device *dev) res = scst_obtain_device_parameters(dev); if (res != 0) { PRINT_ERROR("Failed to obtain control parameters for device " - "%d:%d:%d:%d", dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun); + "%s", dev->virt_name); goto out; } @@ -202,23 +202,28 @@ static int __init processor_init(void) if (res < 0) goto out; +#ifdef CONFIG_SCST_PROC res = scst_dev_handler_build_std_proc(&processor_devtype); if (res != 0) goto out_err; +#endif out: TRACE_EXIT_RES(res); return res; - +#ifdef CONFIG_SCST_PROC out_err: scst_unregister_dev_driver(&processor_devtype); goto out; +#endif } static void __exit processor_exit(void) { TRACE_ENTRY(); +#ifdef CONFIG_SCST_PROC scst_dev_handler_destroy_std_proc(&processor_devtype); +#endif scst_unregister_dev_driver(&processor_devtype); TRACE_EXIT(); return; diff --git a/scst/src/dev_handlers/scst_raid.c b/scst/src/dev_handlers/scst_raid.c index 37e481c38..e08b902f8 100644 --- a/scst/src/dev_handlers/scst_raid.c +++ b/scst/src/dev_handlers/scst_raid.c @@ -27,26 +27,28 @@ #define RAID_NAME "dev_raid" -#define RAID_TYPE { \ - .name = RAID_NAME, \ - .type = TYPE_RAID, \ - .parse_atomic = 1, \ -/* .dev_done_atomic = 1,*/ \ - .attach = raid_attach, \ -/* .detach = raid_detach,*/ \ - .parse = raid_parse, \ -/* .dev_done = raid_done*/ \ -} - -#define RAID_RETRIES 2 -#define READ_CAP_LEN 8 +#define RAID_RETRIES 2 +#define READ_CAP_LEN 8 static int raid_attach(struct scst_device *); /* static void raid_detach(struct scst_device *); */ static int raid_parse(struct scst_cmd *); /* static int raid_done(struct scst_cmd *); */ -static struct scst_dev_type raid_devtype = RAID_TYPE; +static struct scst_dev_type raid_devtype = { + .name = RAID_NAME, + .type = TYPE_RAID, + .parse_atomic = 1, +/* .dev_done_atomic = 1,*/ + .attach = raid_attach, +/* .detach = raid_detach,*/ + .parse = raid_parse, +/* .dev_done = raid_done,*/ +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; /************************************************************** * Function: raid_attach @@ -101,9 +103,7 @@ static int raid_attach(struct scst_device *dev) res = scst_obtain_device_parameters(dev); if (res != 0) { PRINT_ERROR("Failed to obtain control parameters for device " - "%d:%d:%d:%d", dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun); + "%s", dev->virt_name); goto out; } @@ -202,23 +202,29 @@ static int __init raid_init(void) if (res < 0) goto out; +#ifdef CONFIG_SCST_PROC res = scst_dev_handler_build_std_proc(&raid_devtype); if (res != 0) goto out_err; +#endif out: TRACE_EXIT_RES(res); return res; +#ifdef CONFIG_SCST_PROC out_err: scst_unregister_dev_driver(&raid_devtype); goto out; +#endif } static void __exit raid_exit(void) { TRACE_ENTRY(); +#ifdef CONFIG_SCST_PROC scst_dev_handler_destroy_std_proc(&raid_devtype); +#endif scst_unregister_dev_driver(&raid_devtype); TRACE_EXIT(); return; diff --git a/scst/src/dev_handlers/scst_tape.c b/scst/src/dev_handlers/scst_tape.c index e2a320ef8..ffe4c8796 100644 --- a/scst/src/dev_handlers/scst_tape.c +++ b/scst/src/dev_handlers/scst_tape.c @@ -33,37 +33,12 @@ # define TAPE_NAME "dev_tape" # define TAPE_PERF_NAME "dev_tape_perf" -#define TAPE_TYPE { \ - .name = TAPE_NAME, \ - .type = TYPE_TAPE, \ - .parse_atomic = 1, \ - .dev_done_atomic = 1, \ - .exec_atomic = 1, \ - .attach = tape_attach, \ - .detach = tape_detach, \ - .parse = tape_parse, \ - .dev_done = tape_done, \ -} - -#define TAPE_PERF_TYPE { \ - .name = TAPE_PERF_NAME, \ - .type = TYPE_TAPE, \ - .parse_atomic = 1, \ - .dev_done_atomic = 1, \ - .exec_atomic = 1, \ - .attach = tape_attach, \ - .detach = tape_detach, \ - .parse = tape_parse, \ - .dev_done = tape_done, \ - .exec = tape_exec, \ -} - -#define TAPE_RETRIES 2 +#define TAPE_RETRIES 2 #define TAPE_DEF_BLOCK_SIZE 512 /* The fixed bit in READ/WRITE/VERIFY */ -#define SILI_BIT 2 +#define SILI_BIT 2 struct tape_params { int block_size; @@ -75,8 +50,38 @@ static int tape_parse(struct scst_cmd *); static int tape_done(struct scst_cmd *); static int tape_exec(struct scst_cmd *); -static struct scst_dev_type tape_devtype = TAPE_TYPE; -static struct scst_dev_type tape_devtype_perf = TAPE_PERF_TYPE; +static struct scst_dev_type tape_devtype = { + .name = TAPE_NAME, + .type = TYPE_TAPE, + .parse_atomic = 1, + .dev_done_atomic = 1, + .exec_atomic = 1, + .attach = tape_attach, + .detach = tape_detach, + .parse = tape_parse, + .dev_done = tape_done, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; + +static struct scst_dev_type tape_devtype_perf = { + .name = TAPE_PERF_NAME, + .type = TYPE_TAPE, + .parse_atomic = 1, + .dev_done_atomic = 1, + .exec_atomic = 1, + .attach = tape_attach, + .detach = tape_detach, + .parse = tape_parse, + .dev_done = tape_done, + .exec = tape_exec, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; static int __init init_scst_tape_driver(void) { @@ -90,31 +95,35 @@ static int __init init_scst_tape_driver(void) if (res < 0) goto out; - res = scst_dev_handler_build_std_proc(&tape_devtype); - if (res != 0) - goto out_unreg1; - tape_devtype_perf.module = THIS_MODULE; res = scst_register_dev_driver(&tape_devtype_perf); if (res < 0) - goto out_unreg1_err1; + goto out_unreg; + +#ifdef CONFIG_SCST_PROC + res = scst_dev_handler_build_std_proc(&tape_devtype); + if (res != 0) + goto out_unreg1; res = scst_dev_handler_build_std_proc(&tape_devtype_perf); if (res != 0) goto out_unreg2; +#endif out: TRACE_EXIT_RES(res); return res; +#ifdef CONFIG_SCST_PROC out_unreg2: - scst_dev_handler_destroy_std_proc(&tape_devtype_perf); - -out_unreg1_err1: scst_dev_handler_destroy_std_proc(&tape_devtype); out_unreg1: + scst_unregister_dev_driver(&tape_devtype_perf); +#endif + +out_unreg: scst_unregister_dev_driver(&tape_devtype); goto out; } @@ -122,10 +131,14 @@ out_unreg1: static void __exit exit_scst_tape_driver(void) { TRACE_ENTRY(); + +#ifdef CONFIG_SCST_PROC scst_dev_handler_destroy_std_proc(&tape_devtype_perf); - scst_unregister_dev_driver(&tape_devtype_perf); scst_dev_handler_destroy_std_proc(&tape_devtype); +#endif + scst_unregister_dev_driver(&tape_devtype_perf); scst_unregister_dev_driver(&tape_devtype); + TRACE_EXIT(); return; } @@ -226,9 +239,7 @@ static int tape_attach(struct scst_device *dev) res = scst_obtain_device_parameters(dev); if (res != 0) { PRINT_ERROR("Failed to obtain control parameters for device " - "%d:%d:%d:%d", dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun); + "%s", dev->virt_name); goto out_free_buf; } diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index 260bf1ba3..21b54adb3 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -180,8 +180,13 @@ static long dev_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static int dev_user_release(struct inode *inode, struct file *file); static int dev_user_exit_dev(struct scst_user_dev *dev); + +#ifdef CONFIG_SCST_PROC static int dev_user_read_proc(struct seq_file *seq, struct scst_dev_type *dev_type); +#endif + +static int dev_usr_parse(struct scst_cmd *cmd); /** Data **/ @@ -199,6 +204,19 @@ static const struct file_operations dev_user_fops = { .release = dev_user_release, }; +static struct scst_dev_type dev_user_devtype = { + .name = DEV_USER_NAME, + .type = -1, + .parse = dev_usr_parse, +#ifdef CONFIG_SCST_PROC + .read_proc = dev_user_read_proc, +#endif +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, +#endif +}; + static struct class *dev_user_sysfs_class; static DEFINE_SPINLOCK(dev_list_lock); @@ -2489,13 +2507,11 @@ static int dev_user_attach_tgt(struct scst_tgt_dev *tgt_dev) sizeof(ucmd->user_cmd.sess.initiator_name)-1); ucmd->user_cmd.sess.initiator_name[ sizeof(ucmd->user_cmd.sess.initiator_name)-1] = '\0'; - if (tgt_dev->sess->tgt->default_group_name != NULL) { - strncpy(ucmd->user_cmd.sess.target_name, - &tgt_dev->sess->tgt->default_group_name[sizeof(SCST_DEFAULT_ACG_NAME)], - sizeof(ucmd->user_cmd.sess.target_name)-1); - ucmd->user_cmd.sess.target_name[ - sizeof(ucmd->user_cmd.sess.target_name)-1] = '\0'; - } + strncpy(ucmd->user_cmd.sess.target_name, + tgt_dev->sess->tgt->tgt_name, + sizeof(ucmd->user_cmd.sess.target_name)-1); + ucmd->user_cmd.sess.target_name[ + sizeof(ucmd->user_cmd.sess.target_name)-1] = '\0'; TRACE_MGMT_DBG("Preparing ATTACH_SESS %p (h %d, sess_h %llx, LUN %llx, " "threads_num %d, rd_only %d, initiator %s, target %s)", @@ -2758,7 +2774,9 @@ static int dev_user_register_dev(struct file *file, dev->devtype.parse_atomic = 1; dev->devtype.exec_atomic = 0; /* no point to make it 1 */ dev->devtype.dev_done_atomic = 1; +#ifdef CONFIG_SCST_PROC dev->devtype.no_proc = 1; +#endif dev->devtype.attach = dev_user_attach; dev->devtype.detach = dev_user_detach; dev->devtype.attach_tgt = dev_user_attach_tgt; @@ -2767,6 +2785,8 @@ static int dev_user_register_dev(struct file *file, dev->devtype.on_free_cmd = dev_user_on_free_cmd; dev->devtype.task_mgmt_fn = dev_user_task_mgmt_fn; + dev->devtype.parent = &dev_user_devtype; + init_completion(&dev->cleanup_cmpl); dev->block = block; dev->def_block = block; @@ -3200,16 +3220,6 @@ static int dev_usr_parse(struct scst_cmd *cmd) return SCST_CMD_STATE_DEFAULT; } -/* Needed only for /proc support */ -#define USR_TYPE { \ - .name = DEV_USER_NAME, \ - .type = -1, \ - .parse = dev_usr_parse, \ - .read_proc = dev_user_read_proc, \ -} - -static struct scst_dev_type dev_user_devtype = USR_TYPE; - static int dev_user_exit_dev(struct scst_user_dev *dev) { TRACE_ENTRY(); @@ -3351,7 +3361,7 @@ out: return res; } - +#ifdef CONFIG_SCST_PROC /* * Called when a file in the /proc/scsi_tgt/scst_user is read */ @@ -3389,6 +3399,7 @@ static int dev_user_read_proc(struct seq_file *seq, struct scst_dev_type *dev_ty TRACE_EXIT_RES(res); return res; } +#endif /* CONFIG_SCST_PROC */ static inline int test_cleanup_list(void) { @@ -3522,16 +3533,22 @@ static int __init init_scst_user(void) if (res < 0) goto out_cache1; +#ifdef CONFIG_SCST_PROC res = scst_dev_handler_build_std_proc(&dev_user_devtype); if (res != 0) goto out_unreg; +#endif dev_user_sysfs_class = class_create(THIS_MODULE, DEV_USER_NAME); if (IS_ERR(dev_user_sysfs_class)) { PRINT_ERROR("%s", "Unable create sysfs class for SCST user " "space handler"); res = PTR_ERR(dev_user_sysfs_class); +#ifdef CONFIG_SCST_PROC goto out_proc; +#else + goto out_unreg; +#endif } res = register_chrdev(DEV_USER_MAJOR, DEV_USER_NAME, &dev_user_fops); @@ -3586,8 +3603,10 @@ out_chrdev: out_class: class_destroy(dev_user_sysfs_class); +#ifdef CONFIG_SCST_PROC out_proc: scst_dev_handler_destroy_std_proc(&dev_user_devtype); +#endif out_unreg: scst_unregister_dev_driver(&dev_user_devtype); @@ -3618,7 +3637,9 @@ static void __exit exit_scst_user(void) #endif class_destroy(dev_user_sysfs_class); +#ifdef CONFIG_SCST_PROC scst_dev_handler_destroy_std_proc(&dev_user_devtype); +#endif scst_unregister_virtual_dev_driver(&dev_user_devtype); kmem_cache_destroy(user_get_cmd_cachep); diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index c5917dd93..3694c4b5b 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -49,12 +48,14 @@ #define TRACE_ORDER 0x80000000 -static struct scst_proc_log vdisk_proc_local_trace_tbl[] = +static struct scst_trace_log vdisk_local_trace_tbl[] = { { TRACE_ORDER, "order" }, { 0, NULL } }; -#define trace_log_tbl vdisk_proc_local_trace_tbl +#define trace_log_tbl vdisk_local_trace_tbl + +#define VDISK_TRACE_TLB_HELP ", order" #endif @@ -64,7 +65,7 @@ static struct scst_proc_log vdisk_proc_local_trace_tbl[] = #define SCST_FIO_VENDOR "SCST_FIO" #define SCST_BIO_VENDOR "SCST_BIO" /* 4 byte ASCII Product Revision Level - left aligned */ -#define SCST_FIO_REV " 102" +#define SCST_FIO_REV " 200" #define MAX_USN_LEN (20+1) /* For '\0' */ @@ -90,11 +91,11 @@ static struct scst_proc_log vdisk_proc_local_trace_tbl[] = #define DEF_SECTORS 56 #define DEF_HEADS 255 #define LEN_MEM (32 * 1024) -#define VDISK_NAME "vdisk" -#define VCDROM_NAME "vcdrom" #define VDISK_NULLIO_SIZE (3LL*1024*1024*1024*1024/2) +#define VDEV_NONE_FILENAME "none" + #define DEF_TST SCST_CONTR_MODE_SEP_TASK_SETS /* * Since we can't control backstorage device's reordering, we have to always @@ -107,7 +108,9 @@ static struct scst_proc_log vdisk_proc_local_trace_tbl[] = #define DEF_DSENSE SCST_CONTR_MODE_FIXED_SENSE +#ifdef CONFIG_SCST_PROC #define VDISK_PROC_HELP "help" +#endif static unsigned int random_values[256] = { 9862592UL, 3744545211UL, 2348289082UL, 4036111983UL, @@ -176,6 +179,8 @@ static unsigned int random_values[256] = { 1056807896UL, 3525009458UL, 1174811641UL, 3049738746UL, }; +struct vdev_type; + struct scst_vdisk_dev { uint32_t block_size; uint64_t nblocks; @@ -185,7 +190,7 @@ struct scst_vdisk_dev { /* * This lock can be taken on both SIRQ and thread context, but in * all cases for each particular instance it's taken consistenly either - * on SIRQ or thread context. Mix of them is impossible. + * on SIRQ or thread context. Mix of them is forbidden. */ spinlock_t flags_lock; @@ -203,13 +208,16 @@ struct scst_vdisk_dev { unsigned int blockio:1; unsigned int cdrom_empty:1; unsigned int removable:1; + int virt_id; - char name[16+1]; /* Name of virtual device, + char name[16+1]; /* Name of the virtual device, must be <= SCSI Model + 1 */ char *file_name; /* File name */ char usn[MAX_USN_LEN]; struct scst_device *dev; struct list_head vdisk_dev_list_entry; + + const struct vdev_type *vdt; }; struct scst_vdisk_tgt_dev { @@ -229,8 +237,85 @@ struct scst_vdisk_thr { int iv_count; }; -static struct kmem_cache *vdisk_thr_cachep; -static struct kmem_cache *blockio_work_cachep; +/*** + *** We make virtual devices emulation in OOP-like fashion: vdev is + *** an abstract class serving SPC set of commands and some common management + *** tasks, vdisk - an SBC (abstract) class with fileio, blockio and nullio + *** end classes, vcdrom - an MMC device class. + *** + *** Unfortunately, C doesn't support encapsulation and inheritance, so we + *** have to open code it, which makes the code look much less beautiful, than + *** it should. + *** + *** Only part to of the work is done. The second half is ToDo. + ***/ + +/* VDEV sysfs actions */ +#define VDEV_ACTION_OPEN 0 +#define VDEV_ACTION_CLOSE 1 + +/* VDISK sysfs actions */ +#define VDISK_ACTION_RESYNC_SIZE 100 + +/* VCDROM sysfs actions */ +#define VCDROM_ACTION_CHANGE 100 + +struct vdev_type_funcs { + struct scst_vdisk_dev *(*vdev_create) (struct vdev_type *vdt); + void (*vdev_destroy) (struct scst_vdisk_dev *virt_dev); + + void (*vdev_init) (struct vdev_type *vdt, + struct scst_vdisk_dev *virt_dev); + void (*vdev_deinit) (struct scst_vdisk_dev *virt_dev); + + /* Both supposed to be called under scst_vdisk_mutex */ + int (*vdev_open) (struct vdev_type *vdt, char *p, const char *name); + int (*vdev_close) (struct vdev_type *vdt, const char *name); + + /* All 3 supposed to be called under scst_vdisk_mutex */ + void (*vdev_add) (struct scst_vdisk_dev *virt_dev); + void (*vdev_del) (struct scst_vdisk_dev *virt_dev); + struct scst_vdisk_dev *(*vdev_find) (const char *name); + + int (*parse_cmd) (struct vdev_type *vdt, char *p, int *action); + + /* Supposed to be called under scst_vdisk_mutex */ + int (*perform_cmd) (struct vdev_type *vdt, int action, char *p, + char *name); + + int (*parse_option) (struct scst_vdisk_dev *virt_dev, char *p); + + int (*pre_register) (struct scst_vdisk_dev *virt_dev); +}; + +struct vdev_type { + const char *vdt_name; + const char *help_string; + + struct scst_dev_type *vdt_devt; + + struct vdev_type_funcs vfns; +}; + +struct vdisk_fileio_type { + struct vdev_type parent_vdt; + struct vdev_type_funcs parent_vdt_vfns; +}; + +struct vdisk_blockio_type { + struct vdev_type parent_vdt; + struct vdev_type_funcs parent_vdt_vfns; +}; + +struct vdisk_nullio_type { + struct vdev_type parent_vdt; + struct vdev_type_funcs parent_vdt_vfns; +}; + +struct vcdrom_type { + struct vdev_type parent_vdt; + struct vdev_type_funcs parent_vdt_vfns; +}; #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29) #define DEF_NUM_THREADS 5 @@ -270,6 +355,7 @@ static void vdisk_exec_read_toc(struct scst_cmd *cmd); static void vdisk_exec_prevent_allow_medium_removal(struct scst_cmd *cmd); static int vdisk_fsync(struct scst_vdisk_thr *thr, loff_t loff, loff_t len, struct scst_cmd *cmd, struct scst_device *dev); +#ifdef CONFIG_SCST_PROC static int vdisk_read_proc(struct seq_file *seq, struct scst_dev_type *dev_type); static int vdisk_write_proc(char *buffer, char **start, off_t offset, @@ -278,114 +364,252 @@ static int vcdrom_read_proc(struct seq_file *seq, struct scst_dev_type *dev_type); static int vcdrom_write_proc(char *buffer, char **start, off_t offset, int length, int *eof, struct scst_dev_type *dev_type); +#endif static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd, struct scst_tgt_dev *tgt_dev); -/* - * Name of FILEIO vdisk can't be changed from "vdisk", since it is the name - * of the corresponding /proc/scsi_tgt entry, hence a part of user space ABI. - */ +/** SYSFS **/ -#define VDISK_TYPE { \ - .name = VDISK_NAME, \ - .type = TYPE_DISK, \ - .exec_sync = 1, \ - .threads_num = -1, \ - .parse_atomic = 1, \ - .exec_atomic = 0, \ - .dev_done_atomic = 1, \ - .attach = vdisk_attach, \ - .detach = vdisk_detach, \ - .attach_tgt = vdisk_attach_tgt, \ - .detach_tgt = vdisk_detach_tgt, \ - .parse = vdisk_parse, \ - .exec = vdisk_do_job, \ - .read_proc = vdisk_read_proc, \ - .write_proc = vdisk_write_proc, \ - .task_mgmt_fn = vdisk_task_mgmt_fn, \ -} +static ssize_t vdisk_mgmt_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_mgmt_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); -#define VDISK_BLK_TYPE { \ - .name = VDISK_NAME "_blk", \ - .type = TYPE_DISK, \ - .threads_num = 0, \ - .parse_atomic = 1, \ - .exec_atomic = 0, \ - .dev_done_atomic = 1, \ - .no_proc = 1, \ - .attach = vdisk_attach, \ - .detach = vdisk_detach, \ - .attach_tgt = vdisk_attach_tgt, \ - .detach_tgt = vdisk_detach_tgt, \ - .parse = vdisk_parse, \ - .exec = vdisk_do_job, \ - .task_mgmt_fn = vdisk_task_mgmt_fn, \ -} +struct kobj_attribute vdisk_mgmt_attr = + __ATTR(mgmt, S_IRUGO | S_IWUSR, vdisk_mgmt_show, vdisk_mgmt_store); -#define VDISK_NULL_TYPE { \ - .name = VDISK_NAME "_null", \ - .type = TYPE_DISK, \ - .threads_num = 0, \ - .parse_atomic = 1, \ - .exec_atomic = 1, \ - .dev_done_atomic = 1, \ - .no_proc = 1, \ - .attach = vdisk_attach, \ - .detach = vdisk_detach, \ - .attach_tgt = vdisk_attach_tgt, \ - .detach_tgt = vdisk_detach_tgt, \ - .parse = vdisk_parse, \ - .exec = vdisk_do_job, \ - .task_mgmt_fn = vdisk_task_mgmt_fn, \ -} +static const struct attribute *vdisk_attrs[] = { + &vdisk_mgmt_attr.attr, + NULL, +}; -#define VCDROM_TYPE { \ - .name = VCDROM_NAME, \ - .type = TYPE_ROM, \ - .exec_sync = 1, \ - .threads_num = -1, \ - .parse_atomic = 1, \ - .exec_atomic = 0, \ - .dev_done_atomic = 1, \ - .attach = vdisk_attach, \ - .detach = vdisk_detach, \ - .attach_tgt = vdisk_attach_tgt, \ - .detach_tgt = vdisk_detach_tgt, \ - .parse = vcdrom_parse, \ - .exec = vcdrom_exec, \ - .read_proc = vcdrom_read_proc, \ - .write_proc = vcdrom_write_proc, \ - .task_mgmt_fn = vdisk_task_mgmt_fn, \ -} +static ssize_t vdisk_sysfs_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_filename_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +static ssize_t vdisk_sysfs_resync_size_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count); + +static struct kobj_attribute vdisk_size_attr = + __ATTR(size, S_IRUGO, vdisk_sysfs_size_show, NULL); +static struct kobj_attribute vdisk_blocksize_attr = + __ATTR(block_size, S_IRUGO, vdisk_sysfs_blocksize_show, NULL); +static struct kobj_attribute vdisk_rd_only_attr = + __ATTR(read_only, S_IRUGO, vdisk_sysfs_rd_only_show, NULL); +static struct kobj_attribute vdisk_wt_attr = + __ATTR(write_through, S_IRUGO, vdisk_sysfs_wt_show, NULL); +static struct kobj_attribute vdisk_nv_cache_attr = + __ATTR(nv_cache, S_IRUGO, vdisk_sysfs_nv_cache_show, NULL); +static struct kobj_attribute vdisk_o_direct_attr = + __ATTR(o_direct, S_IRUGO, vdisk_sysfs_o_direct_show, NULL); +static struct kobj_attribute vdisk_removable_attr = + __ATTR(removable, S_IRUGO, vdisk_sysfs_removable_show, NULL); +static struct kobj_attribute vdisk_filename_attr = + __ATTR(filename, S_IRUGO, vdisk_sysfs_filename_show, NULL); +static struct kobj_attribute vdisk_resync_size_attr = + __ATTR(resync_size, S_IWUSR, NULL, vdisk_sysfs_resync_size_store); + +static const struct attribute *vdisk_fileio_attrs[] = { + &vdisk_size_attr.attr, + &vdisk_blocksize_attr.attr, + &vdisk_rd_only_attr.attr, + &vdisk_wt_attr.attr, + &vdisk_nv_cache_attr.attr, + &vdisk_o_direct_attr.attr, + &vdisk_removable_attr.attr, + &vdisk_filename_attr.attr, + &vdisk_resync_size_attr.attr, + NULL, +}; + +static const struct attribute *vdisk_blockio_attrs[] = { + &vdisk_size_attr.attr, + &vdisk_blocksize_attr.attr, + &vdisk_rd_only_attr.attr, + &vdisk_removable_attr.attr, + &vdisk_filename_attr.attr, + &vdisk_resync_size_attr.attr, + NULL, +}; + +static const struct attribute *vdisk_nullio_attrs[] = { + &vdisk_size_attr.attr, + &vdisk_blocksize_attr.attr, + &vdisk_rd_only_attr.attr, + &vdisk_removable_attr.attr, + NULL, +}; + +static const struct attribute *vcdrom_attrs[] = { + &vdisk_size_attr.attr, + &vdisk_removable_attr.attr, + &vdisk_filename_attr.attr, + NULL, +}; static DEFINE_MUTEX(scst_vdisk_mutex); + +/* Both protected by scst_vdisk_mutex */ static LIST_HEAD(vdisk_dev_list); static LIST_HEAD(vcdrom_dev_list); -static struct scst_dev_type vdisk_file_devtype = VDISK_TYPE; -static struct scst_dev_type vdisk_blk_devtype = VDISK_BLK_TYPE; -static struct scst_dev_type vdisk_null_devtype = VDISK_NULL_TYPE; -static struct scst_dev_type vcdrom_devtype = VCDROM_TYPE; +static struct vdisk_fileio_type fileio_type; +static struct kmem_cache *vdisk_thr_cachep; + +/* + * Be careful changing "name" field, since it is the name of the corresponding + * /sys/kernel/scst_tgt entry, hence a part of user space ABI. + */ + +static struct scst_dev_type vdisk_file_devtype = { + .name = "vdisk_fileio", + .type = TYPE_DISK, + .exec_sync = 1, + .threads_num = -1, + .parse_atomic = 1, + .exec_atomic = 0, + .dev_done_atomic = 1, + .attach = vdisk_attach, + .detach = vdisk_detach, + .attach_tgt = vdisk_attach_tgt, + .detach_tgt = vdisk_detach_tgt, + .parse = vdisk_parse, + .exec = vdisk_do_job, +#ifdef CONFIG_SCST_PROC + .read_proc = vdisk_read_proc, + .write_proc = vdisk_write_proc, +#endif + .task_mgmt_fn = vdisk_task_mgmt_fn, + .devt_attrs = vdisk_attrs, + .dev_attrs = vdisk_fileio_attrs, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, + .trace_tbl = vdisk_local_trace_tbl, + .trace_tbl_help = VDISK_TRACE_TLB_HELP, +#endif +}; + +static struct vdisk_blockio_type blockio_type; +static struct kmem_cache *blockio_work_cachep; + +static struct scst_dev_type vdisk_blk_devtype = { + .name = "vdisk_blockio", + .type = TYPE_DISK, + .threads_num = 0, + .parse_atomic = 1, + .exec_atomic = 0, + .dev_done_atomic = 1, +#ifdef CONFIG_SCST_PROC + .no_proc = 1, +#endif + .attach = vdisk_attach, + .detach = vdisk_detach, + .attach_tgt = vdisk_attach_tgt, + .detach_tgt = vdisk_detach_tgt, + .parse = vdisk_parse, + .exec = vdisk_do_job, + .task_mgmt_fn = vdisk_task_mgmt_fn, + .devt_attrs = vdisk_attrs, + .dev_attrs = vdisk_blockio_attrs, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, + .trace_tbl = vdisk_local_trace_tbl, + .trace_tbl_help = VDISK_TRACE_TLB_HELP, +#endif +}; + +static struct vdisk_blockio_type nullio_type; +static struct scst_dev_type vdisk_null_devtype = { + .name = "vdisk_nullio", + .type = TYPE_DISK, + .threads_num = 0, + .parse_atomic = 1, + .exec_atomic = 1, + .dev_done_atomic = 1, +#ifdef CONFIG_SCST_PROC + .no_proc = 1, +#endif + .attach = vdisk_attach, + .detach = vdisk_detach, + .attach_tgt = vdisk_attach_tgt, + .detach_tgt = vdisk_detach_tgt, + .parse = vdisk_parse, + .exec = vdisk_do_job, + .task_mgmt_fn = vdisk_task_mgmt_fn, + .devt_attrs = vdisk_attrs, + .dev_attrs = vdisk_nullio_attrs, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, + .trace_tbl = vdisk_local_trace_tbl, + .trace_tbl_help = VDISK_TRACE_TLB_HELP, +#endif +}; + +static struct vcdrom_type vcdrom_type; +static struct scst_dev_type vcdrom_devtype = { + .name = "vcdrom", + .type = TYPE_ROM, + .exec_sync = 1, + .threads_num = -1, + .parse_atomic = 1, + .exec_atomic = 0, + .dev_done_atomic = 1, + .attach = vdisk_attach, + .detach = vdisk_detach, + .attach_tgt = vdisk_attach_tgt, + .detach_tgt = vdisk_detach_tgt, + .parse = vcdrom_parse, + .exec = vcdrom_exec, +#ifdef CONFIG_SCST_PROC + .read_proc = vcdrom_read_proc, + .write_proc = vcdrom_write_proc, +#endif + .task_mgmt_fn = vdisk_task_mgmt_fn, + .devt_attrs = vdisk_attrs, + .dev_attrs = vcdrom_attrs, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + .default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS, + .trace_flags = &trace_flag, + .trace_tbl = vdisk_local_trace_tbl, + .trace_tbl_help = VDISK_TRACE_TLB_HELP, +#endif +}; static struct scst_vdisk_thr nullio_thr_data; +#ifdef CONFIG_SCST_PROC static char *vdisk_proc_help_string = "echo \"open|close|resync_size NAME [FILE_NAME [BLOCK_SIZE] " "[WRITE_THROUGH READ_ONLY O_DIRECT NULLIO NV_CACHE BLOCKIO]]\" " - ">/proc/scsi_tgt/" VDISK_NAME "/" VDISK_NAME "\n"; + ">/proc/scsi_tgt/vdisk/vdisk\n"; static char *vcdrom_proc_help_string = "echo \"open|change|close NAME [FILE_NAME]\" " - ">/proc/scsi_tgt/" VCDROM_NAME "/" VCDROM_NAME "\n"; + ">/proc/scsi_tgt/vcdrom/vcdrom\n"; +#endif static int scst_vdisk_ID; module_param_named(scst_vdisk_ID, scst_vdisk_ID, int, S_IRUGO); MODULE_PARM_DESC(scst_vdisk_ID, "SCST virtual disk subsystem ID"); - /************************************************************** - * Function: vdisk_open + * Function: vdev_open_fd * * Argument: * @@ -393,7 +617,7 @@ MODULE_PARM_DESC(scst_vdisk_ID, "SCST virtual disk subsystem ID"); * * Description: *************************************************************/ -static struct file *vdisk_open(const struct scst_vdisk_dev *virt_dev) +static struct file *vdev_open_fd(const struct scst_vdisk_dev *virt_dev) { int open_flags = 0; struct file *fd; @@ -553,15 +777,7 @@ static int vdisk_attach(struct scst_device *dev) } else virt_dev->file_size = 0; - if (dev->type == TYPE_DISK) { - virt_dev->nblocks = - virt_dev->file_size >> virt_dev->block_shift; - } else { - virt_dev->block_size = DEF_CDROM_BLOCKSIZE; - virt_dev->block_shift = DEF_CDROM_BLOCKSIZE_SHIFT; - virt_dev->nblocks = - virt_dev->file_size >> DEF_CDROM_BLOCKSIZE_SHIFT; - } + virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift; if (!virt_dev->cdrom_empty) { PRINT_INFO("Attached SCSI target virtual %s %s " @@ -666,7 +882,7 @@ static struct scst_vdisk_thr *vdisk_init_thr_data( } if (!virt_dev->cdrom_empty) { - res->fd = vdisk_open(virt_dev); + res->fd = vdev_open_fd(virt_dev); if (IS_ERR(res->fd)) { PRINT_ERROR("filp_open(%s) returned an error %ld", virt_dev->file_name, PTR_ERR(res->fd)); @@ -769,10 +985,10 @@ static int vdisk_do_job(struct scst_cmd *cmd) switch (cmd->queue_type) { case SCST_CMD_QUEUE_ORDERED: - TRACE(TRACE_ORDER, "ORDERED cmd %p", cmd); + TRACE(TRACE_ORDER, "ORDERED cmd %p (op %x)", cmd, cmd->cdb[0]); break; case SCST_CMD_QUEUE_HEAD_OF_QUEUE: - TRACE(TRACE_ORDER, "HQ cmd %p", cmd); + TRACE(TRACE_ORDER, "HQ cmd %p (op %x)", cmd, cmd->cdb[0]); break; default: break; @@ -1196,7 +1412,7 @@ static void vdisk_exec_inquiry(struct scst_cmd *cmd) } buf[0] = cmd->dev->type; /* type dev */ - if ((buf[0] == TYPE_ROM) || virt_dev->removable) + if (virt_dev->removable) buf[1] = 0x80; /* removable */ /* Vital Product */ if (cmd->cdb[1] & EVPD) { @@ -2692,20 +2908,6 @@ out: return; } -static inline struct scst_vdisk_dev *vdisk_alloc_dev(void) -{ - struct scst_vdisk_dev *dev; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of virtual " - "device failed"); - goto out; - } - spin_lock_init(&dev->flags_lock); -out: - return dev; -} - static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd, struct scst_tgt_dev *tgt_dev) { @@ -2733,6 +2935,1030 @@ static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd, return SCST_DEV_TM_NOT_COMPLETED; } +static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev) +{ + char buf[128]; + int i, j; + + i = snprintf(buf, sizeof(buf), "Registering virtual %s device %s ", + virt_dev->vdt->vdt_name, virt_dev->name); + j = i; + + if (virt_dev->wt_flag) + i += snprintf(&buf[i], sizeof(buf) - i, "(WRITE_THROUGH"); + + if (virt_dev->nv_cache) + i += snprintf(&buf[i], sizeof(buf) - i, "%sNV_CACHE", + (j == i) ? "(" : ", "); + + if (virt_dev->rd_only) + i += snprintf(&buf[i], sizeof(buf) - i, "%sREAD_ONLY", + (j == i) ? "(" : ", "); + + if (virt_dev->o_direct_flag) + i += snprintf(&buf[i], sizeof(buf) - i, "%sO_DIRECT", + (j == i) ? "(" : ", "); + + if (virt_dev->nullio) + i += snprintf(&buf[i], sizeof(buf) - i, "%sNULLIO", + (j == i) ? "(" : ", "); + + if (virt_dev->blockio) + i += snprintf(&buf[i], sizeof(buf) - i, "%sBLOCKIO", + (j == i) ? "(" : ", "); + + if (virt_dev->removable) + i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE", + (j == i) ? "(" : ", "); + + if (j == i) + PRINT_INFO("%s", buf); + else + PRINT_INFO("%s)", buf); + + return; +} + +/* scst_vdisk_mutex supposed to be held */ +static int __vdisk_resync_size(struct scst_vdisk_dev *virt_dev) +{ + loff_t file_size; + int res = 0; + + if (!virt_dev->nullio) { + res = vdisk_get_file_size(virt_dev->file_name, + virt_dev->blockio, &file_size); + if (res != 0) + goto out; + } else + file_size = VDISK_NULLIO_SIZE; + + if (file_size == virt_dev->file_size) { + PRINT_INFO("Size of virtual disk %s remained the same", + virt_dev->name); + goto out; + } + + res = scst_suspend_activity(true); + if (res != 0) + goto out; + + virt_dev->file_size = file_size; + virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift; + + scst_dev_del_all_thr_data(virt_dev->dev); + + PRINT_INFO("New size of SCSI target virtual disk %s " + "(fs=%lldMB, bs=%d, nblocks=%lld, cyln=%lld%s)", + virt_dev->name, virt_dev->file_size >> 20, + virt_dev->block_size, + (long long unsigned int)virt_dev->nblocks, + (long long unsigned int)virt_dev->nblocks/64/32, + virt_dev->nblocks < 64*32 ? " !WARNING! cyln less " + "than 1" : ""); + + scst_capacity_data_changed(virt_dev->dev); + + scst_resume_activity(); + +out: + return res; +} + +/* scst_vdisk_mutex supposed to be held */ +static int vdisk_resync_size(struct vdev_type *vdt, char *p, const char *name) +{ + struct scst_vdisk_dev *virt_dev; + int res; + + virt_dev = vdt->vfns.vdev_find(name); + if (virt_dev == NULL) { + PRINT_ERROR("Device %s not found", name); + res = -EINVAL; + goto out; + } + + res = __vdisk_resync_size(virt_dev); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static void vdev_init(struct vdev_type *vdt, struct scst_vdisk_dev *virt_dev) +{ + memset(virt_dev, 0, sizeof(*virt_dev)); + spin_lock_init(&virt_dev->flags_lock); + virt_dev->vdt = vdt; + + virt_dev->block_size = DEF_DISK_BLOCKSIZE; + virt_dev->block_shift = DEF_DISK_BLOCKSIZE_SHIFT; + + return; +} + +static struct scst_vdisk_dev *vdev_create(struct vdev_type *vdt) +{ + struct scst_vdisk_dev *virt_dev; + + virt_dev = kmalloc(sizeof(*virt_dev), GFP_KERNEL); + if (virt_dev == NULL) { + PRINT_ERROR("Allocation of virtual device %s failed", + vdt->vdt_name); + goto out; + } + + vdt->vfns.vdev_init(vdt, virt_dev); + +out: + return virt_dev; +} + +static void vdev_deinit(struct scst_vdisk_dev *virt_dev) +{ + kfree(virt_dev->file_name); + return; +} + +static void vdev_destroy(struct scst_vdisk_dev *virt_dev) +{ + virt_dev->vdt->vfns.vdev_deinit(virt_dev); + kfree(virt_dev); + return; +} + +static int vdisk_fileio_pre_register(struct scst_vdisk_dev *virt_dev) +{ + int res = 0; + + if ((virt_dev->file_name == NULL) || + (strcmp(virt_dev->file_name, VDEV_NONE_FILENAME) == 0)) { + PRINT_ERROR("%s", "File name required"); + res = -EINVAL; + goto out; + } + + if (virt_dev->rd_only && (virt_dev->wt_flag || virt_dev->nv_cache)) { + PRINT_ERROR("Write options on read only device %s", + virt_dev->name); + res = -EINVAL; + goto out; + } + +out: + return res; +} + +static int vdisk_blockio_pre_register(struct scst_vdisk_dev *virt_dev) +{ + int res = 0; + + if ((virt_dev->file_name == NULL) || + (strcmp(virt_dev->file_name, VDEV_NONE_FILENAME) == 0)) { + PRINT_ERROR("%s", "File name required"); + res = -EINVAL; + goto out; + } + +out: + return res; +} + +static void vdisk_blockio_init(struct vdev_type *vdt, + struct scst_vdisk_dev *virt_dev) +{ + struct vdisk_blockio_type *v; + + v = container_of(vdt, struct vdisk_blockio_type, parent_vdt); + + v->parent_vdt_vfns.vdev_init(vdt, virt_dev); + + virt_dev->blockio = 1; + return; +} + +static void vdisk_nullio_init(struct vdev_type *vdt, + struct scst_vdisk_dev *virt_dev) +{ + struct vdisk_nullio_type *v; + + v = container_of(vdt, struct vdisk_nullio_type, parent_vdt); + + v->parent_vdt_vfns.vdev_init(vdt, virt_dev); + + virt_dev->nullio = 1; + return; +} + +static int vdisk_parse_option(struct scst_vdisk_dev *virt_dev, char *p) +{ + int res = -EINVAL; + + TRACE_ENTRY(); + + if (!strncmp("READ_ONLY", p, 9)) { + res = 9; + virt_dev->rd_only = 1; + TRACE_DBG("%s", "READ_ONLY"); + } else if (!strncmp("REMOVABLE", p, 9)) { + res = 9; + virt_dev->removable = 1; + TRACE_DBG("%s", "REMOVABLE"); + } + + TRACE_EXIT_RES(res); + return res; +} + +static int vdisk_fileio_parse_option(struct scst_vdisk_dev *virt_dev, char *p) +{ + int res; + + TRACE_ENTRY(); + + res = vdisk_parse_option(virt_dev, p); + if (res >= 0) + goto out; + + if (!strncmp("WRITE_THROUGH", p, 13)) { + res = 13; + virt_dev->wt_flag = 1; + TRACE_DBG("%s", "WRITE_THROUGH"); + } else if (!strncmp("NV_CACHE", p, 8)) { + res = 8; + virt_dev->nv_cache = 1; + TRACE_DBG("%s", "NON-VOLATILE CACHE"); + } else if (!strncmp("O_DIRECT", p, 8)) { + res = 8; +#if 0 + virt_dev->o_direct_flag = 1; + TRACE_DBG("%s", "O_DIRECT"); +#else + PRINT_INFO("%s flag doesn't currently" + " work, ignoring it, use fileio_tgt " + "in O_DIRECT mode instead", "O_DIRECT"); +#endif + } + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* scst_vdisk_mutex supposed to be held */ +static int vdev_open(struct vdev_type *vdt, char *p, const char *name) +{ + int res = 0; + char *file_name; + struct scst_vdisk_dev *virt_dev; + size_t len; + + TRACE_ENTRY(); + + virt_dev = vdt->vfns.vdev_find(name); + if (virt_dev != NULL) { + PRINT_ERROR("Virtual device with name %s already exist", name); + res = -EINVAL; + goto out; + } + + while (isspace(*p) && *p != '\0') + p++; + file_name = p; + if ((*file_name == '/') || (strcmp(file_name, VDEV_NONE_FILENAME) == 0)) { + while (!isspace(*p) && *p != '\0') + p++; + *p++ = '\0'; + + TRACE_DBG("file name %s", file_name); + + while (isspace(*p) && *p != '\0') + p++; + } else { + TRACE_DBG("file name %s", "not specified"); + file_name = NULL; + } + + virt_dev = vdt->vfns.vdev_create(vdt); + if (virt_dev == NULL) { + TRACE(TRACE_OUT_OF_MEM, "Creation of virt_dev %s failed", name); + res = -ENOMEM; + goto out; + } + + if (isdigit(*p)) { + uint32_t block_size; + int block_shift; + char *pp; + + block_size = simple_strtoul(p, &pp, 0); + p = pp; + if ((*p != '\0') && !isspace(*p)) { + PRINT_ERROR("Parse error: \"%s\"", p); + res = -EINVAL; + goto out_free_vdev; + } + while (isspace(*p) && *p != '\0') + p++; + + block_shift = scst_calc_block_shift(block_size); + if (block_shift < 9) { + res = -EINVAL; + goto out_free_vdev; + } + + TRACE_DBG("block_size %d, block_shift %d", block_size, + block_shift); + + virt_dev->block_size = block_size; + virt_dev->block_shift = block_shift; + } + + while (*p != '\0') { + while (isspace(*p) && *p != '\0') + p++; + + if (vdt->vfns.parse_option != NULL) + res = vdt->vfns.parse_option(virt_dev, p); + else + res = -EINVAL; + + if (res < 0) { + PRINT_ERROR("Unknown option \"%s\"", p); + goto out_free_vdev; + } else if (res == 0) + break; + + p += res; + + if (!isspace(*p) && (*p != '\0')) { + PRINT_ERROR("Syntax error on %s", p); + goto out_free_vdev; + } + } + + strcpy(virt_dev->name, name); + + scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx", + vdisk_gen_dev_id_num(virt_dev)); + TRACE_DBG("usn %s", virt_dev->usn); + + if (file_name != NULL) { + len = strlen(file_name) + 1; + virt_dev->file_name = kmalloc(len, GFP_KERNEL); + if (virt_dev->file_name == NULL) { + TRACE(TRACE_OUT_OF_MEM, "Allocation of file_name %s " + "failed", file_name); + res = -ENOMEM; + goto out_free_vdev; + } + strncpy(virt_dev->file_name, file_name, len); + } + + if (vdt->vfns.pre_register != NULL) { + res = vdt->vfns.pre_register(virt_dev); + if (res != 0) + goto out_free_vpath; + } + + vdt->vfns.vdev_add(virt_dev); + + vdisk_report_registering(virt_dev); + virt_dev->virt_id = scst_register_virtual_device(vdt->vdt_devt, + virt_dev->name); + if (virt_dev->virt_id < 0) { + res = virt_dev->virt_id; + goto out_del; + } + + TRACE_DBG("Added virt_dev (name %s, file name %s, id %d, block size " + "%d) to vdisk_dev_list", virt_dev->name, virt_dev->file_name, + virt_dev->virt_id, virt_dev->block_size); + +out: + TRACE_EXIT_RES(res); + return res; + +out_del: + vdt->vfns.vdev_del(virt_dev); + +out_free_vpath: + kfree(virt_dev->file_name); + +out_free_vdev: + kfree(virt_dev); + goto out; +} + +/* scst_vdisk_mutex supposed to be held */ +static int vdev_close(struct vdev_type *vdt, const char *name) +{ + int res = 0; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + virt_dev = vdt->vfns.vdev_find(name); + if (virt_dev == NULL) { + PRINT_ERROR("Device %s not found", name); + res = -EINVAL; + goto out; + } + + scst_unregister_virtual_device(virt_dev->virt_id); + + PRINT_INFO("Virtual device %s unregistered", virt_dev->name); + TRACE_DBG("virt_id %d unregister", virt_dev->virt_id); + + vdt->vfns.vdev_del(virt_dev); + vdt->vfns.vdev_destroy(virt_dev); + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* scst_vdisk_mutex supposed to be held */ +static void vdev_del(struct scst_vdisk_dev *virt_dev) +{ + TRACE_ENTRY(); + + list_del(&virt_dev->vdisk_dev_list_entry); + + TRACE_EXIT(); + return; +} + +/* scst_vdisk_mutex supposed to be held */ +static void vdisk_add(struct scst_vdisk_dev *virt_dev) +{ + TRACE_ENTRY(); + + list_add_tail(&virt_dev->vdisk_dev_list_entry, &vdisk_dev_list); + + TRACE_EXIT(); + return; +} + +/* scst_vdisk_mutex supposed to be held */ +static struct scst_vdisk_dev *vdisk_find(const char *name) +{ + struct scst_vdisk_dev *res, *vv; + + TRACE_ENTRY(); + + res = NULL; + list_for_each_entry(vv, &vdisk_dev_list, vdisk_dev_list_entry) { + if (strcmp(vv->name, name) == 0) { + res = vv; + break; + } + } + + TRACE_EXIT_HRES((unsigned long)res); + return res; +} + +/* scst_vdisk_mutex supposed to be held */ +static void vcdrom_add(struct scst_vdisk_dev *virt_dev) +{ + TRACE_ENTRY(); + + list_add_tail(&virt_dev->vdisk_dev_list_entry, &vcdrom_dev_list); + + TRACE_EXIT(); + return; +} + +/* scst_vdisk_mutex supposed to be held */ +static struct scst_vdisk_dev *vcdrom_find(const char *name) +{ + struct scst_vdisk_dev *res, *vv; + + TRACE_ENTRY(); + + res = NULL; + list_for_each_entry(vv, &vcdrom_dev_list, vdisk_dev_list_entry) { + if (strcmp(vv->name, name) == 0) { + res = vv; + break; + } + } + + TRACE_EXIT_HRES((unsigned long)res); + return res; +} + +static int vdisk_parse_cmd(struct vdev_type *vdt, char *p, int *action) +{ + int res = -EINVAL; + + TRACE_ENTRY(); + + if (!strncmp("resync_size", p, 11)) { + res = 11; + p += res; + *action = VDISK_ACTION_RESYNC_SIZE; + } + + TRACE_EXIT_RES(res); + return res; +} + +/* scst_vdisk_mutex supposed to be held */ +static int vdisk_perform_cmd(struct vdev_type *vdt, int action, char *p, + char *name) +{ + int res = -EINVAL; + + TRACE_ENTRY(); + + if (action == VDISK_ACTION_RESYNC_SIZE) + res = vdisk_resync_size(vdt, p, name); + + TRACE_EXIT_RES(res); + return res; +} + +static void vcdrom_init(struct vdev_type *vdt, + struct scst_vdisk_dev *virt_dev) +{ + struct vcdrom_type *vt; + + vt = container_of(vdt, struct vcdrom_type, parent_vdt); + + vt->parent_vdt_vfns.vdev_init(vdt, virt_dev); + + virt_dev->rd_only = 1; + virt_dev->removable = 1; + + virt_dev->block_size = DEF_CDROM_BLOCKSIZE; + virt_dev->block_shift = DEF_CDROM_BLOCKSIZE_SHIFT; + + return; +} + +static int vcdrom_pre_register(struct scst_vdisk_dev *virt_dev) +{ + int res = 0; + + if ((virt_dev->file_name == NULL) || + (strcmp(virt_dev->file_name, VDEV_NONE_FILENAME) == 0)) + virt_dev->cdrom_empty = 1; + + if (virt_dev->block_size != DEF_CDROM_BLOCKSIZE) { + PRINT_WARNING("Block size %d for vortual device %s ignored", + virt_dev->block_size, virt_dev->name); + virt_dev->block_size = DEF_CDROM_BLOCKSIZE; + virt_dev->block_shift = DEF_CDROM_BLOCKSIZE_SHIFT; + } + + return res; +} + +/* scst_vdisk_mutex supposed to be held */ +static int vcdrom_change(struct vdev_type *vdt, char *p, const char *name) +{ + loff_t err; + struct scst_vdisk_dev *virt_dev; + char *file_name, *fn = NULL, *old_fn; + int len; + int res = 0; + + virt_dev = vdt->vfns.vdev_find(name); + if (virt_dev == NULL) { + PRINT_ERROR("Virtual device with name " + "%s not found", name); + res = -EINVAL; + goto out; + } + + while (isspace(*p) && *p != '\0') + p++; + file_name = p; + while (!isspace(*p) && *p != '\0') + p++; + *p++ = '\0'; + if (*file_name == '\0') { + virt_dev->cdrom_empty = 1; + TRACE_DBG("%s", "No media"); + } else if (*file_name != '/') { + PRINT_ERROR("File path \"%s\" is not " + "absolute", file_name); + res = -EINVAL; + goto out; + } else + virt_dev->cdrom_empty = 0; + + old_fn = virt_dev->file_name; + + if (!virt_dev->cdrom_empty) { + len = strlen(file_name) + 1; + fn = kmalloc(len, GFP_KERNEL); + if (fn == NULL) { + TRACE(TRACE_OUT_OF_MEM, "%s", + "Allocation of file_name failed"); + res = -ENOMEM; + goto out; + } + + strncpy(fn, file_name, len); + virt_dev->file_name = fn; + + res = vdisk_get_file_size(virt_dev->file_name, + virt_dev->blockio, &err); + if (res != 0) + goto out_free; + } else { + err = 0; + virt_dev->file_name = NULL; + } + + res = scst_suspend_activity(true); + if (res != 0) + goto out_free; + + if (virt_dev->prevent_allow_medium_removal) { + PRINT_ERROR("Prevent medium removal for " + "virtual device with name %s", name); + res = -EINVAL; + goto out_free_resume; + } + + virt_dev->file_size = err; + virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift; + if (!virt_dev->cdrom_empty) + virt_dev->media_changed = 1; + + scst_dev_del_all_thr_data(virt_dev->dev); + + if (!virt_dev->cdrom_empty) { + PRINT_INFO("Changed SCSI target virtual cdrom %s " + "(file=\"%s\", fs=%lldMB, bs=%d, nblocks=%lld," + " cyln=%lld%s)", virt_dev->name, virt_dev->file_name, + virt_dev->file_size >> 20, virt_dev->block_size, + (long long unsigned int)virt_dev->nblocks, + (long long unsigned int)virt_dev->nblocks/64/32, + virt_dev->nblocks < 64*32 ? " !WARNING! cyln less " + "than 1" : ""); + } else { + PRINT_INFO("Removed media from SCSI target virtual cdrom %s", + virt_dev->name); + } + + kfree(old_fn); + +out_resume: + scst_resume_activity(); + +out: + return res; + +out_free: + virt_dev->file_name = old_fn; + kfree(fn); + goto out; + +out_free_resume: + virt_dev->file_name = old_fn; + kfree(fn); + goto out_resume; +} + +static int vcdrom_parse_cmd(struct vdev_type *vdt, char *p, int *action) +{ + int res = -EINVAL; + + TRACE_ENTRY(); + + if (!strncmp("change", p, 6)) { + res = 6; + p += res; + *action = VCDROM_ACTION_CHANGE; + } + + TRACE_EXIT_RES(res); + return res; +} + +/* scst_vdisk_mutex supposed to be held */ +static int vcdrom_perform_cmd(struct vdev_type *vdt, int action, char *p, + char *name) +{ + int res = -EINVAL; + + TRACE_ENTRY(); + + if (action == VCDROM_ACTION_CHANGE) + res = vcdrom_change(vdt, p, name); + + TRACE_EXIT_RES(res); + return res; +} + +static int vdisk_mgmt_cmd(const char *buffer, int length, + struct vdev_type *vdt) +{ + int res = 0, action; + char *p, *name, *i_buf; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + if ((length == 0) || (buffer == NULL) || (buffer[0] == '\0')) + goto out; + + i_buf = kmalloc(length+1, GFP_KERNEL); + if (i_buf == NULL) { + PRINT_ERROR("Unable to alloc intermediate buffer with size %d", + length+1); + goto out; + } + + memcpy(i_buf, buffer, length); + i_buf[length] = '\0'; + + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) { + res = -EINTR; + goto out_free; + } + + p = i_buf; + if (p[strlen(p) - 1] == '\n') + p[strlen(p) - 1] = '\0'; + if (!strncmp("open", p, 4)) { + p += 4; + action = VDEV_ACTION_OPEN; + } else if (!strncmp("close", p, 5)) { + p += 5; + action = VDEV_ACTION_CLOSE; + } else { + int n; + n = vdt->vfns.parse_cmd(vdt, p, &action); + if (n > 0) + p += n; + else { + PRINT_ERROR("Unknown action \"%s\"", p); + res = -EINVAL; + goto out_up; + } + } + + if (!isspace(*p)) { + PRINT_ERROR("Syntax error on %s", p); + res = -EINVAL; + goto out_up; + } + + while (isspace(*p) && *p != '\0') + p++; + name = p; + while (!isspace(*p) && *p != '\0') + p++; + *p++ = '\0'; + if (*name == '\0') { + PRINT_ERROR("%s", "Name required"); + res = -EINVAL; + goto out_up; + } else if (strlen(name) >= sizeof(virt_dev->name)) { + PRINT_ERROR("Name is too long (max %zd " + "characters)", sizeof(virt_dev->name)-1); + res = -EINVAL; + goto out_up; + } + + if (action == VDEV_ACTION_OPEN) { + res = vdt->vfns.vdev_open(vdt, p, name); + if (res != 0) + goto out_up; + } else if (action == VDEV_ACTION_CLOSE) { + res = vdt->vfns.vdev_close(vdt, name); + if (res != 0) + goto out_up; + } else { + res = vdt->vfns.perform_cmd(vdt, action, p, name); + if (res != 0) + goto out_up; + } + res = length; + +out_up: + mutex_unlock(&scst_vdisk_mutex); + +out_free: + kfree(i_buf); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t vdisk_mgmt_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_dev_type *devt; + struct vdev_type *vdt; + + devt = container_of(kobj, struct scst_dev_type, devt_kobj); + vdt = (struct vdev_type *)devt->devt_priv; + + return scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, vdt->help_string); +} + +static ssize_t vdisk_mgmt_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + struct scst_dev_type *devt; + int res = count; + + devt = container_of(kobj, struct scst_dev_type, devt_kobj); + + res = vdisk_mgmt_cmd(buf, count, (struct vdev_type *)devt->devt_priv); + + return res; +} + +static ssize_t vdisk_sysfs_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv; + + pos = sprintf(buf, "%lld\n", virt_dev->file_size); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv; + + pos = sprintf(buf, "%d\n", (int)virt_dev->block_size); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv; + + pos = sprintf(buf, "%d\n", virt_dev->rd_only ? 1 : 0); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv; + + pos = sprintf(buf, "%d\n", virt_dev->wt_flag ? 1 : 0); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv; + + pos = sprintf(buf, "%d\n", virt_dev->nv_cache ? 1 : 0); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv; + + pos = sprintf(buf, "%d\n", virt_dev->o_direct_flag ? 1 : 0); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv; + + pos = sprintf(buf, "%d\n", virt_dev->removable ? 1 : 0); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdisk_sysfs_filename_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv; + + pos = sprintf(buf, "%s\n", virt_dev->file_name); + + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t vdisk_sysfs_resync_size_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + struct scst_device *dev; + struct scst_vdisk_dev *virt_dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + virt_dev = (struct scst_vdisk_dev *)dev->dh_priv; + + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) { + res = -EINTR; + goto out; + } + + res = __vdisk_resync_size(virt_dev); + if (res != 0) + goto out_unlock; + + res = count; + +out_unlock: + mutex_unlock(&scst_vdisk_mutex); + +out: + TRACE_EXIT_RES(res); + return res; +} + +#ifdef CONFIG_SCST_PROC + +/* + * ProcFS + */ + /* * Called when a file in the /proc/VDISK_NAME/VDISK_NAME is read */ @@ -2802,105 +4028,11 @@ out: return res; } -static void vdisk_report_registering(const char *type, - const struct scst_vdisk_dev *virt_dev) -{ - char buf[128]; - int i, j; - - i = snprintf(buf, sizeof(buf), "Registering virtual %s device %s ", - type, virt_dev->name); - j = i; - - if (virt_dev->wt_flag) - i += snprintf(&buf[i], sizeof(buf) - i, "(WRITE_THROUGH"); - - if (virt_dev->nv_cache) - i += snprintf(&buf[i], sizeof(buf) - i, "%sNV_CACHE", - (j == i) ? "(" : ", "); - - if (virt_dev->rd_only) - i += snprintf(&buf[i], sizeof(buf) - i, "%sREAD_ONLY", - (j == i) ? "(" : ", "); - - if (virt_dev->o_direct_flag) - i += snprintf(&buf[i], sizeof(buf) - i, "%sO_DIRECT", - (j == i) ? "(" : ", "); - - if (virt_dev->nullio) - i += snprintf(&buf[i], sizeof(buf) - i, "%sNULLIO", - (j == i) ? "(" : ", "); - - if (virt_dev->blockio) - i += snprintf(&buf[i], sizeof(buf) - i, "%sBLOCKIO", - (j == i) ? "(" : ", "); - - if (virt_dev->removable) - i += snprintf(&buf[i], sizeof(buf) - i, "%sREMOVABLE", - (j == i) ? "(" : ", "); - - if (j == i) - PRINT_INFO("%s", buf); - else - PRINT_INFO("%s)", buf); - - return; -} - -/* scst_vdisk_mutex supposed to be held */ -static int vdisk_resync_size(struct scst_vdisk_dev *virt_dev) -{ - loff_t file_size; - int res = 0; - - if (!virt_dev->nullio) { - res = vdisk_get_file_size(virt_dev->file_name, - virt_dev->blockio, &file_size); - if (res != 0) - goto out; - } else - file_size = VDISK_NULLIO_SIZE; - - if (file_size == virt_dev->file_size) { - PRINT_INFO("Size of virtual disk %s remained the same", - virt_dev->name); - goto out; - } - - res = scst_suspend_activity(true); - if (res != 0) - goto out; - - virt_dev->file_size = file_size; - virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift; - - scst_dev_del_all_thr_data(virt_dev->dev); - - PRINT_INFO("New size of SCSI target virtual disk %s " - "(fs=%lldMB, bs=%d, nblocks=%lld, cyln=%lld%s)", - virt_dev->name, virt_dev->file_size >> 20, - virt_dev->block_size, - (long long unsigned int)virt_dev->nblocks, - (long long unsigned int)virt_dev->nblocks/64/32, - virt_dev->nblocks < 64*32 ? " !WARNING! cyln less " - "than 1" : ""); - - scst_capacity_data_changed(virt_dev->dev); - - scst_resume_activity(); - -out: - return res; -} - -/* - * Called when a file in the /proc/VDISK_NAME/VDISK_NAME is written - */ -static int vdisk_write_proc(char *buffer, char **start, off_t offset, - int length, int *eof, struct scst_dev_type *dev_type) +static int vdisk_proc_mgmt_cmd(const char *buffer, int length, + struct scst_dev_type *dev_type) { int res = 0, action; - char *p, *name, *file_name; + char *p, *name, *file_name, *i_buf; struct scst_vdisk_dev *virt_dev, *vv; uint32_t block_size = DEF_DISK_BLOCKSIZE; int block_shift = DEF_DISK_BLOCKSIZE_SHIFT; @@ -2908,17 +4040,25 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, TRACE_ENTRY(); - /* VERY UGLY code. You can rewrite it if you want */ - - if (buffer[0] == '\0') + if ((length == 0) || (buffer == NULL) || (buffer[0] == '\0')) goto out; - if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) { - res = -EINTR; + i_buf = kmalloc(length+1, GFP_KERNEL); + if (i_buf == NULL) { + PRINT_ERROR("Unable to alloc intermediate buffer with size %d", + length+1); goto out; } - p = buffer; + memcpy(i_buf, buffer, length); + i_buf[length] = '\0'; + + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) { + res = -EINTR; + goto out_free; + } + + p = i_buf; if (p[strlen(p) - 1] == '\n') p[strlen(p) - 1] = '\0'; if (!strncmp("close ", p, 6)) { @@ -2982,13 +4122,16 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, goto out_up; } - virt_dev = vdisk_alloc_dev(); + /* It's going to be removed code, anyway */ + virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL); if (virt_dev == NULL) { TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of virt_dev failed"); res = -ENOMEM; goto out_up; } + spin_lock_init(&virt_dev->flags_lock); + virt_dev->vdt = &fileio_type.parent_vdt; while (isspace(*p) && *p != '\0') p++; @@ -3041,10 +4184,12 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, } else if (!strncmp("NULLIO", p, 6)) { p += 6; virt_dev->nullio = 1; + virt_dev->vdt = &nullio_type.parent_vdt; TRACE_DBG("%s", "NULLIO"); } else if (!strncmp("BLOCKIO", p, 7)) { p += 7; virt_dev->blockio = 1; + virt_dev->vdt = &blockio_type.parent_vdt; TRACE_DBG("%s", "BLOCKIO"); } else if (!strncmp("REMOVABLE", p, 9)) { p += 9; @@ -3085,22 +4230,9 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, list_add_tail(&virt_dev->vdisk_dev_list_entry, &vdisk_dev_list); - if (virt_dev->blockio) { - vdisk_report_registering("BLOCKIO", virt_dev); - virt_dev->virt_id = - scst_register_virtual_device(&vdisk_blk_devtype, - virt_dev->name); - } else if (virt_dev->nullio) { - vdisk_report_registering("NULLIO", virt_dev); - virt_dev->virt_id = - scst_register_virtual_device(&vdisk_null_devtype, - virt_dev->name); - } else { - vdisk_report_registering("FILEIO", virt_dev); - virt_dev->virt_id = - scst_register_virtual_device(&vdisk_file_devtype, - virt_dev->name); - } + vdisk_report_registering(virt_dev); + virt_dev->virt_id = scst_register_virtual_device( + virt_dev->vdt->vdt_devt, virt_dev->name); if (virt_dev->virt_id < 0) { res = virt_dev->virt_id; goto out_free_vpath; @@ -3148,7 +4280,7 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, goto out_up; } - res = vdisk_resync_size(virt_dev); + res = __vdisk_resync_size(virt_dev); if (res != 0) goto out_up; } @@ -3157,6 +4289,9 @@ static int vdisk_write_proc(char *buffer, char **start, off_t offset, out_up: mutex_unlock(&scst_vdisk_mutex); +out_free: + kfree(i_buf); + out: TRACE_EXIT_RES(res); return res; @@ -3170,254 +4305,17 @@ out_free_vdev: goto out_up; } -/* scst_vdisk_mutex supposed to be held */ -static int vcdrom_open(char *p, char *name) +/* + * Called when a file in the /proc/VDISK_NAME/VDISK_NAME is written + */ +static int vdisk_write_proc(char *buffer, char **start, off_t offset, + int length, int *eof, struct scst_dev_type *dev_type) { - struct scst_vdisk_dev *virt_dev, *vv; - char *file_name; - int len; - int res = 0; - int cdrom_empty; + int res; - virt_dev = NULL; - list_for_each_entry(vv, &vcdrom_dev_list, vdisk_dev_list_entry) - { - if (strcmp(vv->name, name) == 0) { - virt_dev = vv; - break; - } - } - if (virt_dev) { - PRINT_ERROR("Virtual device with name " - "%s already exist", name); - res = -EINVAL; - goto out; - } + res = vdisk_proc_mgmt_cmd(buffer, length, dev_type); - while (isspace(*p) && *p != '\0') - p++; - file_name = p; - while (!isspace(*p) && *p != '\0') - p++; - *p++ = '\0'; - if (*file_name == '\0') { - cdrom_empty = 1; - TRACE_DBG("%s", "No media"); - } else if (*file_name != '/') { - PRINT_ERROR("File path \"%s\" is not " - "absolute", file_name); - res = -EINVAL; - goto out; - } else - cdrom_empty = 0; - - virt_dev = vdisk_alloc_dev(); - if (virt_dev == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", - "Allocation of virt_dev failed"); - res = -ENOMEM; - goto out; - } - virt_dev->cdrom_empty = cdrom_empty; - - strcpy(virt_dev->name, name); - - scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx", - vdisk_gen_dev_id_num(virt_dev)); - TRACE_DBG("usn %s", virt_dev->usn); - - if (!virt_dev->cdrom_empty) { - len = strlen(file_name) + 1; - virt_dev->file_name = kmalloc(len, GFP_KERNEL); - if (virt_dev->file_name == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", - "Allocation of file_name failed"); - res = -ENOMEM; - goto out_free_vdev; - } - strncpy(virt_dev->file_name, file_name, len); - } - - list_add_tail(&virt_dev->vdisk_dev_list_entry, - &vcdrom_dev_list); - - PRINT_INFO("Registering virtual CDROM %s", name); - - virt_dev->virt_id = - scst_register_virtual_device(&vcdrom_devtype, - virt_dev->name); - if (virt_dev->virt_id < 0) { - res = virt_dev->virt_id; - goto out_free_vpath; - } - TRACE_DBG("Added virt_dev (name %s file_name %s id %d) " - "to vcdrom_dev_list", virt_dev->name, - virt_dev->file_name, virt_dev->virt_id); - -out: return res; - -out_free_vpath: - list_del(&virt_dev->vdisk_dev_list_entry); - kfree(virt_dev->file_name); - -out_free_vdev: - kfree(virt_dev); - goto out; -} - -/* scst_vdisk_mutex supposed to be held */ -static int vcdrom_close(char *name) -{ - struct scst_vdisk_dev *virt_dev, *vv; - int res = 0; - - virt_dev = NULL; - list_for_each_entry(vv, &vcdrom_dev_list, - vdisk_dev_list_entry) - { - if (strcmp(vv->name, name) == 0) { - virt_dev = vv; - break; - } - } - if (virt_dev == NULL) { - PRINT_ERROR("Virtual device with name " - "%s not found", name); - res = -EINVAL; - goto out; - } - scst_unregister_virtual_device(virt_dev->virt_id); - PRINT_INFO("Virtual device %s unregistered", - virt_dev->name); - TRACE_DBG("virt_id %d unregister", virt_dev->virt_id); - - list_del(&virt_dev->vdisk_dev_list_entry); - - kfree(virt_dev->file_name); - kfree(virt_dev); - -out: - return res; -} - -/* scst_vdisk_mutex supposed to be held */ -static int vcdrom_change(char *p, char *name) -{ - loff_t err; - struct scst_vdisk_dev *virt_dev, *vv; - char *file_name, *fn = NULL, *old_fn; - int len; - int res = 0; - - virt_dev = NULL; - list_for_each_entry(vv, &vcdrom_dev_list, - vdisk_dev_list_entry) { - if (strcmp(vv->name, name) == 0) { - virt_dev = vv; - break; - } - } - if (virt_dev == NULL) { - PRINT_ERROR("Virtual device with name " - "%s not found", name); - res = -EINVAL; - goto out; - } - - while (isspace(*p) && *p != '\0') - p++; - file_name = p; - while (!isspace(*p) && *p != '\0') - p++; - *p++ = '\0'; - if (*file_name == '\0') { - virt_dev->cdrom_empty = 1; - TRACE_DBG("%s", "No media"); - } else if (*file_name != '/') { - PRINT_ERROR("File path \"%s\" is not " - "absolute", file_name); - res = -EINVAL; - goto out; - } else - virt_dev->cdrom_empty = 0; - - old_fn = virt_dev->file_name; - - if (!virt_dev->cdrom_empty && !virt_dev->nullio) { - len = strlen(file_name) + 1; - fn = kmalloc(len, GFP_KERNEL); - if (fn == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", - "Allocation of file_name failed"); - res = -ENOMEM; - goto out; - } - - strncpy(fn, file_name, len); - virt_dev->file_name = fn; - - res = vdisk_get_file_size(virt_dev->file_name, - virt_dev->blockio, &err); - if (res != 0) - goto out_free; - } else { - err = 0; - virt_dev->file_name = NULL; - } - - if (virt_dev->nullio) - err = VDISK_NULLIO_SIZE; - - res = scst_suspend_activity(true); - if (res != 0) - goto out_free; - - if (virt_dev->prevent_allow_medium_removal) { - PRINT_ERROR("Prevent medium removal for " - "virtual device with name %s", name); - res = -EINVAL; - goto out_free_resume; - } - - virt_dev->file_size = err; - virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift; - if (!virt_dev->cdrom_empty) - virt_dev->media_changed = 1; - - scst_dev_del_all_thr_data(virt_dev->dev); - - if (!virt_dev->cdrom_empty) { - PRINT_INFO("Changed SCSI target virtual cdrom %s " - "(file=\"%s\", fs=%lldMB, bs=%d, nblocks=%lld," - " cyln=%lld%s)", virt_dev->name, virt_dev->file_name, - virt_dev->file_size >> 20, virt_dev->block_size, - (long long unsigned int)virt_dev->nblocks, - (long long unsigned int)virt_dev->nblocks/64/32, - virt_dev->nblocks < 64*32 ? " !WARNING! cyln less " - "than 1" : ""); - } else { - PRINT_INFO("Removed media from SCSI target virtual cdrom %s", - virt_dev->name); - } - - kfree(old_fn); - -out_resume: - scst_resume_activity(); - -out: - return res; - -out_free: - virt_dev->file_name = old_fn; - kfree(fn); - goto out; - -out_free_resume: - virt_dev->file_name = old_fn; - kfree(fn); - goto out_resume; } /* @@ -3452,24 +4350,34 @@ out: return res; } -/* - * Called when a file in the /proc/VCDROM_NAME/VCDROM_NAME is written - */ -static int vcdrom_write_proc(char *buffer, char **start, off_t offset, - int length, int *eof, struct scst_dev_type *dev_type) +static int vcdrom_proc_mgmt_cmd(const char *buffer, int length, + struct scst_dev_type *dev_type) { int res = 0, action; - char *p, *name; + char *p, *name, *i_buf; struct scst_vdisk_dev *virt_dev; TRACE_ENTRY(); - if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) { - res = -EINTR; + if ((length == 0) || (buffer == NULL) || (buffer[0] == '\0')) + goto out; + + i_buf = kmalloc(length+1, GFP_KERNEL); + if (i_buf == NULL) { + PRINT_ERROR("Unable to alloc intermediate buffer with size %d", + length+1); goto out; } - p = buffer; + memcpy(i_buf, buffer, length); + i_buf[length] = '\0'; + + if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) { + res = -EINTR; + goto out_free; + } + + p = i_buf; if (p[strlen(p) - 1] == '\n') p[strlen(p) - 1] = '\0'; if (!strncmp("close ", p, 6)) { @@ -3506,17 +4414,17 @@ static int vcdrom_write_proc(char *buffer, char **start, off_t offset, if (action == 2) { /* open */ - res = vcdrom_open(p, name); + res = vdev_open(&vcdrom_type.parent_vdt, p, name); if (res != 0) goto out_up; } else if (action == 1) { /* change */ - res = vcdrom_change(p, name); + res = vcdrom_change(&vcdrom_type.parent_vdt, p, name); if (res != 0) goto out_up; } else { /* close */ - res = vcdrom_close(name); + res = vdev_close(&vcdrom_type.parent_vdt, name); if (res != 0) goto out_up; } @@ -3525,11 +4433,27 @@ static int vcdrom_write_proc(char *buffer, char **start, off_t offset, out_up: mutex_unlock(&scst_vdisk_mutex); +out_free: + kfree(i_buf); + out: TRACE_EXIT_RES(res); return res; } +/* + * Called when a file in the /proc/VCDROM_NAME/VCDROM_NAME is written + */ +static int vcdrom_write_proc(char *buffer, char **start, off_t offset, + int length, int *eof, struct scst_dev_type *dev_type) +{ + int res; + + res = vcdrom_proc_mgmt_cmd(buffer, length, dev_type); + + return res; +} + static int vdisk_help_info_show(struct seq_file *seq, void *v) { char *s = (char *)seq->private; @@ -3562,8 +4486,7 @@ static int vdisk_proc_help_build(struct scst_dev_type *dev_type) &vdisk_help_proc_data); if (p == NULL) { PRINT_ERROR("Not enough memory to register dev " - "handler %s entry %s in /proc", - dev_type->name, VDISK_PROC_HELP); + "handler %s entry %s in /proc", "vdisk", VDISK_PROC_HELP); res = -ENOMEM; } @@ -3584,18 +4507,25 @@ static void vdisk_proc_help_destroy(struct scst_dev_type *dev_type) TRACE_EXIT(); } -static int __init init_scst_vdisk(struct scst_dev_type *devtype) +#endif /* CONFIG_SCST_PROC */ + +static int __init init_scst_vdisk(struct scst_dev_type *devtype, + struct vdev_type *vdt) { int res = 0; TRACE_ENTRY(); + vdt->vdt_devt = devtype; + devtype->devt_priv = vdt; + devtype->module = THIS_MODULE; res = scst_register_virtual_dev_driver(devtype); if (res < 0) goto out; +#ifdef CONFIG_SCST_PROC if (!devtype->no_proc) { res = scst_dev_handler_build_std_proc(devtype); if (res < 0) @@ -3605,11 +4535,13 @@ static int __init init_scst_vdisk(struct scst_dev_type *devtype) if (res < 0) goto out_destroy_proc; } +#endif out: TRACE_EXIT_RES(res); return res; +#ifdef CONFIG_SCST_PROC out_destroy_proc: if (!devtype->no_proc) scst_dev_handler_destroy_std_proc(devtype); @@ -3617,6 +4549,7 @@ out_destroy_proc: out_unreg: scst_unregister_virtual_dev_driver(devtype); goto out; +#endif } static void exit_scst_vdisk(struct scst_dev_type *devtype, @@ -3645,10 +4578,12 @@ static void exit_scst_vdisk(struct scst_dev_type *devtype, } mutex_unlock(&scst_vdisk_mutex); +#ifdef CONFIG_SCST_PROC if (!devtype->no_proc) { vdisk_proc_help_destroy(devtype); scst_dev_handler_destroy_std_proc(devtype); } +#endif scst_unregister_virtual_dev_driver(devtype); @@ -3656,6 +4591,110 @@ static void exit_scst_vdisk(struct scst_dev_type *devtype, return; } +static void __init init_vdev_type(struct vdev_type *vdt) +{ + /* vdt supposed to be already zeroed */ + + vdt->vfns.vdev_create = vdev_create; + vdt->vfns.vdev_destroy = vdev_destroy; + vdt->vfns.vdev_open = vdev_open; + vdt->vfns.vdev_close = vdev_close; + vdt->vfns.vdev_del = vdev_del; + vdt->vfns.vdev_init = vdev_init; + vdt->vfns.vdev_deinit = vdev_deinit; +} + +static void __init init_fileio_type(void) +{ + struct vdev_type *vdt = &fileio_type.parent_vdt; + + init_vdev_type(vdt); + + vdt->vdt_name = "FILEIO"; + vdt->help_string = "Usage:\n" + " echo \"open|close|resync_size NAME [FILE_NAME " + "[BLOCK_SIZE] [WRITE_THROUGH] [READ_ONLY] [O_DIRECT] " + "[NV_CACHE] [REMOVABLE]]\" >mgmt\n"; + + fileio_type.parent_vdt_vfns = vdt->vfns; + + vdt->vfns.vdev_add = vdisk_add; + vdt->vfns.vdev_find = vdisk_find; + vdt->vfns.parse_cmd = vdisk_parse_cmd; + vdt->vfns.perform_cmd = vdisk_perform_cmd; + vdt->vfns.parse_option = vdisk_fileio_parse_option; + vdt->vfns.pre_register = vdisk_fileio_pre_register; + + return; +} + +static void __init init_blockio_type(void) +{ + struct vdev_type *vdt = &blockio_type.parent_vdt; + + init_vdev_type(vdt); + + vdt->vdt_name = "BLOCKIO"; + vdt->help_string = "Usage:\n" + " echo \"open|close|resync_size NAME [DEVICE_NAME " + "[BLOCK_SIZE] [READ_ONLY] [REMOVABLE]]\" >mgmt\n"; + + blockio_type.parent_vdt_vfns = vdt->vfns; + + vdt->vfns.vdev_init = vdisk_blockio_init; + vdt->vfns.vdev_add = vdisk_add; + vdt->vfns.vdev_find = vdisk_find; + vdt->vfns.parse_cmd = vdisk_parse_cmd; + vdt->vfns.perform_cmd = vdisk_perform_cmd; + vdt->vfns.parse_option = vdisk_parse_option; + vdt->vfns.pre_register = vdisk_blockio_pre_register; + + return; +} +static void __init init_nullio_type(void) +{ + struct vdev_type *vdt = &nullio_type.parent_vdt; + + init_vdev_type(vdt); + + vdt->vdt_name = "NULLIO"; + vdt->help_string = "Usage:\n" + " echo \"open|close NAME [none [BLOCK_SIZE] [READ_ONLY] " + "[REMOVABLE]]\" >mgmt\n"; + + nullio_type.parent_vdt_vfns = vdt->vfns; + + vdt->vfns.vdev_init = vdisk_nullio_init; + vdt->vfns.vdev_add = vdisk_add; + vdt->vfns.vdev_find = vdisk_find; + vdt->vfns.parse_cmd = vdisk_parse_cmd; + vdt->vfns.parse_option = vdisk_parse_option; + vdt->vfns.perform_cmd = vdisk_perform_cmd; + + return; +} +static void __init init_vcdrom_type(void) +{ + struct vdev_type *vdt = &vcdrom_type.parent_vdt; + + init_vdev_type(vdt); + + vdt->vdt_name = "VCDROM"; + vdt->help_string = "Usage:\n" + " echo \"open|change|close NAME [FILE_NAME]\" >mgmt\n"; + + vcdrom_type.parent_vdt_vfns = vdt->vfns; + + vdt->vfns.vdev_init = vcdrom_init; + vdt->vfns.vdev_add = vcdrom_add; + vdt->vfns.vdev_find = vcdrom_find; + vdt->vfns.parse_cmd = vcdrom_parse_cmd; + vdt->vfns.perform_cmd = vcdrom_perform_cmd; + vdt->vfns.pre_register = vcdrom_pre_register; + + return; +} + static int __init init_scst_vdisk_driver(void) { int res; @@ -3683,19 +4722,24 @@ static int __init init_scst_vdisk_driver(void) atomic_set(&nullio_thr_data.hdr.ref, 1); /* never destroy it */ - res = init_scst_vdisk(&vdisk_file_devtype); + init_fileio_type(); + init_blockio_type(); + init_nullio_type(); + init_vcdrom_type(); + + res = init_scst_vdisk(&vdisk_file_devtype, &fileio_type.parent_vdt); if (res != 0) goto out_free_slab; - res = init_scst_vdisk(&vdisk_blk_devtype); + res = init_scst_vdisk(&vdisk_blk_devtype, &blockio_type.parent_vdt); if (res != 0) goto out_free_vdisk; - res = init_scst_vdisk(&vdisk_null_devtype); + res = init_scst_vdisk(&vdisk_null_devtype, &nullio_type.parent_vdt); if (res != 0) goto out_free_blk; - res = init_scst_vdisk(&vcdrom_devtype); + res = init_scst_vdisk(&vcdrom_devtype, &vcdrom_type.parent_vdt); if (res != 0) goto out_free_null; diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 305eda6da..024f9b1ee 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -57,11 +57,11 @@ static void scst_alloc_set_UA(struct scst_tgt_dev *tgt_dev, const uint8_t *sense, int sense_len, int flags); static void scst_free_all_UA(struct scst_tgt_dev *tgt_dev); static void scst_release_space(struct scst_cmd *cmd); -static void scst_sess_free_tgt_devs(struct scst_session *sess); static void scst_unblock_cmds(struct scst_device *dev); static void scst_clear_reservation(struct scst_tgt_dev *tgt_dev); static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, struct scst_acg_dev *acg_dev); +static void scst_tgt_retry_timer_fn(unsigned long arg); #ifdef CONFIG_SCST_DEBUG_TM static void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev, @@ -1244,6 +1244,44 @@ restart1: return; } +struct scst_tgt *scst_alloc_tgt(struct scst_tgt_template *tgtt) +{ + struct scst_tgt *tgt; + + TRACE_ENTRY(); + + tgt = kzalloc(sizeof(*tgt), GFP_KERNEL); + if (tgt == NULL) { + TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt failed"); + goto out; + } + + INIT_LIST_HEAD(&tgt->sess_list); + init_waitqueue_head(&tgt->unreg_waitQ); + tgt->tgtt = tgtt; + tgt->sg_tablesize = tgtt->sg_tablesize; + spin_lock_init(&tgt->tgt_lock); + INIT_LIST_HEAD(&tgt->retry_cmd_list); + atomic_set(&tgt->finished_cmds, 0); + init_timer(&tgt->retry_timer); + tgt->retry_timer.data = (unsigned long)tgt; + tgt->retry_timer.function = scst_tgt_retry_timer_fn; + +out: + TRACE_EXIT_HRES((unsigned long)tgt); + return tgt; +} + +void scst_free_tgt(struct scst_tgt *tgt) +{ + TRACE_ENTRY(); + + kfree(tgt); + + TRACE_EXIT(); + return; +} + /* Called under scst_mutex and suspended activity */ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev) { @@ -1310,6 +1348,7 @@ void scst_free_device(struct scst_device *dev) } #endif + kfree(dev->virt_name); __exit_io_context(dev->dev_io_ctx); kfree(dev); @@ -1356,6 +1395,16 @@ out: return res; } +void scst_acg_dev_destroy(struct scst_acg_dev *acg_dev) +{ + TRACE_ENTRY(); + + kmem_cache_free(scst_acgd_cachep, acg_dev); + + TRACE_EXIT(); + return; +} + /* The activity supposed to be suspended and scst_mutex held */ static void scst_free_acg_dev(struct scst_acg_dev *acg_dev) { @@ -1366,7 +1415,11 @@ static void scst_free_acg_dev(struct scst_acg_dev *acg_dev) list_del(&acg_dev->acg_dev_list_entry); list_del(&acg_dev->dev_acg_dev_list_entry); - kmem_cache_free(scst_acgd_cachep, acg_dev); + if (acg_dev->acg_dev_kobj_initialized) { + kobject_del(&acg_dev->acg_dev_kobj); + kobject_put(&acg_dev->acg_dev_kobj); + } else + scst_acg_dev_destroy(acg_dev); TRACE_EXIT(); return; @@ -1505,16 +1558,8 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess, if (sess->tgt->tgtt->unchecked_isa_dma || ini_unchecked_isa_dma) scst_sgv_pool_use_dma(tgt_dev); - if (dev->scsi_dev != NULL) { - TRACE_MGMT_DBG("host=%d, channel=%d, id=%d, lun=%d, " - "SCST lun=%lld", dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun, - (long long unsigned int)tgt_dev->lun); - } else { - TRACE_MGMT_DBG("Virtual device %s on SCST lun=%lld", - dev->virt_name, (long long unsigned int)tgt_dev->lun); - } + TRACE_MGMT_DBG("Device %s on SCST lun=%lld", + dev->virt_name, (long long unsigned int)tgt_dev->lun); spin_lock_init(&tgt_dev->tgt_dev_lock); INIT_LIST_HEAD(&tgt_dev->UA_list); @@ -1750,7 +1795,7 @@ out_free: * scst_mutex supposed to be held, there must not be parallel activity in this * session. */ -static void scst_sess_free_tgt_devs(struct scst_session *sess) +void scst_sess_free_tgt_devs(struct scst_session *sess) { int i; struct scst_tgt_dev *tgt_dev, *t; @@ -1811,20 +1856,9 @@ int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev, if (gen_scst_report_luns_changed) scst_report_luns_changed(acg); - if (dev->virt_name != NULL) { - PRINT_INFO("Added device %s to group %s (LUN %lld, " - "rd_only %d)", dev->virt_name, acg->acg_name, - (long long unsigned int)lun, - read_only); - } else { - PRINT_INFO("Added device %d:%d:%d:%d to group %s (LUN " - "%lld, rd_only %d)", - dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun, acg->acg_name, - (long long unsigned int)lun, - read_only); - } + PRINT_INFO("Added device %s to group %s (LUN %lld, " + "rd_only %d)", dev->virt_name, acg->acg_name, + (long long unsigned int)lun, read_only); out: TRACE_EXIT_RES(res); @@ -1872,15 +1906,8 @@ int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev, if (gen_scst_report_luns_changed) scst_report_luns_changed(acg); - if (dev->virt_name != NULL) { - PRINT_INFO("Removed device %s from group %s", - dev->virt_name, acg->acg_name); - } else { - PRINT_INFO("Removed device %d:%d:%d:%d from group %s", - dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun, acg->acg_name); - } + PRINT_INFO("Removed device %s from group %s", dev->virt_name, + acg->acg_name); out: TRACE_EXIT_RES(res); @@ -2158,9 +2185,7 @@ static void scst_send_release(struct scst_device *dev) req = scsi_allocate_request(scsi_dev, GFP_KERNEL); if (req == NULL) { PRINT_ERROR("Allocation of scsi_request failed: unable " - "to RELEASE device %d:%d:%d:%d", - scsi_dev->host->host_no, scsi_dev->channel, - scsi_dev->id, scsi_dev->lun); + "to RELEASE device %s", dev->virt_name); goto out; } @@ -2347,10 +2372,21 @@ void scst_free_session(struct scst_session *sess) scst_sess_free_tgt_devs(sess); + /* Called under lock to protect from too early tgt release */ wake_up_all(&sess->tgt->unreg_waitQ); mutex_unlock(&scst_mutex); + scst_sess_sysfs_put(sess); + + TRACE_EXIT(); + return; +} + +void scst_release_session(struct scst_session *sess) +{ + TRACE_ENTRY(); + kfree(sess->initiator_name); kmem_cache_free(scst_sess_cachep, sess); @@ -2611,7 +2647,7 @@ void scst_check_retries(struct scst_tgt *tgt) return; } -void scst_tgt_retry_timer_fn(unsigned long arg) +static void scst_tgt_retry_timer_fn(unsigned long arg) { struct scst_tgt *tgt = (struct scst_tgt *)arg; unsigned long flags; @@ -3992,11 +4028,8 @@ int scst_obtain_device_parameters(struct scst_device *dev) dev->tst = buffer[4+2] >> 5; q = buffer[4+3] >> 4; if (q > SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER) { - PRINT_ERROR("Too big QUEUE ALG %x, dev " - "%d:%d:%d:%d", dev->queue_alg, - dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, - dev->scsi_dev->id, dev->scsi_dev->lun); + PRINT_ERROR("Too big QUEUE ALG %x, dev %s", + dev->queue_alg, dev->virt_name); } dev->queue_alg = q; dev->swp = (buffer[4+4] & 0x8) >> 3; @@ -4011,12 +4044,10 @@ int scst_obtain_device_parameters(struct scst_device *dev) dev->has_own_order_mgmt = !dev->queue_alg; TRACE(TRACE_SCSI|TRACE_MGMT_MINOR, - "Device %d:%d:%d:%d: TST %x, " + "Device %s: TST %x, " "QUEUE ALG %x, SWP %x, TAS %x, D_SENSE %d" "has_own_order_mgmt %d", - dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, dev->scsi_dev->id, - dev->scsi_dev->lun, dev->tst, dev->queue_alg, + dev->virt_name, dev->tst, dev->queue_alg, dev->swp, dev->tas, dev->d_sense, dev->has_own_order_mgmt); @@ -4037,16 +4068,13 @@ int scst_obtain_device_parameters(struct scst_device *dev) SCST_SENSE_KEY_VALID, ILLEGAL_REQUEST, 0, 0)) { TRACE(TRACE_SCSI|TRACE_MGMT_MINOR, - "Device %d:%d:%d:%d doesn't " + "Device %s doesn't " "support control mode page, " "using defaults: TST %x, " "QUEUE ALG %x, SWP %x, " "TAS %x, D_SENSE %d, " "has_own_order_mgmt %d ", - dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, - dev->scsi_dev->id, - dev->scsi_dev->lun, + dev->virt_name, dev->tst, dev->queue_alg, dev->swp, dev->tas, dev->d_sense, @@ -4057,23 +4085,16 @@ int scst_obtain_device_parameters(struct scst_device *dev) sizeof(sense_buffer), SCST_SENSE_KEY_VALID, NOT_READY, 0, 0)) { - TRACE(TRACE_SCSI, - "Device %d:%d:%d:%d not ready", - dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, - dev->scsi_dev->id, - dev->scsi_dev->lun); + TRACE(TRACE_SCSI, "Device %s not ready", + dev->virt_name); res = 0; goto out; } } else { TRACE(TRACE_SCSI|TRACE_MGMT_MINOR, "Internal MODE SENSE to " - "device %d:%d:%d:%d failed: %x", - dev->scsi_dev->host->host_no, - dev->scsi_dev->channel, - dev->scsi_dev->id, - dev->scsi_dev->lun, res); + "device %s failed: %x", + dev->virt_name, res); PRINT_BUFF_FLAG(TRACE_SCSI|TRACE_MGMT_MINOR, "MODE SENSE sense", sense_buffer, sizeof(sense_buffer)); @@ -5053,7 +5074,7 @@ static const int tm_dbg_on_state_num_passes[] = { 5, 1, 0x7ffffff }; static void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev, struct scst_acg_dev *acg_dev) { - if ((acg_dev->acg == scst_default_acg) && (acg_dev->lun == 0)) { + if (acg_dev->lun == 6) { unsigned long flags; if (tm_dbg_tgt_dev != NULL) @@ -5065,8 +5086,9 @@ static void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev, tm_dbg_on_state_passes = tm_dbg_on_state_num_passes[tm_dbg_state]; tm_dbg_tgt_dev = tgt_dev; - PRINT_INFO("LUN 0 connected from initiator %s is under " + PRINT_INFO("LUN %lld connected from initiator %s is under " "TM debugging (tgt_dev %p)", + (unsigned long long)tgt_dev->lun, tgt_dev->sess->initiator_name, tgt_dev); spin_unlock_irqrestore(&scst_tm_dbg_lock, flags); } diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 7880a4a65..91c8b753d 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -102,7 +102,9 @@ struct kmem_cache *scst_sess_cachep; struct kmem_cache *scst_acgd_cachep; struct list_head scst_acg_list; +#ifdef CONFIG_SCST_PROC struct scst_acg *scst_default_acg; +#endif spinlock_t scst_init_lock; wait_queue_head_t scst_init_cmd_list_waitQ; @@ -188,21 +190,21 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, } if (!vtt->detect) { - PRINT_ERROR("Target driver %s doesn't have a " + PRINT_ERROR("Target driver %s must have " "detect() method.", vtt->name); res = -EINVAL; goto out_err; } if (!vtt->release) { - PRINT_ERROR("Target driver %s doesn't have a " + PRINT_ERROR("Target driver %s must have " "release() method.", vtt->name); res = -EINVAL; goto out_err; } if (!vtt->xmit_response) { - PRINT_ERROR("Target driver %s doesn't have a " + PRINT_ERROR("Target driver %s must have " "xmit_response() method.", vtt->name); res = -EINVAL; goto out_err; @@ -216,26 +218,40 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, goto out_err; } + if (!vtt->enable_tgt || !vtt->is_tgt_enabled) { + PRINT_WARNING("Target driver %s doesn't have enable_tgt() " + "and/or is_tgt_enabled() method(s). This is unsafe " + "and can lead that initiators connected on the " + "initialization time can see an unexpected set of " + "devices or no devices at all!", vtt->name); + } + +#ifdef CONFIG_SCST_PROC if (!vtt->no_proc_entry) { res = scst_build_proc_target_dir_entries(vtt); if (res < 0) goto out_err; } +#endif + + res = scst_create_tgtt_sysfs(vtt); + if (res) + goto out_sysfs_err; if (vtt->rdy_to_xfer == NULL) vtt->rdy_to_xfer_atomic = 1; if (mutex_lock_interruptible(&m) != 0) - goto out_err; + goto out_sysfs_err; if (mutex_lock_interruptible(&scst_mutex) != 0) - goto out_m_up; + goto out_m_err; list_for_each_entry(t, &scst_template_list, scst_template_list_entry) { if (strcmp(t->name, vtt->name) == 0) { PRINT_ERROR("Target driver %s already registered", vtt->name); mutex_unlock(&scst_mutex); - goto out_cleanup; + goto out_m_err; } } mutex_unlock(&scst_mutex); @@ -246,7 +262,7 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, if (res < 0) { PRINT_ERROR("%s", "The detect() routine failed"); res = -EINVAL; - goto out_cleanup; + goto out_m_err; } mutex_lock(&scst_mutex); @@ -263,12 +279,15 @@ out: TRACE_EXIT_RES(res); return res; -out_cleanup: - scst_cleanup_proc_target_dir_entries(vtt); - -out_m_up: +out_m_err: mutex_unlock(&m); +out_sysfs_err: +#ifdef CONFIG_SCST_PROC + scst_cleanup_proc_target_dir_entries(vtt); +#endif + scst_tgtt_sysfs_put(vtt); + out_err: PRINT_ERROR("Failed to register target template %s", vtt->name); goto out; @@ -293,7 +312,7 @@ void scst_unregister_target_template(struct scst_tgt_template *vtt) } if (!found) { PRINT_ERROR("Target driver %s isn't registered", vtt->name); - goto out_up; + goto out_err_up; } restart: @@ -305,15 +324,23 @@ restart: } list_del(&vtt->scst_template_list_entry); - PRINT_INFO("Target template %s unregistered successfully", vtt->name); - -out_up: mutex_unlock(&scst_mutex); +#ifdef CONFIG_SCST_PROC scst_cleanup_proc_target_dir_entries(vtt); +#endif + scst_tgtt_sysfs_put(vtt); + + PRINT_INFO("Target template %s unregistered successfully", vtt->name); + +out: TRACE_EXIT(); return; + +out_err_up: + mutex_unlock(&scst_mutex); + goto out; } EXPORT_SYMBOL(scst_unregister_target_template); @@ -325,24 +352,12 @@ struct scst_tgt *scst_register(struct scst_tgt_template *vtt, TRACE_ENTRY(); - tgt = kzalloc(sizeof(*tgt), GFP_KERNEL); + tgt = scst_alloc_tgt(vtt); if (tgt == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of tgt failed"); rc = -ENOMEM; goto out_err; } - INIT_LIST_HEAD(&tgt->sess_list); - init_waitqueue_head(&tgt->unreg_waitQ); - tgt->tgtt = vtt; - tgt->sg_tablesize = vtt->sg_tablesize; - spin_lock_init(&tgt->tgt_lock); - INIT_LIST_HEAD(&tgt->retry_cmd_list); - atomic_set(&tgt->finished_cmds, 0); - init_timer(&tgt->retry_timer); - tgt->retry_timer.data = (unsigned long)tgt; - tgt->retry_timer.function = scst_tgt_retry_timer_fn; - rc = scst_suspend_activity(true); if (rc != 0) goto out_free_tgt_err; @@ -353,25 +368,72 @@ struct scst_tgt *scst_register(struct scst_tgt_template *vtt, } if (target_name != NULL) { - int len = strlen(target_name) + 1 + - strlen(SCST_DEFAULT_ACG_NAME) + 1; +#ifdef CONFIG_SCST_PROC + int len = strlen(target_name) + + strlen(SCST_DEFAULT_ACG_NAME) + 1 + 1; tgt->default_group_name = kmalloc(len, GFP_KERNEL); if (tgt->default_group_name == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of default " - "group name failed"); + TRACE(TRACE_OUT_OF_MEM, "Allocation of default " + "group name failed (tgt %s)", target_name); rc = -ENOMEM; goto out_unlock_resume; } sprintf(tgt->default_group_name, "%s_%s", SCST_DEFAULT_ACG_NAME, target_name); +#endif + + tgt->tgt_name = kmalloc(strlen(target_name) + 1, GFP_KERNEL); + if (tgt->tgt_name == NULL) { + TRACE(TRACE_OUT_OF_MEM, "Allocation of tgt name %s failed", + target_name); + rc = -ENOMEM; +#ifdef CONFIG_SCST_PROC + goto out_free_def_name; +#else + goto out_unlock_resume; +#endif + } + strcpy(tgt->tgt_name, target_name); + } else { + static int tgt_num; /* protected by scst_mutex */ + int len = strlen(vtt->name) + + strlen(SCST_DEFAULT_TGT_NAME_SUFFIX) + 11 + 1; + + tgt->tgt_name = kmalloc(len, GFP_KERNEL); + if (tgt->tgt_name == NULL) { + TRACE(TRACE_OUT_OF_MEM, "Allocation of tgt name failed " + "(template name %s)", vtt->name); + rc = -ENOMEM; +#ifdef CONFIG_SCST_PROC + goto out_free_def_name; +#else + goto out_unlock_resume; +#endif + } + sprintf(tgt->tgt_name, "%s%s%d", vtt->name, + SCST_DEFAULT_TGT_NAME_SUFFIX, tgt_num++); } + tgt->default_acg = scst_alloc_add_acg(tgt->tgt_name); + if (tgt->default_acg == NULL) + goto out_free_tgt_name; + +#ifdef CONFIG_SCST_PROC rc = scst_build_proc_target_entries(tgt); if (rc < 0) - goto out_free_name; - else - list_add_tail(&tgt->tgt_list_entry, &vtt->tgt_list); + goto out_free_acg; +#endif + + rc = scst_create_tgt_sysfs(tgt); + if (rc < 0) +#ifdef CONFIG_SCST_PROC + goto out_clean_proc; +#else + goto out_free_acg; +#endif + + list_add_tail(&tgt->tgt_list_entry, &vtt->tgt_list); mutex_unlock(&scst_mutex); scst_resume_activity(); @@ -383,8 +445,21 @@ out: TRACE_EXIT(); return tgt; -out_free_name: +#ifdef CONFIG_SCST_PROC +out_clean_proc: + scst_cleanup_proc_target_entries(tgt); +#endif + +out_free_acg: + scst_destroy_acg(tgt->default_acg); + +out_free_tgt_name: + kfree(tgt->tgt_name); + +#ifdef CONFIG_SCST_PROC +out_free_def_name: kfree(tgt->default_group_name); +#endif out_unlock_resume: mutex_unlock(&scst_mutex); @@ -393,7 +468,7 @@ out_resume_free: scst_resume_activity(); out_free_tgt_err: - kfree(tgt); + scst_tgt_sysfs_put(tgt); tgt = NULL; out_err: @@ -449,20 +524,31 @@ again: list_del(&tgt->tgt_list_entry); +#ifdef CONFIG_SCST_PROC scst_cleanup_proc_target_entries(tgt); - - kfree(tgt->default_group_name); +#endif 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); + + kfree(tgt->tgt_name); +#ifdef CONFIG_SCST_PROC + kfree(tgt->default_group_name); +#endif + del_timer_sync(&tgt->retry_timer); + scst_tgt_sysfs_put(tgt); + PRINT_INFO("Target %p for template %s unregistered successfully", tgt, vtt->name); - kfree(tgt); - TRACE_EXIT(); return; } @@ -654,9 +740,21 @@ static int scst_register_device(struct scsi_device *scsidp) dev->type = scsidp->type; + dev->virt_name = kmalloc(50, GFP_KERNEL); + if (dev->virt_name == NULL) { + PRINT_ERROR("%s", "Unable to alloc device name"); + res = -ENOMEM; + goto out_free_dev; + } + snprintf(dev->virt_name, 50, "%d:%d:%d:%d", scsidp->host->host_no, + scsidp->channel, scsidp->id, scsidp->lun); + dev->rq_disk = alloc_disk(1); if (dev->rq_disk == NULL) { + PRINT_ERROR("Unable to alloc disk object for device %s", + dev->virt_name); res = -ENOMEM; + /* virt_name will be freed in scst_free_dev() */ goto out_free_dev; } dev->rq_disk->major = SCST_MAJOR; @@ -665,6 +763,10 @@ static int scst_register_device(struct scsi_device *scsidp) list_add_tail(&dev->dev_list_entry, &scst_dev_list); + res = scst_create_device_sysfs(dev); + if (res != 0) + goto out_free; + list_for_each_entry(dt, &scst_dev_type_list, dev_type_list_entry) { if (dt->type == scsidp->type) { res = scst_assign_dev_handler(dev, dt); @@ -686,9 +788,9 @@ out_err: "type %d", scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, scsidp->type); } else { - PRINT_ERROR("Failed to to scsi%d, channel %d, id %d, lun %d, " - "type %d", scsidp->host->host_no, scsidp->channel, - scsidp->id, scsidp->lun, scsidp->type); + PRINT_ERROR("Failed to attach to scsi%d, channel %d, id %d, " + "lun %d, type %d", scsidp->host->host_no, + scsidp->channel, scsidp->id, scsidp->lun, scsidp->type); } TRACE_EXIT_RES(res); @@ -699,7 +801,7 @@ out_free: put_disk(dev->rq_disk); out_free_dev: - scst_free_device(dev); + scst_device_sysfs_put(dev); goto out_up; } @@ -735,7 +837,7 @@ static void scst_unregister_device(struct scsi_device *scsidp) scst_assign_dev_handler(dev, &scst_null_devtype); put_disk(dev->rq_disk); - scst_free_device(dev); + scst_device_sysfs_put(dev); PRINT_INFO("Detached from scsi%d, channel %d, id %d, lun %d, type %d", scsidp->host->host_no, scsidp->channel, scsidp->id, @@ -754,7 +856,7 @@ static int scst_dev_handler_check(struct scst_dev_type *dev_handler) int res = 0; if (dev_handler->parse == NULL) { - PRINT_ERROR("scst dev_type driver %s doesn't have a " + PRINT_ERROR("scst dev handler %s must have " "parse() method.", dev_handler->name); res = -EINVAL; goto out; @@ -816,13 +918,25 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler, dev->type = dev_handler->type; dev->scsi_dev = NULL; - dev->virt_name = dev_name; + dev->virt_name = kstrdup(dev_name, GFP_KERNEL); + if (dev->virt_name == NULL) { + PRINT_ERROR("Unable to allocate virt_name for dev %s", + dev_name); + res = -ENOMEM; + goto out_release; + } dev->virt_id = scst_virt_dev_last_id++; list_add_tail(&dev->dev_list_entry, &scst_dev_list); res = dev->virt_id; + rc = scst_create_device_sysfs(dev); + if (rc != 0) { + res = rc; + goto out_free_del; + } + rc = scst_assign_dev_handler(dev, dev_handler); if (rc != 0) { res = rc; @@ -847,7 +961,9 @@ out: out_free_del: list_del(&dev->dev_list_entry); - scst_free_device(dev); + +out_release: + scst_device_sysfs_put(dev); goto out_up; } EXPORT_SYMBOL(scst_register_virtual_device); @@ -877,8 +993,7 @@ void scst_unregister_virtual_device(int id) list_del(&dev->dev_list_entry); list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list, - dev_acg_dev_list_entry) - { + dev_acg_dev_list_entry) { scst_acg_remove_dev(acg_dev->acg, dev, true); } @@ -887,7 +1002,7 @@ void scst_unregister_virtual_device(int id) PRINT_INFO("Detached from virtual device %s (id %d)", dev->virt_name, dev->virt_id); - scst_free_device(dev); + scst_device_sysfs_put(dev); out_unblock: mutex_unlock(&scst_mutex); @@ -959,9 +1074,17 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type, if (exist) goto out_up; - res = scst_build_proc_dev_handler_dir_entries(dev_type); +#ifdef CONFIG_SCST_PROC + if (!dev_type->no_proc) { + res = scst_build_proc_dev_handler_dir_entries(dev_type); + if (res < 0) + goto out_up; + } +#endif + + res = scst_create_devt_sysfs(dev_type); if (res < 0) - goto out_up; + goto out_free; list_add_tail(&dev_type->dev_type_list_entry, &scst_dev_type_list); @@ -984,6 +1107,13 @@ out: TRACE_EXIT_RES(res); return res; +out_free: +#ifdef CONFIG_SCST_PROC + if (!dev_type->no_proc) + scst_cleanup_proc_dev_handler_dir_entries(dev_type); +#endif + scst_devt_sysfs_put(dev_type); + out_up: mutex_unlock(&scst_mutex); @@ -1032,7 +1162,11 @@ void scst_unregister_dev_driver(struct scst_dev_type *dev_type) mutex_unlock(&scst_mutex); scst_resume_activity(); +#ifdef CONFIG_SCST_PROC scst_cleanup_proc_dev_handler_dir_entries(dev_type); +#endif + + scst_devt_sysfs_put(dev_type); PRINT_INFO("Device handler \"%s\" for type %d unloaded", dev_type->name, dev_type->type); @@ -1066,11 +1200,17 @@ int __scst_register_virtual_dev_driver(struct scst_dev_type *dev_type, if (res != 0) goto out_err; +#ifdef CONFIG_SCST_PROC if (!dev_type->no_proc) { res = scst_build_proc_dev_handler_dir_entries(dev_type); if (res < 0) goto out_err; } +#endif + + res = scst_create_devt_sysfs(dev_type); + if (res < 0) + goto out_free; if (dev_type->type != -1) { PRINT_INFO("Virtual device handler %s for type %d " @@ -1085,6 +1225,14 @@ out: TRACE_EXIT_RES(res); return res; +out_free: +#ifdef CONFIG_SCST_PROC + if (!dev_type->no_proc) + scst_cleanup_proc_dev_handler_dir_entries(dev_type); +#endif + + scst_devt_sysfs_put(dev_type); + out_err: PRINT_ERROR("Failed to register virtual device handler \"%s\"", dev_type->name); @@ -1096,8 +1244,12 @@ void scst_unregister_virtual_dev_driver(struct scst_dev_type *dev_type) { TRACE_ENTRY(); +#ifdef CONFIG_SCST_PROC if (!dev_type->no_proc) scst_cleanup_proc_dev_handler_dir_entries(dev_type); +#endif + + scst_devt_sysfs_put(dev_type); PRINT_INFO("Device handler \"%s\" unloaded", dev_type->name); @@ -1272,7 +1424,10 @@ int scst_assign_dev_handler(struct scst_device *dev, if (dev->handler == handler) goto out; - if (dev->handler && dev->handler->detach_tgt) { + if (dev->handler == NULL) + goto assign; + + if (dev->handler->detach_tgt) { list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, dev_tgt_dev_list_entry) { TRACE_DBG("Calling dev handler's detach_tgt(%p)", @@ -1282,7 +1437,7 @@ int scst_assign_dev_handler(struct scst_device *dev, } } - if (dev->handler && dev->handler->detach) { + if (dev->handler->detach) { TRACE_DBG("%s", "Calling dev handler's detach()"); dev->handler->detach(dev); TRACE_DBG("%s", "Old handler's detach() returned"); @@ -1290,26 +1445,34 @@ int scst_assign_dev_handler(struct scst_device *dev, scst_stop_dev_threads(dev); + scst_devt_dev_sysfs_put(dev); + +assign: dev->handler = handler; - if (handler) { - res = scst_create_dev_threads(dev); - if (res != 0) - goto out_null; - } + if (handler == NULL) + goto out; - if (handler && handler->attach) { + res = scst_create_devt_dev_sysfs(dev); + if (res != 0) + goto out_null; + + res = scst_create_dev_threads(dev); + if (res != 0) + goto out_remove_sysfs; + + if (handler->attach) { TRACE_DBG("Calling new dev handler's attach(%p)", dev); res = handler->attach(dev); TRACE_DBG("New dev handler's attach() returned %d", res); if (res != 0) { PRINT_ERROR("New device handler's %s attach() " "failed: %d", handler->name, res); + goto out_thr_null; } - goto out_thr_null; } - if (handler && handler->attach_tgt) { + if (handler->attach_tgt) { list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list, dev_tgt_dev_list_entry) { TRACE_DBG("Calling dev handler's attach_tgt(%p)", @@ -1326,14 +1489,6 @@ int scst_assign_dev_handler(struct scst_device *dev, } } -out_thr_null: - if (res != 0) - scst_stop_dev_threads(dev); - -out_null: - if (res != 0) - dev->handler = &scst_null_devtype; - out: TRACE_EXIT_RES(res); return res; @@ -1341,8 +1496,7 @@ out: out_err_detach_tgt: if (handler && handler->detach_tgt) { list_for_each_entry(tgt_dev, &attached_tgt_devs, - extra_tgt_dev_list_entry) - { + extra_tgt_dev_list_entry) { TRACE_DBG("Calling handler's detach_tgt(%p)", tgt_dev); handler->detach_tgt(tgt_dev); @@ -1354,7 +1508,16 @@ out_err_detach_tgt: handler->detach(dev); TRACE_DBG("%s", "Handler's detach() returned"); } - goto out_null; + +out_thr_null: + scst_stop_dev_threads(dev); + +out_remove_sysfs: + scst_devt_dev_sysfs_put(dev); + +out_null: + dev->handler = &scst_null_devtype; + goto out; } int scst_global_threads_count(void) @@ -1830,6 +1993,10 @@ static int __init init_scst(void) goto out_destroy_sense_mempool; } + res = scst_sysfs_init(); + if (res != 0) + goto out_destroy_aen_mempool; + if (scst_max_cmd_mem == 0) { struct sysinfo si; si_meminfo(&si); @@ -1857,17 +2024,23 @@ static int __init init_scst(void) res = scst_sgv_pools_init( ((uint64_t)scst_max_cmd_mem << 10) >> (PAGE_SHIFT - 10), 0); if (res != 0) - goto out_destroy_aen_mempool; + goto out_sysfs_cleanup; +#ifdef CONFIG_SCST_PROC scst_default_acg = scst_alloc_add_acg(SCST_DEFAULT_ACG_NAME); if (scst_default_acg == NULL) { res = -ENOMEM; goto out_destroy_sgv_pool; } +#endif res = scsi_register_interface(&scst_interface); if (res != 0) +#ifdef CONFIG_SCST_PROC goto out_free_acg; +#else + goto out_destroy_sgv_pool; +#endif for (i = 0; i < (int)ARRAY_SIZE(scst_tasklets); i++) { spin_lock_init(&scst_tasklets[i].tasklet_lock); @@ -1884,10 +2057,11 @@ static int __init init_scst(void) if (res < 0) goto out_thread_free; +#ifdef CONFIG_SCST_PROC res = scst_proc_init_module(); if (res != 0) goto out_thread_free; - +#endif PRINT_INFO("SCST version %s loaded successfully (max mem for " "commands %dMB, per device %dMB)", SCST_VERSION_STRING, @@ -1904,12 +2078,17 @@ out_thread_free: scsi_unregister_interface(&scst_interface); +#ifdef CONFIG_SCST_PROC out_free_acg: scst_destroy_acg(scst_default_acg); +#endif out_destroy_sgv_pool: scst_sgv_pools_deinit(); +out_sysfs_cleanup: + scst_sysfs_cleanup(); + out_destroy_aen_mempool: mempool_destroy(scst_aen_mempool); @@ -1963,12 +2142,17 @@ static void __exit exit_scst(void) /* ToDo: unregister_cpu_notifier() */ +#ifdef CONFIG_SCST_PROC scst_proc_cleanup_module(); +#endif + scst_sysfs_cleanup(); scst_stop_all_threads(); scsi_unregister_interface(&scst_interface); +#ifdef CONFIG_SCST_PROC scst_destroy_acg(scst_default_acg); +#endif scst_sgv_pools_deinit(); diff --git a/scst/src/scst_mem.c b/scst/src/scst_mem.c index 99364d93c..ceab46903 100644 --- a/scst/src/scst_mem.c +++ b/scst/src/scst_mem.c @@ -36,7 +36,7 @@ /* Max pages freed from a pool per shrinking iteration */ #define MAX_PAGES_PER_POOL 50 -static struct sgv_pool sgv_norm_clust_pool, sgv_norm_pool, sgv_dma_pool; +static struct sgv_pool *sgv_norm_clust_pool, *sgv_norm_pool, *sgv_dma_pool; static atomic_t sgv_pages_total = ATOMIC_INIT(0); @@ -78,7 +78,7 @@ static inline bool sgv_pool_clustered(const struct sgv_pool *pool) void scst_sgv_pool_use_norm(struct scst_tgt_dev *tgt_dev) { tgt_dev->gfp_mask = __GFP_NOWARN; - tgt_dev->pool = &sgv_norm_pool; + tgt_dev->pool = sgv_norm_pool; clear_bit(SCST_TGT_DEV_CLUST_POOL, &tgt_dev->tgt_dev_flags); } @@ -86,7 +86,7 @@ void scst_sgv_pool_use_norm_clust(struct scst_tgt_dev *tgt_dev) { TRACE_MEM("%s", "Use clustering"); tgt_dev->gfp_mask = __GFP_NOWARN; - tgt_dev->pool = &sgv_norm_clust_pool; + tgt_dev->pool = sgv_norm_clust_pool; set_bit(SCST_TGT_DEV_CLUST_POOL, &tgt_dev->tgt_dev_flags); } @@ -94,7 +94,7 @@ void scst_sgv_pool_use_dma(struct scst_tgt_dev *tgt_dev) { TRACE_MEM("%s", "Use ISA DMA memory"); tgt_dev->gfp_mask = __GFP_NOWARN | GFP_DMA; - tgt_dev->pool = &sgv_dma_pool; + tgt_dev->pool = sgv_dma_pool; clear_bit(SCST_TGT_DEV_CLUST_POOL, &tgt_dev->tgt_dev_flags); } @@ -1292,7 +1292,7 @@ static void sgv_pool_init_cache(struct sgv_pool *pool, int cache_num) } /* Must be called under sgv_pools_mutex */ -int sgv_pool_init(struct sgv_pool *pool, const char *name, +static int sgv_pool_init(struct sgv_pool *pool, const char *name, enum sgv_clustering_types clustering_type, int single_alloc_pages, int purge_interval) { @@ -1436,7 +1436,7 @@ void sgv_pool_flush(struct sgv_pool *pool) } EXPORT_SYMBOL(sgv_pool_flush); -void sgv_pool_deinit(struct sgv_pool *pool) +static void sgv_pool_deinit_put(struct sgv_pool *pool) { int i; @@ -1458,6 +1458,10 @@ void sgv_pool_deinit(struct sgv_pool *pool) pool->caches[i] = NULL; } + scst_sgv_sysfs_put(pool); + + /* pool can be dead here */ + TRACE_EXIT(); return; } @@ -1511,6 +1515,10 @@ struct sgv_pool *sgv_pool_create(const char *name, if (rc != 0) goto out_free_unlock; + rc = scst_create_sgv_sysfs(pool); + if (rc != 0) + goto out_err_unlock_put; + out_unlock: mutex_unlock(&sgv_pools_mutex); @@ -1523,14 +1531,18 @@ out_free_unlock: out_err_unlock: pool = NULL; goto out_unlock; + +out_err_unlock_put: + mutex_unlock(&sgv_pools_mutex); + sgv_pool_deinit_put(pool); + goto out_err_unlock; } EXPORT_SYMBOL(sgv_pool_create); -static void sgv_pool_destroy(struct sgv_pool *pool) +void sgv_pool_destroy(struct sgv_pool *pool) { TRACE_ENTRY(); - sgv_pool_deinit(pool); kfree(pool); TRACE_EXIT(); @@ -1551,7 +1563,7 @@ void sgv_pool_put(struct sgv_pool *pool) TRACE_MEM("Decrementing sgv pool %p ref (new value %d)", pool, atomic_read(&pool->sgv_pool_ref)-1); if (atomic_dec_and_test(&pool->sgv_pool_ref)) - sgv_pool_destroy(pool); + sgv_pool_deinit_put(pool); return; } EXPORT_SYMBOL(sgv_pool_put); @@ -1570,7 +1582,7 @@ EXPORT_SYMBOL(sgv_pool_del); /* Both parameters in pages */ int scst_sgv_pools_init(unsigned long mem_hwmark, unsigned long mem_lwmark) { - int res; + int res = 0; TRACE_ENTRY(); @@ -1579,23 +1591,20 @@ int scst_sgv_pools_init(unsigned long mem_hwmark, unsigned long mem_lwmark) sgv_evaluate_local_max_pages(); - mutex_lock(&sgv_pools_mutex); + sgv_norm_pool = sgv_pool_create("sgv", sgv_no_clustering, 0, false, 0); + if (sgv_norm_pool == NULL) + goto out_err; - res = sgv_pool_init(&sgv_norm_pool, "sgv", sgv_no_clustering, 0, 0); - if (res != 0) - goto out_unlock; - - res = sgv_pool_init(&sgv_norm_clust_pool, "sgv-clust", - sgv_full_clustering, 0, 0); - if (res != 0) + sgv_norm_clust_pool = sgv_pool_create("sgv-clust", + sgv_full_clustering, 0, false, 0); + if (sgv_norm_clust_pool == NULL) goto out_free_norm; - res = sgv_pool_init(&sgv_dma_pool, "sgv-dma", sgv_no_clustering, 0, 0); - if (res != 0) + sgv_dma_pool = sgv_pool_create("sgv-dma", sgv_no_clustering, 0, + false, 0); + if (sgv_dma_pool == NULL) goto out_free_clust; - mutex_unlock(&sgv_pools_mutex); - #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)) sgv_shrinker = set_shrinker(DEFAULT_SEEKS, sgv_shrink); #else @@ -1609,13 +1618,13 @@ out: return res; out_free_clust: - sgv_pool_deinit(&sgv_norm_clust_pool); + sgv_pool_deinit_put(sgv_norm_clust_pool); out_free_norm: - sgv_pool_deinit(&sgv_norm_pool); + sgv_pool_deinit_put(sgv_norm_pool); -out_unlock: - mutex_unlock(&sgv_pools_mutex); +out_err: + res = -ENOMEM; goto out; } @@ -1629,9 +1638,9 @@ void scst_sgv_pools_deinit(void) unregister_shrinker(&sgv_shrinker); #endif - sgv_pool_deinit(&sgv_dma_pool); - sgv_pool_deinit(&sgv_norm_pool); - sgv_pool_deinit(&sgv_norm_clust_pool); + sgv_pool_deinit_put(sgv_dma_pool); + sgv_pool_deinit_put(sgv_norm_pool); + sgv_pool_deinit_put(sgv_norm_clust_pool); flush_scheduled_work(); @@ -1639,6 +1648,8 @@ void scst_sgv_pools_deinit(void) return; } +#ifdef CONFIG_SCST_PROC + static void sgv_do_proc_read(struct seq_file *seq, const struct sgv_pool *pool) { int i, total = 0, hit = 0, merged = 0, allocated = 0; @@ -1730,3 +1741,90 @@ int sgv_procinfo_show(struct seq_file *seq, void *v) TRACE_EXIT(); return 0; } + +#endif /* CONFIG_SCST_PROC */ + +ssize_t sgv_sysfs_stat_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct sgv_pool *pool; + int i, total = 0, hit = 0, merged = 0, allocated = 0; + int oa, om, res; + + pool = container_of(kobj, struct sgv_pool, sgv_kobj); + + for (i = 0; i < SGV_POOL_ELEMENTS; i++) { + int t; + + hit += atomic_read(&pool->cache_acc[i].hit_alloc); + total += atomic_read(&pool->cache_acc[i].total_alloc); + + t = atomic_read(&pool->cache_acc[i].total_alloc) - + atomic_read(&pool->cache_acc[i].hit_alloc); + allocated += t * (1 << i); + merged += atomic_read(&pool->cache_acc[i].merged); + } + + res = sprintf(buf, "%-30s %-11s %-11s %-11s %-11s", "Name", "Hit", "Total", + "% merged", "Cached (P/I/O)"); + + res += sprintf(&buf[res], "\n%-30s %-11d %-11d %-11d %d/%d/%d\n", + pool->name, hit, total, + (allocated != 0) ? merged*100/allocated : 0, + pool->cached_pages, pool->inactive_cached_pages, + pool->cached_entries); + + for (i = 0; i < SGV_POOL_ELEMENTS; i++) { + int t = atomic_read(&pool->cache_acc[i].total_alloc) - + atomic_read(&pool->cache_acc[i].hit_alloc); + allocated = t * (1 << i); + merged = atomic_read(&pool->cache_acc[i].merged); + + res += sprintf(&buf[res], " %-28s %-11d %-11d %d\n", + pool->cache_names[i], + atomic_read(&pool->cache_acc[i].hit_alloc), + atomic_read(&pool->cache_acc[i].total_alloc), + (allocated != 0) ? merged*100/allocated : 0); + } + + allocated = atomic_read(&pool->big_pages); + merged = atomic_read(&pool->big_merged); + oa = atomic_read(&pool->other_pages); + om = atomic_read(&pool->other_merged); + + res += sprintf(&buf[res], " %-40s %d/%-9d %d/%d\n", "big/other", + atomic_read(&pool->big_alloc), atomic_read(&pool->other_alloc), + (allocated != 0) ? merged*100/allocated : 0, + (oa != 0) ? om/oa : 0); + + return res; +} + +ssize_t sgv_sysfs_global_stat_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct sgv_pool *pool; + int inactive_pages = 0, res; + + TRACE_ENTRY(); + + spin_lock_bh(&sgv_pools_lock); + list_for_each_entry(pool, &sgv_active_pools_list, + sgv_active_pools_list_entry) { + inactive_pages += pool->inactive_cached_pages; + } + spin_unlock_bh(&sgv_pools_lock); + + res = sprintf(buf, "%-42s %d/%d\n%-42s %d/%d\n%-42s %d/%d\n" + "%-42s %-11d\n", + "Inactive/active pages", inactive_pages, + atomic_read(&sgv_pages_total) - inactive_pages, + "Hi/lo watermarks [pages]", sgv_hi_wmk, sgv_lo_wmk, + "Hi watermark releases/failures", + atomic_read(&sgv_releases_on_hiwmk), + atomic_read(&sgv_releases_on_hiwmk_failed), + "Other allocs", atomic_read(&sgv_other_total_alloc)); + + TRACE_EXIT(); + return res; +} diff --git a/scst/src/scst_mem.h b/scst/src/scst_mem.h index 2d1ba5c6a..1e3d9120f 100644 --- a/scst/src/scst_mem.h +++ b/scst/src/scst_mem.h @@ -17,7 +17,6 @@ #include #include -#include #define SGV_POOL_ELEMENTS 11 @@ -77,8 +76,9 @@ struct sgv_pool { int purge_interval; - /* Protected by sgv_pool_lock */ + /* Protected by sgv_pool_lock, if necessary */ unsigned int purge_work_scheduled:1; + unsigned int sgv_kobj_initialized:1; /* Protected by sgv_pool_lock */ struct list_head sorted_recycling_list; @@ -114,22 +114,28 @@ struct sgv_pool { struct mm_struct *owner_mm; struct list_head sgv_pools_list_entry; -}; -int sgv_pool_init(struct sgv_pool *pool, const char *name, - enum sgv_clustering_types clustering_type, int single_alloc_pages, - int purge_interval); -void sgv_pool_deinit(struct sgv_pool *pool); + struct kobject sgv_kobj; +}; static inline struct scatterlist *sgv_pool_sg(struct sgv_pool_obj *obj) { return obj->sg_entries; } -extern int scst_sgv_pools_init(unsigned long mem_hwmark, - unsigned long mem_lwmark); -extern void scst_sgv_pools_deinit(void); -extern int sgv_procinfo_show(struct seq_file *seq, void *v); +int scst_sgv_pools_init(unsigned long mem_hwmark, unsigned long mem_lwmark); +void scst_sgv_pools_deinit(void); + +void sgv_pool_destroy(struct sgv_pool *pool); + +#ifdef CONFIG_SCST_PROC +int sgv_procinfo_show(struct seq_file *seq, void *v); +#endif + +ssize_t sgv_sysfs_stat_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); +ssize_t sgv_sysfs_global_stat_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); void scst_sgv_pool_use_norm(struct scst_tgt_dev *tgt_dev); void scst_sgv_pool_use_norm_clust(struct scst_tgt_dev *tgt_dev); diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 4026412b3..0662e4f09 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -35,6 +35,10 @@ #include "scst_debug.h" +#ifdef CONFIG_SCST_PROC +void sgv_pool_destroy(struct sgv_pool *pool); +#endif + #define SCST_MAJOR 177 #define TRACE_RTRY 0x80000000 @@ -146,7 +150,9 @@ 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 scst_acg *scst_default_acg; +#endif extern spinlock_t scst_init_lock; extern struct list_head scst_init_cmd_list; @@ -287,6 +293,12 @@ void scst_del_dev_threads(struct scst_device *dev, int num); int scst_queue_retry_cmd(struct scst_cmd *cmd, int finished_cmds); +static inline void scst_tgtt_cleanup(struct scst_tgt_template *tgtt) { } +static inline void scst_devt_cleanup(struct scst_dev_type *devt) { } + +struct scst_tgt *scst_alloc_tgt(struct scst_tgt_template *tgtt); +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); @@ -298,6 +310,7 @@ struct scst_acg *scst_find_acg(const struct scst_session *sess); void scst_check_reassign_sessions(void); int scst_sess_alloc_tgt_devs(struct scst_session *sess); +void scst_sess_free_tgt_devs(struct scst_session *sess); void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA); int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev, @@ -305,6 +318,8 @@ int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev, int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev, bool gen_scst_report_luns_changed); +void scst_acg_dev_destroy(struct scst_acg_dev *acg_dev); + int scst_acg_add_name(struct scst_acg *acg, const char *name); int scst_acg_remove_name(struct scst_acg *acg, const char *name, bool reassign); void __scst_acg_remove_acn(struct scst_acn *n); @@ -320,6 +335,7 @@ int scst_assign_dev_handler(struct scst_device *dev, struct scst_session *scst_alloc_session(struct scst_tgt *tgt, gfp_t gfp_mask, const char *initiator_name); void scst_free_session(struct scst_session *sess); +void scst_release_session(struct scst_session *sess); void scst_free_session_callback(struct scst_session *sess); struct scst_cmd *scst_alloc_cmd(gfp_t gfp_mask); @@ -331,7 +347,6 @@ static inline void scst_destroy_cmd(struct scst_cmd *cmd) } void scst_check_retries(struct scst_tgt *tgt); -void scst_tgt_retry_timer_fn(unsigned long arg); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) int scst_alloc_request(struct scst_cmd *cmd); @@ -360,7 +375,7 @@ static inline int scst_exec_req(struct scsi_device *sdev, return scsi_execute_async(sdev, cmd, cmd_len, data_direction, (void *)sgl, bufflen, nents, timeout, retries, privdata, done, gfp); #elif !defined(SCSI_EXEC_REQ_FIFO_DEFINED) - WARN_ON_ONCE(1); + WARN_ON(); return -1; #else return scsi_execute_async_fifo(sdev, cmd, cmd_len, data_direction, @@ -399,6 +414,7 @@ struct scst_mgmt_cmd *scst_alloc_mgmt_cmd(gfp_t gfp_mask); void scst_free_mgmt_cmd(struct scst_mgmt_cmd *mcmd); void scst_done_cmd_mgmt(struct scst_cmd *cmd); +#ifdef CONFIG_SCST_PROC /* /proc support */ int scst_proc_init_module(void); void scst_proc_cleanup_module(void); @@ -408,6 +424,96 @@ int scst_build_proc_target_entries(struct scst_tgt *vtt); void scst_cleanup_proc_target_entries(struct scst_tgt *vtt); int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type); void scst_cleanup_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type); +#endif + +/* sysfs support */ +#ifdef CONFIG_SCST_PROC +static inline int scst_sysfs_init(void) +{ + return 0; +} +static inline void scst_sysfs_cleanup(void) { } + +static inline int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt) +{ + return 0; +} +static inline void scst_tgtt_sysfs_put(struct scst_tgt_template *tgtt) +{ + scst_tgtt_cleanup(tgtt); +} + +static inline int scst_create_tgt_sysfs(struct scst_tgt *tgt) +{ + return 0; +} +static inline void scst_tgt_sysfs_put(struct scst_tgt *tgt) +{ + scst_free_tgt(tgt); +} + +static inline int scst_create_sess_sysfs(struct scst_session *sess) +{ + return 0; +} +static inline void scst_sess_sysfs_put(struct scst_session *sess) +{ + scst_release_session(sess); +} + +static inline int scst_create_sgv_sysfs(struct sgv_pool *pool) +{ + return 0; +} +static inline void scst_sgv_sysfs_put(struct sgv_pool *pool) +{ + sgv_pool_destroy(pool); +} + +static inline int scst_create_devt_sysfs(struct scst_dev_type *devt) +{ + return 0; +} +static inline void scst_devt_sysfs_put(struct scst_dev_type *devt) +{ + scst_devt_cleanup(devt); +} + +static inline int scst_create_device_sysfs(struct scst_device *dev) +{ + return 0; +} +static inline void scst_device_sysfs_put(struct scst_device *dev) +{ + scst_free_device(dev); +} + +static inline int scst_create_devt_dev_sysfs(struct scst_device *dev) +{ + return 0; +} +static inline void scst_devt_dev_sysfs_put(struct scst_device *dev) { } + +#else /* CONFIG_SCST_PROC */ + +int scst_sysfs_init(void); +void scst_sysfs_cleanup(void); +int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt); +void scst_tgtt_sysfs_put(struct scst_tgt_template *tgtt); +int scst_create_tgt_sysfs(struct scst_tgt *tgt); +void scst_tgt_sysfs_put(struct scst_tgt *tgt); +int scst_create_sess_sysfs(struct scst_session *sess); +void scst_sess_sysfs_put(struct scst_session *sess); +int scst_create_sgv_sysfs(struct sgv_pool *pool); +void scst_sgv_sysfs_put(struct sgv_pool *pool); +int scst_create_devt_sysfs(struct scst_dev_type *devt); +void scst_devt_sysfs_put(struct scst_dev_type *devt); +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); + +#endif /* CONFIG_SCST_PROC */ int scst_get_cdb_len(const uint8_t *cdb); diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index ab6009f8e..b05532b52 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -92,7 +92,7 @@ static struct proc_dir_entry *scst_proc_groups_root; #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) static struct scst_proc_data scst_log_proc_data; -static struct scst_proc_log scst_proc_trace_tbl[] = +static struct scst_trace_log scst_proc_trace_tbl[] = { { TRACE_OUT_OF_MEM, "out_of_mem" }, { TRACE_MINOR, "minor" }, @@ -112,7 +112,7 @@ static struct scst_proc_log scst_proc_trace_tbl[] = { 0, NULL } }; -static struct scst_proc_log scst_proc_local_trace_tbl[] = +static struct scst_trace_log scst_proc_local_trace_tbl[] = { { TRACE_RTRY, "retry" }, { TRACE_SCSI_SERIALIZING, "scsi_serializing" }, @@ -242,13 +242,13 @@ static DEFINE_MUTEX(scst_log_mutex); int scst_proc_log_entry_write(struct file *file, const char __user *buf, unsigned long length, unsigned long *log_level, - unsigned long default_level, const struct scst_proc_log *tbl) + unsigned long default_level, const struct scst_trace_log *tbl) { int res = length; int action; unsigned long level = 0, oldlevel; char *buffer, *p, *e; - const struct scst_proc_log *t; + const struct scst_trace_log *t; char *data = (char *)PDE(file->f_dentry->d_inode)->data; TRACE_ENTRY(); @@ -1375,17 +1375,23 @@ int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type) { int res = 0; struct proc_dir_entry *p; + const char *name; /* workaround to keep /proc ABI intact */ TRACE_ENTRY(); sBUG_ON(dev_type->proc_dev_type_root); + if (strcmp(dev_type->name, "vdisk_fileio") == 0) + name = "vdisk"; + else + name = dev_type->name; + /* create the proc directory entry for the dev type handler */ - dev_type->proc_dev_type_root = proc_mkdir(dev_type->name, + dev_type->proc_dev_type_root = proc_mkdir(name, scst_proc_scsi_tgt); if (dev_type->proc_dev_type_root == NULL) { PRINT_ERROR("Not enough memory to register dev handler dir " - "%s in /proc/%s", dev_type->name, SCST_PROC_ENTRY_NAME); + "%s in /proc/%s", name, SCST_PROC_ENTRY_NAME); goto out_nomem; } @@ -1398,7 +1404,7 @@ int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type) PRINT_ERROR("Not enough memory to register dev " "handler entry %s in /proc/%s/%s", SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME, - SCST_PROC_ENTRY_NAME, dev_type->name); + SCST_PROC_ENTRY_NAME, name); goto out_remove; } } @@ -1407,12 +1413,12 @@ int scst_build_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type) /* create the proc file entry for the dev type handler */ scst_dev_handler_proc_data.data = (void *)dev_type; p = scst_create_proc_entry(dev_type->proc_dev_type_root, - dev_type->name, + name, &scst_dev_handler_proc_data); if (p == NULL) { PRINT_ERROR("Not enough memory to register dev " - "handler entry %s in /proc/%s/%s", dev_type->name, - SCST_PROC_ENTRY_NAME, dev_type->name); + "handler entry %s in /proc/%s/%s", name, + SCST_PROC_ENTRY_NAME, name); goto out_remove1; } } @@ -1427,7 +1433,7 @@ out_remove1: dev_type->proc_dev_type_root); out_remove: - remove_proc_entry(dev_type->name, scst_proc_scsi_tgt); + remove_proc_entry(name, scst_proc_scsi_tgt); out_nomem: res = -ENOMEM; @@ -1436,17 +1442,25 @@ out_nomem: void scst_cleanup_proc_dev_handler_dir_entries(struct scst_dev_type *dev_type) { + /* Workaround to keep /proc ABI intact */ + const char *name; + TRACE_ENTRY(); sBUG_ON(dev_type->proc_dev_type_root == NULL); + if (strcmp(dev_type->name, "vdisk_fileio") == 0) + name = "vdisk"; + else + name = dev_type->name; + if (dev_type->type >= 0) { remove_proc_entry(SCST_PROC_DEV_HANDLER_TYPE_ENTRY_NAME, dev_type->proc_dev_type_root); } if (dev_type->read_proc || dev_type->write_proc) - remove_proc_entry(dev_type->name, dev_type->proc_dev_type_root); - remove_proc_entry(dev_type->name, scst_proc_scsi_tgt); + remove_proc_entry(name, dev_type->proc_dev_type_root); + remove_proc_entry(name, scst_proc_scsi_tgt); dev_type->proc_dev_type_root = NULL; TRACE_EXIT(); @@ -2428,11 +2442,11 @@ static struct scst_proc_data scst_groups_devices_proc_data = { #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) -static int scst_proc_read_tlb(const struct scst_proc_log *tbl, +static int scst_proc_read_tlb(const struct scst_trace_log *tbl, struct seq_file *seq, unsigned long log_level, int *first) { - const struct scst_proc_log *t = tbl; + const struct scst_trace_log *t = tbl; int res = 0; while (t->token) { @@ -2446,7 +2460,7 @@ static int scst_proc_read_tlb(const struct scst_proc_log *tbl, } int scst_proc_log_entry_read(struct seq_file *seq, unsigned long log_level, - const struct scst_proc_log *tbl) + const struct scst_trace_log *tbl) { int res = 0, first = 1; @@ -2641,3 +2655,17 @@ int scst_single_seq_open(struct inode *inode, struct file *file) return single_open(file, pdata->show, PDE(inode)->data); } EXPORT_SYMBOL(scst_single_seq_open); + +struct proc_dir_entry *scst_proc_get_tgt_root( + struct scst_tgt_template *vtt) +{ + return vtt->proc_tgt_root; +} +EXPORT_SYMBOL(scst_proc_get_tgt_root); + +struct proc_dir_entry *scst_proc_get_dev_type_root( + struct scst_dev_type *dtt) +{ + return dtt->proc_dev_type_root; +} +EXPORT_SYMBOL(scst_proc_get_dev_type_root); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c new file mode 100644 index 000000000..d96c5a811 --- /dev/null +++ b/scst/src/scst_sysfs.c @@ -0,0 +1,1747 @@ +/* + * scst_sysfs.c + * + * Copyright (C) 2009 Daniel Henrique Debonzi + * Copyright (C) 2009 Vladislav Bolkhovitin + * Copyright (C) 2009 ID7 Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "scst.h" +#include "scst_priv.h" +#include "scst_mem.h" + +static DEFINE_MUTEX(scst_sysfs_mutex); + +static DECLARE_COMPLETION(scst_sysfs_root_release_completion); + +static struct kobject scst_sysfs_root_kobj; +static struct kobject *scst_targets_kobj; +static struct kobject *scst_devices_kobj; +static struct kobject *scst_sgv_kobj; +static struct kobject *scst_handlers_kobj; + +static struct sysfs_ops scst_sysfs_ops; + +static const char *scst_dev_handler_types[] = +{ + "Direct-access device (e.g., magnetic disk)", + "Sequential-access device (e.g., magnetic tape)", + "Printer device", + "Processor device", + "Write-once device (e.g., some optical disks)", + "CD-ROM device", + "Scanner device (obsolete)", + "Optical memory device (e.g., some optical disks)", + "Medium changer device (e.g., jukeboxes)", + "Communications device (obsolete)", + "Defined by ASC IT8 (Graphic arts pre-press devices)", + "Defined by ASC IT8 (Graphic arts pre-press devices)", + "Storage array controller device (e.g., RAID)", + "Enclosure services device", + "Simplified direct-access device (e.g., magnetic disk)", + "Optical card reader/writer device" +}; + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + +static DEFINE_MUTEX(scst_log_mutex); + +static struct scst_trace_log scst_trace_tbl[] = +{ + { TRACE_OUT_OF_MEM, "out_of_mem" }, + { TRACE_MINOR, "minor" }, + { TRACE_SG_OP, "sg" }, + { TRACE_MEMORY, "mem" }, + { TRACE_BUFF, "buff" }, + { TRACE_ENTRYEXIT, "entryexit" }, + { TRACE_PID, "pid" }, + { TRACE_LINE, "line" }, + { TRACE_FUNCTION, "function" }, + { TRACE_DEBUG, "debug" }, + { TRACE_SPECIAL, "special" }, + { TRACE_SCSI, "scsi" }, + { TRACE_MGMT, "mgmt" }, + { TRACE_MGMT_MINOR, "mgmt_minor" }, + { TRACE_MGMT_DEBUG, "mgmt_dbg" }, + { 0, NULL } +}; + +static struct scst_trace_log scst_local_trace_tbl[] = +{ + { TRACE_RTRY, "retry" }, + { TRACE_SCSI_SERIALIZING, "scsi_serializing" }, + { TRACE_RCV_BOT, "recv_bot" }, + { TRACE_SND_BOT, "send_bot" }, + { TRACE_RCV_TOP, "recv_top" }, + { TRACE_SND_TOP, "send_top" }, + { 0, NULL } +}; + +static ssize_t scst_trace_level_show(const struct scst_trace_log *local_tbl, + unsigned long log_level, char *buf, const char *help); +static int scst_write_trace(const char *buf, size_t length, + unsigned long *log_level, unsigned long default_level, + const char *name, const struct scst_trace_log *tbl); + +#endif /* defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */ + +static ssize_t scst_luns_mgmt_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf); +static ssize_t scst_luns_mgmt_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count); + +static void scst_sysfs_release(struct kobject *kobj) +{ + kfree(kobj); +} + +/* + * Target Template + */ + +static void scst_tgtt_release(struct kobject *kobj) +{ + struct scst_tgt_template *tgtt; + + TRACE_ENTRY(); + + tgtt = container_of(kobj, struct scst_tgt_template, tgtt_kobj); + + complete_all(&tgtt->tgtt_kobj_release_cmpl); + + scst_tgtt_cleanup(tgtt); + + TRACE_EXIT(); + return; +} + +static struct kobj_type tgtt_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_tgtt_release, +}; + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + +static ssize_t scst_tgtt_trace_level_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_tgt_template *tgtt; + + tgtt = container_of(kobj, struct scst_tgt_template, tgtt_kobj); + + return scst_trace_level_show(tgtt->trace_tbl, + tgtt->trace_flags ? *tgtt->trace_flags : 0, buf, + tgtt->trace_tbl_help); +} + +static ssize_t scst_tgtt_trace_level_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + struct scst_tgt_template *tgtt; + + TRACE_ENTRY(); + + tgtt = container_of(kobj, struct scst_tgt_template, tgtt_kobj); + + if (mutex_lock_interruptible(&scst_log_mutex) != 0) { + res = -EINTR; + goto out; + } + + res = scst_write_trace(buf, count, tgtt->trace_flags, + tgtt->default_trace_flags, tgtt->name, tgtt->trace_tbl); + + mutex_unlock(&scst_log_mutex); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static struct kobj_attribute tgtt_trace_attr = + __ATTR(trace_level, S_IRUGO | S_IWUSR, + scst_tgtt_trace_level_show, scst_tgtt_trace_level_store); + +#endif /* #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */ + +int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt) +{ + int retval = 0; + const struct attribute **pattr; + + TRACE_ENTRY(); + + init_completion(&tgtt->tgtt_kobj_release_cmpl); + + tgtt->tgtt_kobj_initialized = 1; + + retval = kobject_init_and_add(&tgtt->tgtt_kobj, &tgtt_ktype, + scst_targets_kobj, tgtt->name); + if (retval != 0) { + PRINT_ERROR("Can't add tgtt %s to sysfs", tgtt->name); + goto out; + } + + /* + * In case of errors there's no need for additional cleanup, because + * it will be done by the _put function() called by the caller. + */ + + pattr = tgtt->tgtt_attrs; + if (pattr != NULL) { + while (*pattr != NULL) { + TRACE_DBG("Creating attr %s for target driver %s", + (*pattr)->name, tgtt->name); + retval = sysfs_create_file(&tgtt->tgtt_kobj, *pattr); + if (retval != 0) { + PRINT_ERROR("Can't add attr %s for target " + "driver %s", (*pattr)->name, + tgtt->name); + goto out; + } + pattr++; + } + } + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + if (tgtt->trace_flags != NULL) { + retval = sysfs_create_file(&tgtt->tgtt_kobj, + &tgtt_trace_attr.attr); + if (retval != 0) { + PRINT_ERROR("Can't add trace_flag for target " + "driver %s", tgtt->name); + goto out; + } + } +#endif + +out: + TRACE_EXIT_RES(retval); + return retval; +} + +void scst_tgtt_sysfs_put(struct scst_tgt_template *tgtt) +{ + TRACE_ENTRY(); + + if (tgtt->tgtt_kobj_initialized) { + int rc; + + kobject_del(&tgtt->tgtt_kobj); + kobject_put(&tgtt->tgtt_kobj); + + rc = wait_for_completion_timeout(&tgtt->tgtt_kobj_release_cmpl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing sysfs entry " + "for target template %s...", tgtt->name); + wait_for_completion(&tgtt->tgtt_kobj_release_cmpl); + PRINT_INFO("Done waiting for releasing sysfs " + "entry for target template %s", tgtt->name); + } + } else + scst_tgtt_cleanup(tgtt); + + TRACE_EXIT(); + return; +} + +/* + * Target directory implementation + */ + +static void scst_tgt_release(struct kobject *kobj) +{ + struct scst_tgt *tgt; + + TRACE_ENTRY(); + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + + scst_free_tgt(tgt); + + TRACE_EXIT(); + return; +} + +static struct kobj_type tgt_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_tgt_release, +}; + +static struct kobj_attribute scst_luns_mgmt = + __ATTR(mgmt, S_IRUGO | S_IWUSR, scst_luns_mgmt_show, + scst_luns_mgmt_store); + +static ssize_t scst_tgt_enable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_tgt *tgt; + int res; + bool enabled; + + TRACE_ENTRY(); + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + + enabled = tgt->tgtt->is_tgt_enabled(tgt); + + res = sprintf(buf, "%d\n", enabled ? 1 : 0); + + TRACE_EXIT_RES(res); + return res; +} + +static ssize_t scst_tgt_enable_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + struct scst_tgt *tgt; + + TRACE_ENTRY(); + + if (buf == NULL) + goto out_err; + + tgt = container_of(kobj, struct scst_tgt, tgt_kobj); + + res = tgt->tgtt->enable_tgt(tgt, buf, count); + +out: + TRACE_EXIT_RES(res); + return res; + +out_err: + PRINT_ERROR("%s: Requested action not understood: %s", __func__, buf); + res = -EINVAL; + goto out; +} + +static struct kobj_attribute tgt_enable_attr = + __ATTR(enabled, S_IRUGO | S_IWUSR, + scst_tgt_enable_show, scst_tgt_enable_store); + +int scst_create_tgt_sysfs(struct scst_tgt *tgt) +{ + int retval; + const struct attribute **pattr; + + TRACE_ENTRY(); + + tgt->tgt_kobj_initialized = 1; + + retval = kobject_init_and_add(&tgt->tgt_kobj, &tgt_ktype, + &tgt->tgtt->tgtt_kobj, tgt->tgt_name); + if (retval != 0) { + PRINT_ERROR("Can't add tgt %s to sysfs", tgt->tgt_name); + goto out; + } + + /* + * In case of errors there's no need for additional cleanup, because + * it will be done by the _put function() called by the caller. + */ + + if ((tgt->tgtt->enable_tgt != NULL) && + (tgt->tgtt->is_tgt_enabled != NULL)) { + retval = sysfs_create_file(&tgt->tgt_kobj, + &tgt_enable_attr.attr); + if (retval != 0) { + PRINT_ERROR("Can't add attr %s to sysfs", + tgt_enable_attr.attr.name); + goto out; + } + } + + tgt->tgt_sess_kobj = kobject_create_and_add("sessions", &tgt->tgt_kobj); + if (tgt->tgt_sess_kobj == NULL) { + PRINT_ERROR("Can't create sess kobj for tgt %s", tgt->tgt_name); + goto out_nomem; + } + + tgt->tgt_luns_kobj = kobject_create_and_add("luns", &tgt->tgt_kobj); + if (tgt->tgt_luns_kobj == NULL) { + PRINT_ERROR("Can't create luns kobj for tgt %s", tgt->tgt_name); + goto out_nomem; + } + + retval = sysfs_create_file(tgt->tgt_luns_kobj, &scst_luns_mgmt.attr); + if (retval != 0) { + PRINT_ERROR("Can't add tgt attr %s for tgt %s", + scst_luns_mgmt.attr.name, tgt->tgt_name); + goto out; + } + + tgt->tgt_ini_grp_kobj = kobject_create_and_add("ini_group", + &tgt->tgt_kobj); + if (tgt->tgt_ini_grp_kobj == NULL) { + PRINT_ERROR("Can't create ini_grp kobj for tgt %s", + tgt->tgt_name); + goto out_nomem; + } + + pattr = tgt->tgtt->tgt_attrs; + if (pattr != NULL) { + while (*pattr != NULL) { + TRACE_DBG("Creating attr %s for tgt %s", (*pattr)->name, + tgt->tgt_name); + retval = sysfs_create_file(&tgt->tgt_kobj, *pattr); + if (retval != 0) { + PRINT_ERROR("Can't add tgt attr %s for tgt %s", + (*pattr)->name, tgt->tgt_name); + goto out; + } + pattr++; + } + } + +out: + TRACE_EXIT_RES(retval); + return retval; + +out_nomem: + retval = -ENOMEM; + goto out; +} + +void scst_tgt_sysfs_put(struct scst_tgt *tgt) +{ + if (tgt->tgt_kobj_initialized) { + 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); + + kobject_del(tgt->tgt_ini_grp_kobj); + kobject_put(tgt->tgt_ini_grp_kobj); + + kobject_del(&tgt->tgt_kobj); + kobject_put(&tgt->tgt_kobj); + } else + scst_free_tgt(tgt); + return; +} + +/* + * Devices directory implementation + */ + +ssize_t scst_device_sysfs_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos = 0; + + struct scst_device *dev; + + dev = container_of(kobj, struct scst_device, dev_kobj); + + pos = sprintf(buf, "%d - %s\n", dev->type, + (unsigned)dev->type > ARRAY_SIZE(scst_dev_handler_types) ? + "unknown" : scst_dev_handler_types[dev->type]); + + return pos; +} + +static struct kobj_attribute device_type_attr = + __ATTR(type, S_IRUGO, scst_device_sysfs_type_show, NULL); + +static struct attribute *scst_device_attrs[] = { + &device_type_attr.attr, + NULL, +}; + +static void scst_sysfs_device_release(struct kobject *kobj) +{ + struct scst_device *dev; + + TRACE_ENTRY(); + + dev = container_of(kobj, struct scst_device, dev_kobj); + + scst_free_device(dev); + + TRACE_EXIT(); + return; +} + +int scst_create_devt_dev_sysfs(struct scst_device *dev) +{ + int retval = 0; + const struct attribute **pattr; + + TRACE_ENTRY(); + + if (dev->handler == &scst_null_devtype) + goto out; + + sBUG_ON(!dev->handler->devt_kobj_initialized); + + /* + * In case of errors there's no need for additional cleanup, because + * it will be done by the _put function() called by the caller. + */ + + retval = sysfs_create_link(&dev->dev_kobj, + &dev->handler->devt_kobj, "handler"); + if (retval != 0) { + PRINT_ERROR("Can't create handler link for dev %s", + dev->virt_name); + goto out; + } + + pattr = dev->handler->dev_attrs; + if (pattr != NULL) { + while (*pattr != NULL) { + retval = sysfs_create_file(&dev->dev_kobj, *pattr); + if (retval != 0) { + PRINT_ERROR("Can't add dev attr %s for dev %s", + (*pattr)->name, dev->virt_name); + goto out; + } + pattr++; + } + } + +out: + TRACE_EXIT_RES(retval); + return retval; +} + +void scst_devt_dev_sysfs_put(struct scst_device *dev) +{ + const struct attribute **pattr; + + TRACE_ENTRY(); + + if (dev->handler == &scst_null_devtype) + goto out; + + sBUG_ON(!dev->handler->devt_kobj_initialized); + + pattr = dev->handler->dev_attrs; + if (pattr != NULL) { + while (*pattr != NULL) { + sysfs_remove_file(&dev->dev_kobj, *pattr); + pattr++; + } + } + + sysfs_remove_link(&dev->dev_kobj, "handler"); + +out: + TRACE_EXIT(); + return; +} + +static struct kobj_type scst_device_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_sysfs_device_release, + .default_attrs = scst_device_attrs, +}; + +int scst_create_device_sysfs(struct scst_device *dev) +{ + int retval = 0; + + TRACE_ENTRY(); + + dev->dev_kobj_initialized = 1; + + retval = kobject_init_and_add(&dev->dev_kobj, &scst_device_ktype, + scst_devices_kobj, dev->virt_name); + if (retval != 0) { + PRINT_ERROR("Can't add device %s to sysfs", dev->virt_name); + goto out; + } + + /* + * In case of errors there's no need for additional cleanup, because + * it will be done by the _put function() called by the caller. + */ + + dev->dev_exp_kobj = kobject_create_and_add("exported", + &dev->dev_kobj); + if (dev->dev_exp_kobj == NULL) { + PRINT_ERROR("Can't create exported link for device %s", + dev->virt_name); + retval = -ENOMEM; + goto out; + } + + if (dev->scsi_dev != NULL) { + retval = sysfs_create_link(&dev->dev_kobj, + &dev->scsi_dev->sdev_dev.kobj, "scsi_device"); + if (retval != 0) { + PRINT_ERROR("Can't create scsi_device link for dev %s", + dev->virt_name); + goto out; + } + } + +out: + TRACE_EXIT_RES(retval); + return retval; +} + +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_kobj); + kobject_put(&dev->dev_kobj); + } else + scst_free_device(dev); + + TRACE_EXIT(); + return; +} + +/* + * Target sessions directory implementation + */ + +ssize_t scst_sess_sysfs_commands_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_session *sess; + + sess = container_of(kobj, struct scst_session, sess_kobj); + + return sprintf(buf, "%i\n", atomic_read(&sess->sess_cmd_count)); +} + +static struct kobj_attribute session_commands_attr = + __ATTR(commands, S_IRUGO, scst_sess_sysfs_commands_show, NULL); + +static struct attribute *scst_session_attrs[] = { + &session_commands_attr.attr, + NULL, +}; + +static void scst_sysfs_session_release(struct kobject *kobj) +{ + struct scst_session *sess; + + TRACE_ENTRY(); + + sess = container_of(kobj, struct scst_session, sess_kobj); + + scst_release_session(sess); + + TRACE_EXIT(); + return; +} + +static struct kobj_type scst_session_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_sysfs_session_release, + .default_attrs = scst_session_attrs, +}; + +int scst_create_sess_sysfs(struct scst_session *sess) +{ + int retval = 0; + + TRACE_ENTRY(); + + sess->sess_kobj_initialized = 1; + + retval = kobject_init_and_add(&sess->sess_kobj, &scst_session_ktype, + sess->tgt->tgt_sess_kobj, sess->initiator_name); + if (retval != 0) { + PRINT_ERROR("Can't add session %s to sysfs", + sess->initiator_name); + goto out; + } + + /* + * In case of errors there's no need for additional cleanup, because + * it will be done by the _put function() called by the caller. + */ + +out: + TRACE_EXIT_RES(retval); + return retval; +} + +void scst_sess_sysfs_put(struct scst_session *sess) +{ + TRACE_ENTRY(); + + if (sess->sess_kobj_initialized) { + kobject_del(&sess->sess_kobj); + kobject_put(&sess->sess_kobj); + } else + scst_release_session(sess); + + TRACE_EXIT(); + return; +} + +/* + * Target luns directory implementation + */ + +static void scst_acg_dev_release(struct kobject *kobj) +{ + struct scst_acg_dev *acg_dev; + + TRACE_ENTRY(); + + acg_dev = container_of(kobj, struct scst_acg_dev, acg_dev_kobj); + + scst_acg_dev_destroy(acg_dev); + + TRACE_EXIT(); + return; +} + +static ssize_t scst_lun_rd_only_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct scst_acg_dev *acg_dev; + + acg_dev = container_of(kobj, struct scst_acg_dev, acg_dev_kobj); + + return sprintf(buf, "%d\n", + (acg_dev->rd_only || acg_dev->dev->rd_only) ? 1 : 0); +} + +static struct kobj_attribute lun_options_attr = + __ATTR(read_only, S_IRUGO, scst_lun_rd_only_show, NULL); + +static struct attribute *lun_attrs[] = { + &lun_options_attr.attr, + NULL, +}; + +static struct kobj_type acg_dev_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_acg_dev_release, + .default_attrs = lun_attrs, +}; + +int scst_create_acg_dev_sysfs(struct scst_acg *acg, unsigned int virt_lun, + struct kobject *parent) +{ + int retval; + struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp; + char str[10]; + + TRACE_ENTRY(); + + list_for_each_entry(acg_dev_tmp, &acg->acg_dev_list, + acg_dev_list_entry) { + if (acg_dev_tmp->lun == virt_lun) { + acg_dev = acg_dev_tmp; + break; + } + } + if (acg_dev == NULL) { + PRINT_ERROR("%s", "acg_dev lookup for kobject creation failed"); + retval = -EINVAL; + goto out; + } + + snprintf(str, sizeof(str), "%u", acg_dev->dev->dev_exported_lun_num++); + + acg_dev->acg_dev_kobj_initialized = 1; + + retval = kobject_init_and_add(&acg_dev->acg_dev_kobj, &acg_dev_ktype, + parent, "%u", virt_lun); + if (retval != 0) { + PRINT_ERROR("Can't add acg %s to sysfs", acg->acg_name); + goto out; + } + + /* + * In case of errors there's no need for additional cleanup, because + * it will be done by the _put function() called by the caller. + */ + + retval = sysfs_create_link(acg_dev->dev->dev_exp_kobj, + &acg_dev->acg_dev_kobj, str); + if (retval != 0) { + PRINT_ERROR("Can't create acg %s LUN link", acg->acg_name); + goto out; + } + + retval = sysfs_create_link(&acg_dev->acg_dev_kobj, + &acg_dev->dev->dev_kobj, "device"); + if (retval != 0) { + PRINT_ERROR("Can't create acg %s device link", acg->acg_name); + goto out; + } + +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) +{ + 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 + + 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; + 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_LUN_ACTION_ADD; + } else if (strncasecmp("del", p, 3) == 0) { + p += 3; + action = SCST_LUN_ACTION_DEL; + } 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; + + if (mutex_lock_interruptible(&scst_mutex) != 0) { + res = -EINTR; + 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'; + } + + 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); + } + res = -EINVAL; + goto out_free_up; + } + + switch (action) { + case SCST_LUN_ACTION_ADD: + e++; + while (isspace(*e) && *e != '\0') + e++; + virt_lun = simple_strtoul(e, &e, 0); + + while (isspace(*e) && *e != '\0') + e++; + + if (*e != '\0') { + if ((strncasecmp("READ_ONLY", e, 9) == 0) && + (isspace(e[9]) || (e[9] == '\0'))) + read_only = 1; + else { + PRINT_ERROR("Unknown option \"%s\"", e); + res = -EINVAL; + goto out_free_up; + } + } + + acg_dev = NULL; + list_for_each_entry(acg_dev_tmp, &acg->acg_dev_list, + acg_dev_list_entry) { + if (acg_dev_tmp->lun == virt_lun) { + acg_dev = acg_dev_tmp; + break; + } + } + + if (acg_dev != NULL) { + acg_dev = acg_dev_tmp; + PRINT_ERROR("virt lun %d already exists in group %s", + virt_lun, acg->acg_name); + res = -EINVAL; + goto out_free_up; + } + + res = scst_acg_add_dev(acg, dev, virt_lun, read_only, true); + if (res != 0) + goto out_free_up; + + res = scst_create_acg_dev_sysfs(acg, virt_lun, kobj); + if (res != 0) { + PRINT_ERROR("%s", "Creation of acg_dev kobject failed"); + goto out_remove_acg_dev; + } + break; + case SCST_LUN_ACTION_DEL: + res = scst_acg_remove_dev(acg, dev, true); + 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; + +out_remove_acg_dev: + scst_acg_remove_dev(acg, dev, true); + goto out_free_up; + +#undef SCST_LUN_ACTION_ADD +#undef SCST_LUN_ACTION_DEL +} + +/* + * SGV directory implementation + */ + +static struct kobj_attribute sgv_stat_attr = + __ATTR(stats, S_IRUGO, sgv_sysfs_stat_show, NULL); + +static struct attribute *sgv_attrs[] = { + &sgv_stat_attr.attr, + NULL, +}; + +static void sgv_kobj_release(struct kobject *kobj) +{ + struct sgv_pool *pool; + + TRACE_ENTRY(); + + pool = container_of(kobj, struct sgv_pool, sgv_kobj); + + sgv_pool_destroy(pool); + + TRACE_EXIT(); + return; +} + +static struct kobj_type sgv_pool_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = sgv_kobj_release, + .default_attrs = sgv_attrs, +}; + +int scst_create_sgv_sysfs(struct sgv_pool *pool) +{ + int retval; + + TRACE_ENTRY(); + + pool->sgv_kobj_initialized = 1; + + retval = kobject_init_and_add(&pool->sgv_kobj, &sgv_pool_ktype, + scst_sgv_kobj, pool->name); + if (retval != 0) { + PRINT_ERROR("Can't add sgv pool %s to sysfs", pool->name); + goto out; + } + +out: + TRACE_EXIT_RES(retval); + return retval; +} + +/* pool can be dead upon exit from this function! */ +void scst_sgv_sysfs_put(struct sgv_pool *pool) +{ + if (pool->sgv_kobj_initialized) { + kobject_del(&pool->sgv_kobj); + kobject_put(&pool->sgv_kobj); + } else + sgv_pool_destroy(pool); + return; +} + +static struct kobj_attribute sgv_global_stat_attr = + __ATTR(global_stats, S_IRUGO, sgv_sysfs_global_stat_show, NULL); + +static struct attribute *sgv_default_attrs[] = { + &sgv_global_stat_attr.attr, + NULL, +}; + +static struct kobj_type sgv_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_sysfs_release, + .default_attrs = sgv_default_attrs, +}; + +/* + * SCST sysfs root directory implementation + */ + +static ssize_t scst_threads_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int count; + + TRACE_ENTRY(); + + count = sprintf(buf, "%d\n", scst_global_threads_count()); + + TRACE_EXIT(); + return count; +} + +static ssize_t scst_threads_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res = count; + int oldtn, newtn, delta; + + TRACE_ENTRY(); + + if (mutex_lock_interruptible(&scst_sysfs_mutex) != 0) { + res = -EINTR; + goto out; + } + + mutex_lock(&scst_global_threads_mutex); + + oldtn = scst_nr_global_threads; + sscanf(buf, "%du", &newtn); + + if (newtn <= 0) { + PRINT_ERROR("Illegal threads num value %d", newtn); + res = -EINVAL; + goto out_up_thr_free; + } + delta = newtn - oldtn; + if (delta < 0) + __scst_del_global_threads(-delta); + else + __scst_add_global_threads(delta); + + PRINT_INFO("Changed cmd threads num: old %d, new %d", oldtn, newtn); + +out_up_thr_free: + mutex_unlock(&scst_global_threads_mutex); + + mutex_unlock(&scst_sysfs_mutex); + +out: + TRACE_EXIT_RES(res); + return res; +} + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + +static void scst_read_trace_tlb(const struct scst_trace_log *tbl, char *buf, + unsigned long log_level, int *pos) +{ + const struct scst_trace_log *t = tbl; + + if (t == NULL) + goto out; + + while (t->token) { + if (log_level & t->val) { + *pos += sprintf(&buf[*pos], "%s%s", + (*pos == 0) ? "" : " | ", + t->token); + } + t++; + } +out: + return; +} + +static ssize_t scst_trace_level_show(const struct scst_trace_log *local_tbl, + unsigned long log_level, char *buf, const char *help) +{ + int pos = 0; + + scst_read_trace_tlb(scst_trace_tbl, buf, log_level, &pos); + scst_read_trace_tlb(local_tbl, buf, log_level, &pos); + + pos += sprintf(&buf[pos], "\n\n\nUsage:\n" + " echo \"all|none|default\" >trace_level\n" + " echo \"value DEC|0xHEX|0OCT\" >trace_level\n" + " echo \"add|del TOKEN\" >trace_level\n" + "\nwhere TOKEN is one of [debug, function, line, pid,\n" + " entryexit, buff, mem, sg, out_of_mem,\n" + " special, scsi, mgmt, minor,\n" + " mgmt_minor, mgmt_dbg, scsi_serializing,\n" + " retry, recv_bot, send_bot, recv_top,\n" + " send_top%s]", help != NULL ? help : ""); + + return pos; +} + +static ssize_t scst_main_trace_level_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return scst_trace_level_show(scst_local_trace_tbl, trace_flag, + buf, NULL); +} + +static int scst_write_trace(const char *buf, size_t length, + unsigned long *log_level, unsigned long default_level, + const char *name, const struct scst_trace_log *tbl) +{ + int res = length; + int action; + unsigned long level = 0, oldlevel; + char *buffer, *p, *e; + const struct scst_trace_log *t; + +#define SCST_TRACE_ACTION_ALL 1 +#define SCST_TRACE_ACTION_NONE 2 +#define SCST_TRACE_ACTION_DEFAULT 3 +#define SCST_TRACE_ACTION_ADD 4 +#define SCST_TRACE_ACTION_DEL 5 +#define SCST_TRACE_ACTION_VALUE 6 + + TRACE_ENTRY(); + + if ((buf == NULL) || (length == 0)) { + res = -EINVAL; + goto out; + } + + buffer = kmalloc(length+1, GFP_KERNEL); + if (buffer == NULL) { + PRINT_ERROR("Unable to alloc intermediate buffer (size %d)", + length+1); + res = -ENOMEM; + goto out; + } + memcpy(buffer, buf, length); + buffer[length] = '\0'; + + p = buffer; + if (!strncasecmp("all", p, 3)) { + action = SCST_TRACE_ACTION_ALL; + } else if (!strncasecmp("none", p, 4) || !strncasecmp("null", p, 4)) { + action = SCST_TRACE_ACTION_NONE; + } else if (!strncasecmp("default", p, 7)) { + action = SCST_TRACE_ACTION_DEFAULT; + } else if (!strncasecmp("add", p, 3)) { + p += 3; + action = SCST_TRACE_ACTION_ADD; + } else if (!strncasecmp("del", p, 3)) { + p += 3; + action = SCST_TRACE_ACTION_DEL; + } else if (!strncasecmp("value", p, 5)) { + p += 5; + action = SCST_TRACE_ACTION_VALUE; + } else { + if (p[strlen(p) - 1] == '\n') + p[strlen(p) - 1] = '\0'; + PRINT_ERROR("Unknown action \"%s\"", p); + res = -EINVAL; + goto out_free; + } + + switch (action) { + case SCST_TRACE_ACTION_ADD: + case SCST_TRACE_ACTION_DEL: + case SCST_TRACE_ACTION_VALUE: + if (!isspace(*p)) { + PRINT_ERROR("%s", "Syntax error"); + res = -EINVAL; + goto out_free; + } + } + + switch (action) { + case SCST_TRACE_ACTION_ALL: + level = TRACE_ALL; + break; + case SCST_TRACE_ACTION_DEFAULT: + level = default_level; + break; + case SCST_TRACE_ACTION_NONE: + level = TRACE_NULL; + break; + case SCST_TRACE_ACTION_ADD: + case SCST_TRACE_ACTION_DEL: + while (isspace(*p) && *p != '\0') + p++; + e = p; + while (!isspace(*e) && *e != '\0') + e++; + *e = 0; + if (tbl) { + t = tbl; + while (t->token) { + if (!strcasecmp(p, t->token)) { + level = t->val; + break; + } + t++; + } + } + if (level == 0) { + t = scst_trace_tbl; + while (t->token) { + if (!strcasecmp(p, t->token)) { + level = t->val; + break; + } + t++; + } + } + if (level == 0) { + PRINT_ERROR("Unknown token \"%s\"", p); + res = -EINVAL; + goto out_free; + } + break; + case SCST_TRACE_ACTION_VALUE: + while (isspace(*p) && *p != '\0') + p++; + level = simple_strtoul(p, NULL, 0); + break; + } + + oldlevel = *log_level; + + switch (action) { + case SCST_TRACE_ACTION_ADD: + *log_level |= level; + break; + case SCST_TRACE_ACTION_DEL: + *log_level &= ~level; + break; + default: + *log_level = level; + break; + } + + PRINT_INFO("Changed trace level for \"%s\": old 0x%08lx, new 0x%08lx", + name, oldlevel, *log_level); + +out_free: + kfree(buffer); +out: + TRACE_EXIT_RES(res); + return res; + +#undef SCST_TRACE_ACTION_ALL +#undef SCST_TRACE_ACTION_NONE +#undef SCST_TRACE_ACTION_DEFAULT +#undef SCST_TRACE_ACTION_ADD +#undef SCST_TRACE_ACTION_DEL +#undef SCST_TRACE_ACTION_VALUE +} + +static ssize_t scst_main_trace_level_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + + TRACE_ENTRY(); + + if (mutex_lock_interruptible(&scst_log_mutex) != 0) { + res = -EINTR; + goto out; + } + + res = scst_write_trace(buf, count, &trace_flag, + SCST_DEFAULT_LOG_FLAGS, "scst", scst_local_trace_tbl); + + mutex_unlock(&scst_log_mutex); + +out: + TRACE_EXIT_RES(res); + return res; +} + +#endif /* defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */ + +static ssize_t scst_version_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + TRACE_ENTRY(); + + sprintf(buf, "%s\n", SCST_VERSION_STRING); + +#ifdef CONFIG_SCST_STRICT_SERIALIZING + strcat(buf, "Strict serializing enabled\n"); +#endif + +#ifdef CONFIG_SCST_EXTRACHECKS + strcat(buf, "EXTRACHECKS\n"); +#endif + +#ifdef CONFIG_SCST_TRACING + strcat(buf, "TRACING\n"); +#endif + +#ifdef CONFIG_SCST_DEBUG + strcat(buf, "DEBUG\n"); +#endif + +#ifdef CONFIG_SCST_DEBUG_TM + strcat(buf, "DEBUG_TM\n"); +#endif + +#ifdef CONFIG_SCST_DEBUG_RETRY + strcat(buf, "DEBUG_RETRY\n"); +#endif + +#ifdef CONFIG_SCST_DEBUG_OOM + strcat(buf, "DEBUG_OOM\n"); +#endif + +#ifdef CONFIG_SCST_DEBUG_SN + strcat(buf, "DEBUG_SN\n"); +#endif + +#ifdef CONFIG_SCST_USE_EXPECTED_VALUES + strcat(buf, "USE_EXPECTED_VALUES\n"); +#endif + +#ifdef CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ + strcat(buf, "ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ\n"); +#endif + +#ifdef CONFIG_SCST_STRICT_SECURITY + strcat(buf, "SCST_STRICT_SECURITY\n"); +#endif + + TRACE_EXIT(); + return strlen(buf); +} + +static struct kobj_attribute scst_threads_attr = + __ATTR(threads, S_IRUGO | S_IWUSR, scst_threads_show, + scst_threads_store); + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) +static struct kobj_attribute scst_trace_level_attr = + __ATTR(trace_level, S_IRUGO | S_IWUSR, scst_main_trace_level_show, + scst_main_trace_level_store); +#endif + +static struct kobj_attribute scst_version_attr = + __ATTR(version, S_IRUGO, scst_version_show, NULL); + +static struct attribute *scst_sysfs_root_default_attrs[] = { + &scst_threads_attr.attr, +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + &scst_trace_level_attr.attr, +#endif + &scst_version_attr.attr, + NULL, +}; + +static void scst_sysfs_root_release(struct kobject *kobj) +{ + complete_all(&scst_sysfs_root_release_completion); +} + +static ssize_t scst_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct kobj_attribute *kobj_attr; + kobj_attr = container_of(attr, struct kobj_attribute, attr); + + return kobj_attr->show(kobj, kobj_attr, buf); +} + +static ssize_t scst_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct kobj_attribute *kobj_attr; + kobj_attr = container_of(attr, struct kobj_attribute, attr); + + return kobj_attr->store(kobj, kobj_attr, buf, count); +} + +static struct sysfs_ops scst_sysfs_ops = { + .show = scst_show, + .store = scst_store, +}; + +static struct kobj_type scst_sysfs_root_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_sysfs_root_release, + .default_attrs = scst_sysfs_root_default_attrs, +}; + +static void scst_devt_free(struct kobject *kobj) +{ + struct scst_dev_type *devt; + + TRACE_ENTRY(); + + devt = container_of(kobj, struct scst_dev_type, devt_kobj); + + complete_all(&devt->devt_kobj_release_compl); + + scst_devt_cleanup(devt); + + TRACE_EXIT(); + return; +} + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + +static ssize_t scst_devt_trace_level_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct scst_dev_type *devt; + + devt = container_of(kobj, struct scst_dev_type, devt_kobj); + + return scst_trace_level_show(devt->trace_tbl, + devt->trace_flags ? *devt->trace_flags : 0, buf, + devt->trace_tbl_help); +} + +static ssize_t scst_devt_trace_level_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + struct scst_dev_type *devt; + + TRACE_ENTRY(); + + devt = container_of(kobj, struct scst_dev_type, devt_kobj); + + if (mutex_lock_interruptible(&scst_log_mutex) != 0) { + res = -EINTR; + goto out; + } + + res = scst_write_trace(buf, count, devt->trace_flags, + devt->default_trace_flags, devt->name, devt->trace_tbl); + + mutex_unlock(&scst_log_mutex); + +out: + TRACE_EXIT_RES(res); + return res; +} + +static struct kobj_attribute devt_trace_attr = + __ATTR(trace_level, S_IRUGO | S_IWUSR, + scst_devt_trace_level_show, scst_devt_trace_level_store); + +#endif /* #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */ + +static ssize_t scst_devt_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos; + struct scst_dev_type *devt; + + devt = container_of(kobj, struct scst_dev_type, devt_kobj); + + pos = sprintf(buf, "%d - %s\n", devt->type, + (unsigned)devt->type > ARRAY_SIZE(scst_dev_handler_types) ? + "unknown" : scst_dev_handler_types[devt->type]); + + return pos; +} + +static struct kobj_attribute scst_devt_type_attr = + __ATTR(type, S_IRUGO, scst_devt_type_show, NULL); + +static struct attribute *scst_devt_default_attrs[] = { + &scst_devt_type_attr.attr, + NULL, +}; + +static struct kobj_type scst_devt_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_devt_free, + .default_attrs = scst_devt_default_attrs, +}; + +int scst_create_devt_sysfs(struct scst_dev_type *devt) +{ + int retval; + struct kobject *parent; + const struct attribute **pattr; + + TRACE_ENTRY(); + + init_completion(&devt->devt_kobj_release_compl); + + if (devt->parent != NULL) + parent = &devt->parent->devt_kobj; + else + parent = scst_handlers_kobj; + + devt->devt_kobj_initialized = 1; + + retval = kobject_init_and_add(&devt->devt_kobj, &scst_devt_ktype, + parent, devt->name); + if (retval != 0) { + PRINT_ERROR("Can't add devt %s to sysfs", devt->name); + goto out; + } + + /* + * In case of errors there's no need for additional cleanup, because + * it will be done by the _put function() called by the caller. + */ + + pattr = devt->devt_attrs; + if (pattr != NULL) { + while (*pattr != NULL) { + retval = sysfs_create_file(&devt->devt_kobj, *pattr); + if (retval != 0) { + PRINT_ERROR("Can't add devt attr %s for dev " + "handler %s", (*pattr)->name, + devt->name); + goto out; + } + pattr++; + } + } + +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + if (devt->trace_flags != NULL) { + retval = sysfs_create_file(&devt->devt_kobj, + &devt_trace_attr.attr); + if (retval != 0) { + PRINT_ERROR("Can't add devt trace_flag for dev " + "handler %s", devt->name); + goto out; + } + } +#endif + +out: + TRACE_EXIT_RES(retval); + return retval; +} + +void scst_devt_sysfs_put(struct scst_dev_type *devt) +{ + TRACE_ENTRY(); + + if (devt->devt_kobj_initialized) { + int rc; + + kobject_del(&devt->devt_kobj); + kobject_put(&devt->devt_kobj); + + rc = wait_for_completion_timeout(&devt->devt_kobj_release_compl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing sysfs entry " + "for dev handler template %s...", devt->name); + wait_for_completion(&devt->devt_kobj_release_compl); + PRINT_INFO("Done waiting for releasing sysfs entry " + "for dev handler template %s", devt->name); + } + } else + scst_devt_cleanup(devt); + + TRACE_EXIT(); + return; +} + +int __init scst_sysfs_init(void) +{ + int retval = 0; + + TRACE_ENTRY(); + + retval = kobject_init_and_add(&scst_sysfs_root_kobj, + &scst_sysfs_root_ktype, kernel_kobj, "%s", "scst_tgt"); + if (retval != 0) + goto sysfs_root_add_error; + + scst_targets_kobj = kobject_create_and_add("targets", + &scst_sysfs_root_kobj); + if (scst_targets_kobj == NULL) + goto targets_kobj_error; + + scst_devices_kobj = kobject_create_and_add("devices", + &scst_sysfs_root_kobj); + if (scst_devices_kobj == NULL) + goto devices_kobj_error; + + scst_sgv_kobj = kzalloc(sizeof(*scst_sgv_kobj), GFP_KERNEL); + if (scst_sgv_kobj == NULL) + goto sgv_kobj_error; + + retval = kobject_init_and_add(scst_sgv_kobj, &sgv_ktype, + &scst_sysfs_root_kobj, "%s", "sgv"); + if (retval != 0) + goto sgv_kobj_add_error; + + scst_handlers_kobj = kobject_create_and_add("handlers", + &scst_sysfs_root_kobj); + if (scst_handlers_kobj == NULL) + goto handlers_kobj_error; + + +out: + TRACE_EXIT_RES(retval); + return retval; + +handlers_kobj_error: + kobject_del(scst_sgv_kobj); + +sgv_kobj_add_error: + kobject_put(scst_sgv_kobj); + +sgv_kobj_error: + kobject_del(scst_devices_kobj); + kobject_put(scst_devices_kobj); + +devices_kobj_error: + kobject_del(scst_targets_kobj); + kobject_put(scst_targets_kobj); + +targets_kobj_error: + kobject_del(&scst_sysfs_root_kobj); + +sysfs_root_add_error: + kobject_put(&scst_sysfs_root_kobj); + + if (retval == 0) + retval = -EINVAL; + goto out; +} + +void __exit scst_sysfs_cleanup(void) +{ + TRACE_ENTRY(); + + PRINT_INFO("%s", "Exiting SCST sysfs hierarchy..."); + + kobject_del(scst_sgv_kobj); + kobject_put(scst_sgv_kobj); + + kobject_del(scst_devices_kobj); + kobject_put(scst_devices_kobj); + + kobject_del(scst_targets_kobj); + kobject_put(scst_targets_kobj); + + kobject_del(scst_handlers_kobj); + kobject_put(scst_handlers_kobj); + + kobject_del(&scst_sysfs_root_kobj); + kobject_put(&scst_sysfs_root_kobj); + + wait_for_completion(&scst_sysfs_root_release_completion); + + PRINT_INFO("%s", "Exiting SCST sysfs hierarchy done"); + + TRACE_EXIT(); + return; +} diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 7c2f59241..5b939e4fa 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -5356,6 +5356,7 @@ 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) { @@ -5376,6 +5377,7 @@ out: TRACE_EXIT_HRES(res); return res; } +#endif /* CONFIG_SCST_PROC */ /* Must be called under scst_mitex */ struct scst_acg *scst_find_acg(const struct scst_session *sess) @@ -5386,10 +5388,19 @@ struct scst_acg *scst_find_acg(const struct scst_session *sess) 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; + } +#else if (acg == NULL) - acg = scst_default_acg; + acg = sess->tgt->default_acg; +#endif TRACE_EXIT_HRES((unsigned long)acg); return acg; @@ -5397,7 +5408,7 @@ struct scst_acg *scst_find_acg(const struct scst_session *sess) static int scst_init_session(struct scst_session *sess) { - int res = 0; + int res = 0, rc; struct scst_cmd *cmd; struct scst_mgmt_cmd *mcmd, *tm; int mwake = 0; @@ -5418,6 +5429,11 @@ static int scst_init_session(struct scst_session *sess) res = scst_sess_alloc_tgt_devs(sess); + /* Let's always create session's sysfs to simplify error recovery */ + rc = scst_create_sess_sysfs(sess); + if (res == 0) + res = rc; + mutex_unlock(&scst_mutex); if (sess->init_result_fn) { @@ -5454,7 +5470,12 @@ restart: &scst_active_mgmt_cmd_list); mwake = 1; } + spin_unlock(&scst_mcmd_lock); + /* + * In case of an error at this point the caller target driver supposed + * to already call this sess's unregistration. + */ sess->init_phase = SCST_SESS_IPH_READY; spin_unlock_irq(&sess->sess_list_lock); diff --git a/usr-full_perf.patch b/usr-full_perf.patch deleted file mode 100644 index 71e45a543..000000000 --- a/usr-full_perf.patch +++ /dev/null @@ -1,16 +0,0 @@ -Index: usr/fileio/Makefile -=================================================================== ---- usr/fileio/Makefile (revision 324) -+++ usr/fileio/Makefile (working copy) -@@ -30,9 +30,9 @@ CFLAGS += -O2 -Wall -Wextra -Wno-unused- - PROGS = fileio_tgt - LIBS = -lpthread - --CFLAGS += -DEXTRACHECKS -+#CFLAGS += -DEXTRACHECKS - #CFLAGS += -DTRACING --CFLAGS += -DDEBUG -g -W -Wno-unused-parameter -+#CFLAGS += -DDEBUG -g -W -Wno-unused-parameter - - #CFLAGS += -DDEBUG_NOMEM - #CFLAGS += -DDEBUG_SENSE diff --git a/usr-release.patch b/usr-release.patch deleted file mode 100644 index cf03c3503..000000000 --- a/usr-release.patch +++ /dev/null @@ -1,17 +0,0 @@ -Index: usr/fileio/Makefile -=================================================================== ---- usr/fileio/Makefile (revision 324) -+++ usr/fileio/Makefile (working copy) -@@ -30,9 +30,9 @@ CFLAGS += -O2 -Wall -Wextra -Wno-unused- - PROGS = fileio_tgt - LIBS = -lpthread - --CFLAGS += -DEXTRACHECKS --#CFLAGS += -DTRACING --CFLAGS += -DDEBUG -g -W -Wno-unused-parameter -+#CFLAGS += -DEXTRACHECKS -+CFLAGS += -DTRACING -+#CFLAGS += -DDEBUG -g -W -Wno-unused-parameter - - #CFLAGS += -DDEBUG_NOMEM - #CFLAGS += -DDEBUG_SENSE diff --git a/usr/fileio/Makefile b/usr/fileio/Makefile index 9928402fe..7505b6602 100644 --- a/usr/fileio/Makefile +++ b/usr/fileio/Makefile @@ -32,7 +32,8 @@ LIBS = -lpthread CFLAGS += -DEXTRACHECKS #CFLAGS += -DTRACING -CFLAGS += -DDEBUG -g -W -Wno-unused-parameter +CFLAGS += -DDEBUG -g +CFLAGS += -W -Wno-unused-parameter #CFLAGS += -DDEBUG_NOMEM #CFLAGS += -DDEBUG_SENSE @@ -78,4 +79,36 @@ clean: extraclean: clean -.PHONY: all install uninstall clean extraclean +debug2release: + sed -c -i.aa s/"^CFLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#CFLAGS += \-DTRACING"/"CFLAGS += \-DTRACING"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^CFLAGS += \-DDEBUG -g"/"#CFLAGS += \-DDEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +release2debug: + sed -c -i.aa s/"#CFLAGS += \-DEXTRACHECKS"/"CFLAGS += \-DEXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^CFLAGS += \-DTRACING"/"#CFLAGS += \-DTRACING"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#CFLAGS += \-DDEBUG -g"/"CFLAGS += \-DDEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +debug2perf: + sed -c -i.aa s/"^CFLAGS += \-DEXTRACHECKS"/"#CFLAGS += \-DEXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"^CFLAGS += \-DDEBUG -g"/"#CFLAGS += \-DDEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +perf2debug: + sed -c -i.aa s/"#CFLAGS += \-DEXTRACHECKS"/"CFLAGS += \-DEXTRACHECKS"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + sed -c -i.aa s/"#CFLAGS += \-DDEBUG -g"/"CFLAGS += \-DDEBUG -g"/ Makefile + @cmp Makefile Makefile.aa >/dev/null; if [[ $$? = 0 ]]; then rm Makefile.aa; echo "sed failed"; false; fi + rm Makefile.aa + +.PHONY: all install uninstall clean extraclean debug2release release2debug debug2perf perf2debug