From 1607d12c9c3ca3f2c81f2c3cfe734eb0659ca225 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Fri, 20 Aug 2010 15:38:21 +0000 Subject: [PATCH] Sysfs cleanups and fixes, part 1 git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1962 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- iscsi-scst/kernel/conn.c | 201 +++--- iscsi-scst/kernel/iscsi.c | 2 + iscsi-scst/kernel/iscsi.h | 11 +- iscsi-scst/kernel/nthread.c | 4 - qla2x00t/qla2x00-target/qla2x00t.c | 14 +- scst/ToDo | 6 + scst/include/scst.h | 77 +-- scst/src/dev_handlers/scst_user.c | 4 +- scst/src/dev_handlers/scst_vdisk.c | 2 +- scst/src/scst_lib.c | 487 ++++++++------- scst/src/scst_main.c | 485 +++++++------- scst/src/scst_mem.c | 75 +-- scst/src/scst_mem.h | 6 +- scst/src/scst_priv.h | 139 ++--- scst/src/scst_proc.c | 44 +- scst/src/scst_sysfs.c | 972 ++++++++++++----------------- scst/src/scst_targ.c | 11 +- 17 files changed, 1160 insertions(+), 1380 deletions(-) diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index b39b2c83c..59e55f064 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -121,28 +121,20 @@ void conn_info_show(struct seq_file *seq, struct iscsi_session *session) #else /* CONFIG_SCST_PROC */ -static int conn_free(struct iscsi_conn *conn); - static void iscsi_conn_release(struct kobject *kobj) { struct iscsi_conn *conn; - struct iscsi_target *target; TRACE_ENTRY(); - conn = container_of(kobj, struct iscsi_conn, iscsi_conn_kobj); - target = conn->target; - - mutex_lock(&target->target_mutex); - conn_free(conn); - mutex_unlock(&target->target_mutex); + conn = container_of(kobj, struct iscsi_conn, conn_kobj); + complete_all(&conn->conn_kobj_release_cmpl); TRACE_EXIT(); return; } -static struct kobj_type iscsi_conn_ktype = { - .sysfs_ops = &scst_sysfs_ops, +struct kobj_type iscsi_conn_ktype = { .release = iscsi_conn_release, }; @@ -192,7 +184,7 @@ static ssize_t iscsi_conn_ip_show(struct kobject *kobj, TRACE_ENTRY(); - conn = container_of(kobj, struct iscsi_conn, iscsi_conn_kobj); + conn = container_of(kobj, struct iscsi_conn, conn_kobj); pos = iscsi_get_initiator_ip(conn, buf, SCST_SYSFS_BLOCK_SIZE); @@ -211,7 +203,7 @@ static ssize_t iscsi_conn_cid_show(struct kobject *kobj, TRACE_ENTRY(); - conn = container_of(kobj, struct iscsi_conn, iscsi_conn_kobj); + conn = container_of(kobj, struct iscsi_conn, conn_kobj); pos = sprintf(buf, "%u", conn->cid); @@ -230,7 +222,7 @@ static ssize_t iscsi_conn_state_show(struct kobject *kobj, TRACE_ENTRY(); - conn = container_of(kobj, struct iscsi_conn, iscsi_conn_kobj); + conn = container_of(kobj, struct iscsi_conn, conn_kobj); pos = print_conn_state(buf, SCST_SYSFS_BLOCK_SIZE, conn); @@ -241,6 +233,102 @@ static ssize_t iscsi_conn_state_show(struct kobject *kobj, static struct kobj_attribute iscsi_conn_state_attr = __ATTR(state, S_IRUGO, iscsi_conn_state_show, NULL); +static void conn_sysfs_del(struct iscsi_conn *conn) +{ + int rc; + + TRACE_ENTRY(); + + kobject_del(&conn->conn_kobj); + kobject_put(&conn->conn_kobj); + + rc = wait_for_completion_timeout(&conn->conn_kobj_release_cmpl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing sysfs entry " + "for conn %p (%d refs)...", conn, + atomic_read(&conn->conn_kobj.kref.refcount)); + wait_for_completion(&conn->conn_kobj_release_cmpl); + PRINT_INFO("Done waiting for releasing sysfs " + "entry for conn %p", conn); + } + + TRACE_EXIT(); + return; +} + +static int conn_sysfs_add(struct iscsi_conn *conn) +{ + int res; + struct iscsi_session *session = conn->session; + struct iscsi_conn *c; + int n = 1; + char addr[64]; + + TRACE_ENTRY(); + + iscsi_get_initiator_ip(conn, addr, sizeof(addr)); + +restart: + list_for_each_entry(c, &session->conn_list, conn_list_entry) { + if (strcmp(addr, kobject_name(&conn->conn_kobj)) == 0) { + char c_addr[64]; + + iscsi_get_initiator_ip(conn, c_addr, sizeof(c_addr)); + + TRACE_DBG("Duplicated conn from the same initiator " + "%s found", c_addr); + + snprintf(addr, sizeof(addr), "%s_%d", c_addr, n); + n++; + goto restart; + } + } + + init_completion(&conn->conn_kobj_release_cmpl); + + res = kobject_init_and_add(&conn->conn_kobj, &iscsi_conn_ktype, + scst_sysfs_get_sess_kobj(session->scst_sess), addr); + if (res != 0) { + PRINT_ERROR("Unable create sysfs entries for conn %s", + addr); + goto out; + } + + TRACE_DBG("conn %p, conn_kobj %p", conn, &conn->conn_kobj); + + res = sysfs_create_file(&conn->conn_kobj, + &iscsi_conn_state_attr.attr); + if (res != 0) { + PRINT_ERROR("Unable create sysfs attribute %s for conn %s", + iscsi_conn_state_attr.attr.name, addr); + goto out_err; + } + + res = sysfs_create_file(&conn->conn_kobj, + &iscsi_conn_cid_attr.attr); + if (res != 0) { + PRINT_ERROR("Unable create sysfs attribute %s for conn %s", + iscsi_conn_cid_attr.attr.name, addr); + goto out_err; + } + + res = sysfs_create_file(&conn->conn_kobj, + &iscsi_conn_ip_attr.attr); + if (res != 0) { + PRINT_ERROR("Unable create sysfs attribute %s for conn %s", + iscsi_conn_ip_attr.attr.name, addr); + goto out_err; + } + +out: + TRACE_EXIT_RES(res); + return res; + +out_err: + conn_sysfs_del(conn); + goto out; +} + #endif /* CONFIG_SCST_PROC */ /* target_mutex supposed to be locked */ @@ -665,19 +753,21 @@ out: } /* target_mutex supposed to be locked */ -#ifdef CONFIG_SCST_PROC int conn_free(struct iscsi_conn *conn) -#else -static int conn_free(struct iscsi_conn *conn) -#endif { struct iscsi_session *session = conn->session; + TRACE_ENTRY(); + TRACE_MGMT_DBG("Freeing conn %p (sess=%p, %#Lx %u)", conn, session, (long long unsigned int)session->sid, conn->cid); del_timer_sync(&conn->rsp_timer); +#ifndef CONFIG_SCST_PROC + conn_sysfs_del(conn); +#endif + sBUG_ON(atomic_read(&conn->conn_ref_cnt) != 0); sBUG_ON(!list_empty(&conn->cmd_list)); sBUG_ON(!list_empty(&conn->write_list)); @@ -722,11 +812,6 @@ static int iscsi_conn_alloc(struct iscsi_session *session, { struct iscsi_conn *conn; int res = 0; -#ifndef CONFIG_SCST_PROC - struct iscsi_conn *c; - int n = 1; - char addr[64]; -#endif conn = kzalloc(sizeof(*conn), GFP_KERNEL); if (!conn) { @@ -758,7 +843,7 @@ static int iscsi_conn_alloc(struct iscsi_session *session, conn->ddigest_type = session->sess_params.data_digest; res = digest_init(conn); if (res != 0) - goto out_err_free1; + goto out_free_iov; conn->target = session->target; spin_lock_init(&conn->cmd_list_lock); @@ -794,61 +879,13 @@ static int iscsi_conn_alloc(struct iscsi_session *session, res = conn_setup_sock(conn); if (res != 0) - goto out_err_free2; + goto out_fput; #ifndef CONFIG_SCST_PROC - iscsi_get_initiator_ip(conn, addr, sizeof(addr)); - -restart: - list_for_each_entry(c, &session->conn_list, conn_list_entry) { - if (strcmp(addr, kobject_name(&conn->iscsi_conn_kobj)) == 0) { - char c_addr[64]; - - iscsi_get_initiator_ip(conn, c_addr, sizeof(c_addr)); - - TRACE_DBG("Duplicated conn from the same initiator " - "%s found", c_addr); - - snprintf(addr, sizeof(addr), "%s_%d", c_addr, n); - n++; - goto restart; - } - } - - res = kobject_init_and_add(&conn->iscsi_conn_kobj, &iscsi_conn_ktype, - scst_sysfs_get_sess_kobj(session->scst_sess), addr); - if (res != 0) { - PRINT_ERROR("Unable create sysfs entries for conn %s", - addr); - goto out_err_free2; - } - - TRACE_DBG("conn %p, iscsi_conn_kobj %p", conn, &conn->iscsi_conn_kobj); - - res = sysfs_create_file(&conn->iscsi_conn_kobj, - &iscsi_conn_state_attr.attr); - if (res != 0) { - PRINT_ERROR("Unable create sysfs attribute %s for conn %s", - iscsi_conn_state_attr.attr.name, addr); - goto out_err_free3; - } - - res = sysfs_create_file(&conn->iscsi_conn_kobj, - &iscsi_conn_cid_attr.attr); - if (res != 0) { - PRINT_ERROR("Unable create sysfs attribute %s for conn %s", - iscsi_conn_cid_attr.attr.name, addr); - goto out_err_free3; - } - - res = sysfs_create_file(&conn->iscsi_conn_kobj, - &iscsi_conn_ip_attr.attr); - if (res != 0) { - PRINT_ERROR("Unable create sysfs attribute %s for conn %s", - iscsi_conn_ip_attr.attr.name, addr); - goto out_err_free3; - } -#endif /* CONFIG_SCST_PROC */ + res = conn_sysfs_add(conn); + if (res != 0) + goto out_fput; +#endif list_add_tail(&conn->conn_list_entry, &session->conn_list); @@ -857,16 +894,10 @@ restart: out: return res; -#ifndef CONFIG_SCST_PROC -out_err_free3: - kobject_put(&conn->iscsi_conn_kobj); - goto out; -#endif - -out_err_free2: +out_fput: fput(conn->file); -out_err_free1: +out_free_iov: free_page((unsigned long)conn->read_iov); out_err_free_conn: diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 6fd84da1b..d92a84f81 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -3881,6 +3881,8 @@ static int __init iscsi_init(void) err = iscsi_procfs_init(); if (err < 0) goto out_reg_tmpl; +#else + iscsi_conn_ktype.sysfs_ops = scst_sysfs_get_sysfs_ops(); #endif num = max((int)num_online_cpus(), 2); diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 3e316d24a..9267be740 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -88,7 +88,6 @@ struct iscsi_target { struct list_head target_list_entry; u32 tid; - /* Protected by iscsi_sysfs_mutex */ unsigned int tgt_enabled:1; #ifndef CONFIG_SCST_PROC @@ -281,8 +280,9 @@ struct iscsi_conn { u32 nop_in_ttt; #ifndef CONFIG_SCST_PROC - /* Doesn't need any protection */ - struct kobject iscsi_conn_kobj; + /* Don't need any protection */ + struct kobject conn_kobj; + struct completion conn_kobj_release_cmpl; #endif /* CONFIG_SCST_PROC */ }; @@ -504,13 +504,14 @@ extern int set_scst_preliminary_status_rsp(struct iscsi_cmnd *req, bool get_data, int key, int asc, int ascq); /* conn.c */ +#ifndef CONFIG_SCST_PROC +extern struct kobj_type iscsi_conn_ktype; +#endif extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16); extern void conn_reinst_finished(struct iscsi_conn *); extern int __add_conn(struct iscsi_session *, struct iscsi_kern_conn_info *); extern int __del_conn(struct iscsi_session *, struct iscsi_kern_conn_info *); -#ifdef CONFIG_SCST_PROC extern int conn_free(struct iscsi_conn *); -#endif extern void iscsi_make_conn_rd_active(struct iscsi_conn *conn); #define ISCSI_CONN_ACTIVE_CLOSE 1 #define ISCSI_CONN_DELETING 2 diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 563cff6bb..96cd15e7a 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -562,13 +562,9 @@ static void close_conn(struct iscsi_conn *conn) sid = session->sid; cid = conn->cid; -#ifdef CONFIG_SCST_PROC mutex_lock(&target->target_mutex); conn_free(conn); mutex_unlock(&target->target_mutex); -#else - kobject_put(&conn->iscsi_conn_kobj); -#endif /* * We can't send E_CONN_CLOSE earlier, because otherwise we would have diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index dc05456a3..0efc2a135 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -283,7 +283,7 @@ static inline int q2t_issue_marker(scsi_qla_host_t *ha, int ha_locked) */ static int q2t_target_detect(struct scst_tgt_template *tgtt) { - int res; + int res, rc; struct qla_tgt_data t = { .magic = QLA2X_TARGET_MAGIC, .tgt24_atio_pkt = q24_atio_pkt, @@ -297,21 +297,23 @@ static int q2t_target_detect(struct scst_tgt_template *tgtt) TRACE_ENTRY(); - res = qla2xxx_tgt_register_driver(&t); - if (res < 0) { + rc = qla2xxx_tgt_register_driver(&t); + if (rc < 0) { + res = rc; PRINT_ERROR("Unable to register driver: %d", res); goto out; } - if (res != QLA2X_INITIATOR_MAGIC) { - PRINT_ERROR("Wrong version of the initiator part: %d", - res); + if (rc != QLA2X_INITIATOR_MAGIC) { + PRINT_ERROR("Wrong version of the initiator part: %d", rc); res = -EINVAL; goto out; } qla2xxx_add_targets(); + res = 0; + PRINT_INFO("%s", "Target mode driver for QLogic 2x00 controller " "registered successfully"); diff --git a/scst/ToDo b/scst/ToDo index dc16027f7..fa484869f 100644 --- a/scst/ToDo +++ b/scst/ToDo @@ -3,6 +3,12 @@ To be done - See http://scst.sourceforge.net/contributing.html + - Add choosing default number of threads for BLOCKIO based on "rotational" + sysfs attribute of the device. + + - Allow to set via sysfs of what returned for "rotational" attribute to + remote initiators. + - Remove cmd->data_len - Reimplement VDISK handler with usage of async. read/write operations diff --git a/scst/include/scst.h b/scst/include/scst.h index 59a140584..c3db75c18 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -953,11 +953,9 @@ struct scst_tgt_template { 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 */ + /* sysfs release completion */ struct completion tgtt_kobj_release_cmpl; #ifdef CONFIG_SCST_PROC @@ -1261,8 +1259,6 @@ struct scst_dev_type { 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 */ @@ -1281,12 +1277,11 @@ struct scst_tgt { struct scst_tgt_template *tgtt; /* corresponding target template */ - struct scst_acg *default_acg; /* The default acg for this target. */ +#ifndef CONFIG_SCST_PROC + struct scst_acg *default_acg; /* default acg for this target */ - /* - * Device ACG groups - */ - struct list_head tgt_acg_list; + struct list_head tgt_acg_list; /* target ACG groups */ +#endif /* * Maximum SG table size. Needed here, since different cards on the @@ -1328,17 +1323,8 @@ struct scst_tgt { char *default_group_name; #endif - /* Set if tgt_kobj was initialized */ - unsigned int tgt_kobj_initialized:1; - - /* Set if scst_tgt_sysfs_prepare_put() was called for tgt_kobj */ - unsigned int tgt_kobj_put_prepared:1; - - /* - * Used to protect sysfs attributes to be called after this - * object was unregistered. - */ - struct rw_semaphore tgt_attr_rwsem; + /* sysfs release completion */ + struct completion tgt_kobj_release_cmpl; struct kobject tgt_kobj; /* main targets/target kobject */ struct kobject *tgt_sess_kobj; /* target/sessions/ */ @@ -1469,14 +1455,10 @@ 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; + /* sysfs release completion */ + struct completion sess_kobj_release_cmpl; - /* - * Used to protect sysfs attributes to be called after this - * object was unregistered. - */ - struct rw_semaphore sess_attr_rwsem; + unsigned int sess_kobj_ready:1; struct kobject sess_kobj; /* kobject for this struct */ @@ -2012,9 +1994,6 @@ struct scst_device { /* If set, dev is read only */ unsigned short rd_only:1; - /* Set if tgt_kobj was initialized */ - unsigned short dev_kobj_initialized:1; - /**************************************************************/ /************************************************************* @@ -2162,11 +2141,8 @@ struct scst_device { /* Threads pool type of the device. Valid only if threads_num > 0. */ enum scst_dev_type_threads_pool_type threads_pool_type; - /* - * Used to protect sysfs attributes to be called after this - * object was unregistered. - */ - struct rw_semaphore dev_attr_rwsem; + /* sysfs release completion */ + struct completion dev_kobj_release_cmpl; struct kobject dev_kobj; /* kobject for this struct */ struct kobject *dev_exp_kobj; /* exported groups */ @@ -2318,9 +2294,6 @@ struct scst_acg_dev { /* 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 */ @@ -2331,6 +2304,12 @@ struct scst_acg_dev { /* kobject for this structure */ struct kobject acg_dev_kobj; + + /* sysfs release completion */ + struct completion acg_dev_kobj_release_cmpl; + + /* Name of the link to the corresponding LUN */ + char acg_dev_link_name[20]; }; /* @@ -2361,8 +2340,10 @@ struct scst_acg { /* Type of I/O initiators groupping */ int acg_io_grouping_type; - unsigned int acg_kobj_initialized:1; - unsigned int in_tgt_acg_list:1; + unsigned int tgt_acg:1; + + /* sysfs release completion */ + struct completion acg_kobj_release_cmpl; /* kobject for this structure */ struct kobject acg_kobj; @@ -2378,8 +2359,10 @@ struct scst_acg { * incoming sessions will be assigned to appropriate ACG. */ struct scst_acn { - /* Initiator's name */ - const char *name; + struct scst_acg *acg; /* owner ACG */ + + const char *name; /* initiator's name */ + /* List entry in acg->acn_list */ struct list_head acn_list_entry; @@ -2430,8 +2413,6 @@ struct scst_aen { /* * Registers target template. * Returns 0 on success or appropriate error code otherwise. - * - * Note: *vtt must be static! */ int __scst_register_target_template(struct scst_tgt_template *vtt, const char *version); @@ -2465,8 +2446,6 @@ int __scst_register_virtual_dev_driver(struct scst_dev_type *dev_type, /* * Registers dev handler driver for virtual devices (eg VDISK). * Returns 0 on success or appropriate error code otherwise. - * - * Note: *dev_type must be static! */ static inline int scst_register_virtual_dev_driver( struct scst_dev_type *dev_type) @@ -3508,9 +3487,9 @@ struct proc_dir_entry *scst_create_proc_entry(struct proc_dir_entry *root, #else /* CONFIG_SCST_PROC */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) -extern const struct sysfs_ops scst_sysfs_ops; +const struct sysfs_ops *scst_sysfs_get_sysfs_ops(void); #else -extern struct sysfs_ops scst_sysfs_ops; +struct sysfs_ops *scst_sysfs_get_sysfs_ops(void); #endif /* diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index 8633f35e1..819e67e3f 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -2525,7 +2525,7 @@ static int dev_user_attach(struct scst_device *sdev) dev->sdev = sdev; - PRINT_INFO("Attached user space SCSI target virtual device \"%s\"", + PRINT_INFO("Attached user space virtual device \"%s\"", dev->name); out: @@ -2541,7 +2541,7 @@ static void dev_user_detach(struct scst_device *sdev) TRACE_DBG("virt_id %d", sdev->virt_id); - PRINT_INFO("Detached user space SCSI target virtual device \"%s\"", + PRINT_INFO("Detached user space virtual device \"%s\"", dev->name); /* dev will be freed by the caller */ diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index b990dfa34..052bc9acb 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -788,7 +788,7 @@ static void vdisk_detach(struct scst_device *dev) TRACE_DBG("virt_id %d", dev->virt_id); - PRINT_INFO("Detached SCSI target virtual device %s (\"%s\")", + PRINT_INFO("Detached virtual device %s (\"%s\")", virt_dev->name, vdev_get_filename(virt_dev)); /* virt_dev will be freed by the caller */ diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index e87b466eb..682f4d8fa 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -1800,8 +1800,10 @@ next: old_acg->acg_name, acg->acg_name); list_move_tail(&sess->acg_sess_list_entry, &acg->acg_sess_list); +#ifndef CONFIG_SCST_PROC scst_recreate_sess_luns_link(sess); /* Ignore possible error, since we can't do anything on it */ +#endif if (luns_changed) { scst_report_luns_changed_sess(sess); @@ -2400,7 +2402,7 @@ restart: return; } -bool scst_is_relative_target_port_id_unique(uint16_t id, +static bool __scst_is_relative_target_port_id_unique(uint16_t id, const struct scst_tgt *t) { bool res = true; @@ -2408,7 +2410,6 @@ bool scst_is_relative_target_port_id_unique(uint16_t id, TRACE_ENTRY(); - mutex_lock(&scst_mutex); list_for_each_entry(tgtt, &scst_template_list, scst_template_list_entry) { struct scst_tgt *tgt; @@ -2424,6 +2425,21 @@ bool scst_is_relative_target_port_id_unique(uint16_t id, } } } + + TRACE_EXIT_RES(res); + return res; +} + +/* scst_mutex supposed to be locked */ +bool scst_is_relative_target_port_id_unique(uint16_t id, + const struct scst_tgt *t) +{ + bool res; + + TRACE_ENTRY(); + + mutex_lock(&scst_mutex); + res = __scst_is_relative_target_port_id_unique(id, t); mutex_unlock(&scst_mutex); TRACE_EXIT_RES(res); @@ -2437,12 +2453,17 @@ int gen_relative_target_port_id(uint16_t *id) TRACE_ENTRY(); + if (mutex_lock_interruptible(&scst_mutex) != 0) { + res = -EINTR; + goto out; + } + rti_prev = rti; do { - if (scst_is_relative_target_port_id_unique(rti, NULL)) { + if (__scst_is_relative_target_port_id_unique(rti, NULL)) { *id = (uint16_t)rti++; res = 0; - goto out; + goto out_unlock; } rti++; if (rti > SCST_MAX_REL_TGT_ID) @@ -2451,11 +2472,15 @@ int gen_relative_target_port_id(uint16_t *id) PRINT_ERROR("%s", "Unable to create unique relative target port id"); +out_unlock: + mutex_unlock(&scst_mutex); + out: TRACE_EXIT_RES(res); return res; } +/* No locks */ int scst_alloc_tgt(struct scst_tgt_template *tgtt, struct scst_tgt **tgt) { struct scst_tgt *t; @@ -2487,6 +2512,8 @@ int scst_alloc_tgt(struct scst_tgt_template *tgtt, struct scst_tgt **tgt) scst_free_tgt(t); goto out; } +#else + INIT_LIST_HEAD(&t->tgt_acg_list); #endif *tgt = t; @@ -2496,13 +2523,11 @@ out: return res; } +/* No locks */ void scst_free_tgt(struct scst_tgt *tgt) { TRACE_ENTRY(); - if (tgt->default_acg != NULL) - scst_free_acg(tgt->default_acg); - kfree(tgt->tgt_name); #ifdef CONFIG_SCST_PROC kfree(tgt->default_group_name); @@ -2628,23 +2653,11 @@ out: return res; } -void scst_acg_dev_destroy(struct scst_acg_dev *acg_dev) -{ - TRACE_ENTRY(); - -#ifndef CONFIG_SCST_PROC - if ((acg_dev->dev != NULL) && acg_dev->dev->dev_kobj_initialized) - kobject_put(&acg_dev->dev->dev_kobj); -#endif - - kmem_cache_free(scst_acgd_cachep, acg_dev); - - TRACE_EXIT(); - return; -} - -/* The activity supposed to be suspended and scst_mutex held */ -static void scst_free_acg_dev(struct scst_acg_dev *acg_dev) +/* + * The activity supposed to be suspended and scst_mutex held or the + * corresponding target supposed to be stopped. + */ +static void scst_del_free_acg_dev(struct scst_acg_dev *acg_dev, bool del_sysfs) { TRACE_ENTRY(); @@ -2653,16 +2666,124 @@ 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); - 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); + if (del_sysfs) + scst_acg_dev_sysfs_del(acg_dev); + + kmem_cache_free(scst_acgd_cachep, acg_dev); TRACE_EXIT(); return; } +/* The activity supposed to be suspended and scst_mutex held */ +int scst_acg_add_lun(struct scst_acg *acg, struct kobject *parent, + struct scst_device *dev, uint64_t lun, int read_only, + bool gen_scst_report_luns_changed, struct scst_acg_dev **out_acg_dev) +{ + int res = 0; + struct scst_acg_dev *acg_dev; + struct scst_tgt_dev *tgt_dev; + struct scst_session *sess; + LIST_HEAD(tmp_tgt_dev_list); + bool del_sysfs = true; + + TRACE_ENTRY(); + + INIT_LIST_HEAD(&tmp_tgt_dev_list); + + acg_dev = scst_alloc_acg_dev(acg, dev, lun); + if (acg_dev == NULL) { + res = -ENOMEM; + goto out; + } + acg_dev->rd_only = read_only; + + TRACE_DBG("Adding acg_dev %p to acg_dev_list and dev_acg_dev_list", + acg_dev); + list_add_tail(&acg_dev->acg_dev_list_entry, &acg->acg_dev_list); + list_add_tail(&acg_dev->dev_acg_dev_list_entry, &dev->dev_acg_dev_list); + + list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) { + res = scst_alloc_add_tgt_dev(sess, acg_dev, &tgt_dev); + if (res == -EPERM) + continue; + else if (res != 0) + goto out_free; + + list_add_tail(&tgt_dev->extra_tgt_dev_list_entry, + &tmp_tgt_dev_list); + } + + res = scst_acg_dev_sysfs_create(acg_dev, parent); + if (res != 0) { + del_sysfs = false; + goto out_free; + } + + if (gen_scst_report_luns_changed) + scst_report_luns_changed(acg); + + 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); + + if (out_acg_dev != NULL) + *out_acg_dev = acg_dev; + +out: + TRACE_EXIT_RES(res); + return res; + +out_free: + list_for_each_entry(tgt_dev, &tmp_tgt_dev_list, + extra_tgt_dev_list_entry) { + scst_free_tgt_dev(tgt_dev); + } + scst_del_free_acg_dev(acg_dev, del_sysfs); + goto out; +} + +/* The activity supposed to be suspended and scst_mutex held */ +int scst_acg_del_lun(struct scst_acg *acg, uint64_t lun, + bool gen_scst_report_luns_changed) +{ + int res = 0; + struct scst_acg_dev *acg_dev = NULL, *a; + struct scst_tgt_dev *tgt_dev, *tt; + + TRACE_ENTRY(); + + list_for_each_entry(a, &acg->acg_dev_list, acg_dev_list_entry) { + if (a->lun == lun) { + acg_dev = a; + break; + } + } + if (acg_dev == NULL) { + PRINT_ERROR("Device is not found in group %s", acg->acg_name); + res = -EINVAL; + goto out; + } + + list_for_each_entry_safe(tgt_dev, tt, &acg_dev->dev->dev_tgt_dev_list, + dev_tgt_dev_list_entry) { + if (tgt_dev->acg_dev == acg_dev) + scst_free_tgt_dev(tgt_dev); + } + + scst_del_free_acg_dev(acg_dev, true); + + if (gen_scst_report_luns_changed) + scst_report_luns_changed(acg); + + PRINT_INFO("Removed LUN %lld from group %s", (unsigned long long)lun, + acg->acg_name); + +out: + TRACE_EXIT_RES(res); + return res; +} + /* The activity supposed to be suspended and scst_mutex held */ struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt, const char *acg_name) @@ -2695,10 +2816,17 @@ struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt, scst_check_reassign_sessions(); #else if (tgt != NULL) { + int rc; + TRACE_DBG("Adding acg '%s' to device '%s' acg_list", acg_name, tgt->tgt_name); + list_add_tail(&acg->acg_list_entry, &tgt->tgt_acg_list); - acg->in_tgt_acg_list = 1; + acg->tgt_acg = 1; + + rc = scst_acg_sysfs_create(tgt, acg); + if (rc != 0) + goto out_del; } #endif @@ -2706,16 +2834,60 @@ out: TRACE_EXIT_HRES(acg); return acg; +#ifndef CONFIG_SCST_PROC +out_del: + list_del(&acg->acg_list_entry); +#endif + out_free: kfree(acg); acg = NULL; goto out; } -void scst_free_acg(struct scst_acg *acg) +/* The activity supposed to be suspended and scst_mutex held */ +void scst_del_free_acg(struct scst_acg *acg) { + struct scst_acn *acn, *acnt; + struct scst_acg_dev *acg_dev, *acg_dev_tmp; + TRACE_ENTRY(); + TRACE_DBG("Clearing acg %s from list", acg->acg_name); + + sBUG_ON(!list_empty(&acg->acg_sess_list)); + + /* Freeing acg_devs */ + list_for_each_entry_safe(acg_dev, acg_dev_tmp, &acg->acg_dev_list, + acg_dev_list_entry) { + struct scst_tgt_dev *tgt_dev, *tt; + list_for_each_entry_safe(tgt_dev, tt, + &acg_dev->dev->dev_tgt_dev_list, + dev_tgt_dev_list_entry) { + if (tgt_dev->acg_dev == acg_dev) + scst_free_tgt_dev(tgt_dev); + } + scst_del_free_acg_dev(acg_dev, true); + } + + /* Freeing names */ + list_for_each_entry_safe(acn, acnt, &acg->acn_list, acn_list_entry) { + scst_del_free_acn(acn, + list_is_last(&acn->acn_list_entry, &acg->acn_list)); + } + INIT_LIST_HEAD(&acg->acn_list); + +#ifdef CONFIG_SCST_PROC + list_del(&acg->acg_list_entry); +#else + if (acg->tgt_acg) { + TRACE_DBG("Removing acg %s from list", acg->acg_name); + list_del(&acg->acg_list_entry); + + scst_acg_sysfs_del(acg); + } +#endif + sBUG_ON(!list_empty(&acg->acg_sess_list)); sBUG_ON(!list_empty(&acg->acg_dev_list)); sBUG_ON(!list_empty(&acg->acn_list)); @@ -2727,68 +2899,7 @@ void scst_free_acg(struct scst_acg *acg) return; } -/* The activity supposed to be suspended and scst_mutex held */ -void scst_clear_acg(struct scst_acg *acg) -{ - struct scst_acn *n, *nn; - struct scst_acg_dev *acg_dev, *acg_dev_tmp; - - TRACE_ENTRY(); - - TRACE_DBG("Clearing acg %s from list", acg->acg_name); - - sBUG_ON(!list_empty(&acg->acg_sess_list)); -#ifdef CONFIG_SCST_PROC - list_del(&acg->acg_list_entry); -#else - if (acg->in_tgt_acg_list) { - TRACE_DBG("Removing acg %s from list", acg->acg_name); - list_del(&acg->acg_list_entry); - acg->in_tgt_acg_list = 0; - } -#endif - /* Freeing acg_devs */ - list_for_each_entry_safe(acg_dev, acg_dev_tmp, &acg->acg_dev_list, - acg_dev_list_entry) { - struct scst_tgt_dev *tgt_dev, *tt; - list_for_each_entry_safe(tgt_dev, tt, - &acg_dev->dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { - if (tgt_dev->acg_dev == acg_dev) - scst_free_tgt_dev(tgt_dev); - } - scst_free_acg_dev(acg_dev); - } - - /* Freeing names */ - list_for_each_entry_safe(n, nn, &acg->acn_list, - acn_list_entry) { -#ifdef CONFIG_SCST_PROC - scst_acg_remove_acn(n); - if (list_is_last(&n->acn_list_entry, &acg->acn_list)) - scst_check_reassign_sessions(); -#else - scst_acn_sysfs_del(acg, n, - list_is_last(&n->acn_list_entry, &acg->acn_list)); -#endif - } - INIT_LIST_HEAD(&acg->acn_list); - - TRACE_EXIT(); - return; -} - -/* The activity supposed to be suspended and scst_mutex held */ -void scst_destroy_acg(struct scst_acg *acg) -{ - TRACE_ENTRY(); - - scst_clear_acg(acg); - scst_free_acg(acg); - - TRACE_EXIT(); - return; -} +#ifndef CONFIG_SCST_PROC /* The activity supposed to be suspended and scst_mutex held */ struct scst_acg *scst_tgt_find_acg(struct scst_tgt *tgt, const char *name) @@ -2808,6 +2919,8 @@ struct scst_acg *scst_tgt_find_acg(struct scst_tgt *tgt, const char *name) return acg_ret; } +#endif + /* scst_mutex supposed to be held */ static struct scst_tgt_dev *scst_find_shared_io_tgt_dev( struct scst_tgt_dev *tgt_dev) @@ -3462,115 +3575,17 @@ void scst_sess_free_tgt_devs(struct scst_session *sess) } /* The activity supposed to be suspended and scst_mutex held */ -int scst_acg_add_dev(struct scst_acg *acg, struct scst_device *dev, - uint64_t lun, int read_only, bool gen_scst_report_luns_changed) +int scst_acg_add_acn(struct scst_acg *acg, const char *name) { int res = 0; - struct scst_acg_dev *acg_dev; - struct scst_tgt_dev *tgt_dev; - struct scst_session *sess; - LIST_HEAD(tmp_tgt_dev_list); - - TRACE_ENTRY(); - - INIT_LIST_HEAD(&tmp_tgt_dev_list); - - acg_dev = scst_alloc_acg_dev(acg, dev, lun); - if (acg_dev == NULL) { - res = -ENOMEM; - goto out; - } - acg_dev->rd_only = read_only; - - TRACE_DBG("Adding acg_dev %p to acg_dev_list and dev_acg_dev_list", - acg_dev); - list_add_tail(&acg_dev->acg_dev_list_entry, &acg->acg_dev_list); - list_add_tail(&acg_dev->dev_acg_dev_list_entry, &dev->dev_acg_dev_list); - - list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) { - res = scst_alloc_add_tgt_dev(sess, acg_dev, &tgt_dev); - if (res == -EPERM) - continue; - else if (res != 0) - goto out_free; - - list_add_tail(&tgt_dev->extra_tgt_dev_list_entry, - &tmp_tgt_dev_list); - } - - if (gen_scst_report_luns_changed) - scst_report_luns_changed(acg); - - 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); - return res; - -out_free: - list_for_each_entry(tgt_dev, &tmp_tgt_dev_list, - extra_tgt_dev_list_entry) { - scst_free_tgt_dev(tgt_dev); - } - scst_free_acg_dev(acg_dev); - goto out; -} - -/* The activity supposed to be suspended and scst_mutex held */ -int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev, - bool gen_scst_report_luns_changed) -{ - int res = 0; - struct scst_acg_dev *acg_dev = NULL, *a; - struct scst_tgt_dev *tgt_dev, *tt; - - TRACE_ENTRY(); - - list_for_each_entry(a, &acg->acg_dev_list, acg_dev_list_entry) { - if (a->dev == dev) { - acg_dev = a; - break; - } - } - - if (acg_dev == NULL) { - PRINT_ERROR("Device is not found in group %s", acg->acg_name); - res = -EINVAL; - goto out; - } - - list_for_each_entry_safe(tgt_dev, tt, &dev->dev_tgt_dev_list, - dev_tgt_dev_list_entry) { - if (tgt_dev->acg_dev == acg_dev) - scst_free_tgt_dev(tgt_dev); - } - scst_free_acg_dev(acg_dev); - - if (gen_scst_report_luns_changed) - scst_report_luns_changed(acg); - - PRINT_INFO("Removed device %s from group %s", dev->virt_name, - acg->acg_name); - -out: - TRACE_EXIT_RES(res); - return res; -} - -/* The activity supposed to be suspended and scst_mutex held */ -int scst_acg_add_name(struct scst_acg *acg, const char *name) -{ - int res = 0; - struct scst_acn *n; + struct scst_acn *acn; int len; char *nm; TRACE_ENTRY(); - list_for_each_entry(n, &acg->acn_list, acn_list_entry) { - if (strcmp(n->name, name) == 0) { + list_for_each_entry(acn, &acg->acn_list, acn_list_entry) { + if (strcmp(acn->name, name) == 0) { PRINT_ERROR("Name %s already exists in group %s", name, acg->acg_name); res = -EEXIST; @@ -3578,13 +3593,15 @@ int scst_acg_add_name(struct scst_acg *acg, const char *name) } } - n = kmalloc(sizeof(*n), GFP_KERNEL); - if (n == NULL) { + acn = kzalloc(sizeof(*acn), GFP_KERNEL); + if (acn == NULL) { PRINT_ERROR("%s", "Unable to allocate scst_acn"); res = -ENOMEM; goto out; } + acn->acg = acg; + len = strlen(name); nm = kmalloc(len + 1, GFP_KERNEL); if (nm == NULL) { @@ -3594,17 +3611,17 @@ int scst_acg_add_name(struct scst_acg *acg, const char *name) } strcpy(nm, name); - n->name = nm; + acn->name = nm; - res = scst_create_acn_sysfs(acg, n); + res = scst_acn_sysfs_create(acn); if (res != 0) goto out_free_nm; - list_add_tail(&n->acn_list_entry, &acg->acn_list); + list_add_tail(&acn->acn_list_entry, &acg->acn_list); out: if (res == 0) { - PRINT_INFO("Added name '%s' to group '%s'", name, acg->acg_name); + PRINT_INFO("Added name %s to group %s", name, acg->acg_name); scst_check_reassign_sessions(); } @@ -3615,42 +3632,48 @@ out_free_nm: kfree(nm); out_free: - kfree(n); + kfree(acn); goto out; } /* The activity supposed to be suspended and scst_mutex held */ -struct scst_acn *scst_acg_find_name(struct scst_acg *acg, const char *name) +void scst_del_free_acn(struct scst_acn *acn, bool reassign) { - struct scst_acn *n; + TRACE_ENTRY(); + + list_del(&acn->acn_list_entry); + + scst_acn_sysfs_del(acn); + + kfree(acn->name); + kfree(acn); + + if (reassign) + scst_check_reassign_sessions(); + + TRACE_EXIT(); + return; +} + +/* The activity supposed to be suspended and scst_mutex held */ +struct scst_acn *scst_find_acn(struct scst_acg *acg, const char *name) +{ + struct scst_acn *acn; TRACE_ENTRY(); TRACE_DBG("Trying to find name '%s'", name); - list_for_each_entry(n, &acg->acn_list, acn_list_entry) { - if (strcmp(n->name, name) == 0) { + list_for_each_entry(acn, &acg->acn_list, acn_list_entry) { + if (strcmp(acn->name, name) == 0) { TRACE_DBG("%s", "Found"); goto out; } } - n = NULL; + acn = NULL; out: TRACE_EXIT(); - return n; -} - -/* scst_mutex supposed to be held */ -void scst_acg_remove_acn(struct scst_acn *acn) -{ - TRACE_ENTRY(); - - list_del(&acn->acn_list_entry); - kfree(acn->name); - kfree(acn); - - TRACE_EXIT(); - return; + return acn; } #ifdef CONFIG_SCST_PROC @@ -3658,13 +3681,13 @@ void scst_acg_remove_acn(struct scst_acn *acn) int scst_acg_remove_name(struct scst_acg *acg, const char *name, bool reassign) { int res = -EINVAL; - struct scst_acn *n; + struct scst_acn *acn; TRACE_ENTRY(); - list_for_each_entry(n, &acg->acn_list, acn_list_entry) { - if (strcmp(n->name, name) == 0) { - scst_acg_remove_acn(n); + list_for_each_entry(acn, &acg->acn_list, acn_list_entry) { + if (strcmp(acn->name, name) == 0) { + scst_del_free_acn(acn, false); res = 0; break; } @@ -4032,6 +4055,8 @@ void scst_free_session(struct scst_session *sess) { TRACE_ENTRY(); + scst_sess_sysfs_del(sess); + mutex_lock(&scst_mutex); TRACE_DBG("Removing sess %p from the list", sess); @@ -4048,16 +4073,6 @@ void scst_free_session(struct scst_session *sess) kfree(sess->transport_id); - scst_sess_sysfs_put(sess); /* must not be called under scst_mutex */ - - TRACE_EXIT(); - return; -} - -void scst_release_session(struct scst_session *sess) -{ - TRACE_ENTRY(); - kfree(sess->initiator_name); kmem_cache_free(scst_sess_cachep, sess); @@ -5910,7 +5925,7 @@ EXPORT_SYMBOL(scst_to_dma_dir); * scst_to_tgt_dma_dir() - translate SCST data direction to DMA direction * * Translates SCST data direction to DMA data direction from the perspective - * of the target device. + * of a target. */ enum dma_data_direction scst_to_tgt_dma_dir(int scst_dir) { diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 4da1bb466..fb4a75254 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -184,14 +184,14 @@ static void __scst_resume_activity(void); * Registers a target template and returns 0 on success or appropriate * error code otherwise. * - * Note: *vtt must be static! + * Target drivers supposed to behave sanely and not call register() + * and unregister() randomly sinultaneously. */ int __scst_register_target_template(struct scst_tgt_template *vtt, const char *version) { int res = 0; struct scst_tgt_template *t; - static DEFINE_MUTEX(m); TRACE_ENTRY(); @@ -200,28 +200,28 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, if (strcmp(version, SCST_INTERFACE_VERSION) != 0) { PRINT_ERROR("Incorrect version of target %s", vtt->name); res = -EINVAL; - goto out_err; + goto out; } if (!vtt->detect) { PRINT_ERROR("Target driver %s must have " "detect() method.", vtt->name); res = -EINVAL; - goto out_err; + goto out; } if (!vtt->release) { PRINT_ERROR("Target driver %s must have " "release() method.", vtt->name); res = -EINVAL; - goto out_err; + goto out; } if (!vtt->xmit_response) { PRINT_ERROR("Target driver %s must have " "xmit_response() method.", vtt->name); res = -EINVAL; - goto out_err; + goto out; } if (vtt->get_initiator_port_transport_id == NULL) @@ -233,7 +233,7 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, "target \"%s\"", vtt->threads_num, vtt->name); res = -EINVAL; - goto out_err; + goto out; } #ifndef CONFIG_SCST_PROC @@ -249,80 +249,84 @@ int __scst_register_target_template(struct scst_tgt_template *vtt, PRINT_ERROR("Target driver %s must either define both " "add_target() and del_target(), or none.", vtt->name); res = -EINVAL; - goto out_err; - } -#else - if (!vtt->no_proc_entry) { - res = scst_build_proc_target_dir_entries(vtt); - if (res < 0) - goto out_err; + goto out; } #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_sysfs_err; - if (mutex_lock_interruptible(&scst_mutex) != 0) - goto out_m_err; + goto out; 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_m_err; + goto out_unlock; } } mutex_unlock(&scst_mutex); +#ifndef CONFIG_SCST_PROC + res = scst_tgtt_sysfs_create(vtt); + if (res) + goto out; +#else + if (!vtt->no_proc_entry) { + res = scst_build_proc_target_dir_entries(vtt); + if (res < 0) + goto out; + } +#endif + + mutex_lock(&scst_mutex); + mutex_lock(&scst_mutex2); + list_add_tail(&vtt->scst_template_list_entry, &scst_template_list); + mutex_unlock(&scst_mutex2); + mutex_unlock(&scst_mutex); + TRACE_DBG("%s", "Calling target driver's detect()"); res = vtt->detect(vtt); TRACE_DBG("Target driver's detect() returned %d", res); if (res < 0) { PRINT_ERROR("%s", "The detect() routine failed"); res = -EINVAL; - goto out_m_err; + goto out_del; } - mutex_lock(&scst_mutex); - mutex_lock(&scst_mutex2); - list_add_tail(&vtt->scst_template_list_entry, &scst_template_list); - mutex_unlock(&scst_mutex2); - mutex_unlock(&scst_mutex); - - res = 0; - PRINT_INFO("Target template %s registered successfully", vtt->name); - mutex_unlock(&m); - out: TRACE_EXIT_RES(res); return res; -out_m_err: - mutex_unlock(&m); - -out_sysfs_err: +out_del: #ifdef CONFIG_SCST_PROC scst_cleanup_proc_target_dir_entries(vtt); +#else + scst_tgtt_sysfs_del(vtt); #endif - scst_tgtt_sysfs_put(vtt); -out_err: - PRINT_ERROR("Failed to register target template %s", vtt->name); + mutex_lock(&scst_mutex); + + mutex_lock(&scst_mutex2); + list_del(&vtt->scst_template_list_entry); + mutex_unlock(&scst_mutex2); + +out_unlock: + mutex_unlock(&scst_mutex); goto out; } EXPORT_SYMBOL(__scst_register_target_template); /** * scst_unregister_target_template() - unregister target template + * + * Target drivers supposed to behave sanely and not call register() + * and unregister() randomly sinultaneously. Also it is supposed that + * no attepts to create new targets for this vtt will be done in a race + * with this function. */ void scst_unregister_target_template(struct scst_tgt_template *vtt) { @@ -345,6 +349,10 @@ void scst_unregister_target_template(struct scst_tgt_template *vtt) goto out_err_up; } + mutex_lock(&scst_mutex2); + list_del(&vtt->scst_template_list_entry); + mutex_unlock(&scst_mutex2); + restart: list_for_each_entry(tgt, &vtt->tgt_list, tgt_list_entry) { mutex_unlock(&scst_mutex); @@ -353,18 +361,14 @@ restart: goto restart; } - mutex_lock(&scst_mutex2); - list_del(&vtt->scst_template_list_entry); - mutex_unlock(&scst_mutex2); - mutex_unlock(&scst_mutex); #ifdef CONFIG_SCST_PROC scst_cleanup_proc_target_dir_entries(vtt); +#else + scst_tgtt_sysfs_del(vtt); #endif - scst_tgtt_sysfs_put(vtt); - PRINT_INFO("Target template %s unregistered successfully", vtt->name); out: @@ -393,32 +397,24 @@ struct scst_tgt *scst_register_target(struct scst_tgt_template *vtt, rc = scst_alloc_tgt(vtt, &tgt); if (rc != 0) - goto out_err; - - rc = scst_suspend_activity(true); - if (rc != 0) - goto out_free_tgt_err; - - if (mutex_lock_interruptible(&scst_mutex) != 0) { - rc = -EINTR; - goto out_resume_free; - } + goto out; if (target_name != NULL) { #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, "Allocation of default " "group name failed (tgt %s)", target_name); rc = -ENOMEM; - goto out_unlock_resume; + goto out_free_tgt; } sprintf(tgt->default_group_name, "%s_%s", SCST_DEFAULT_ACG_NAME, target_name); + + /* In case of error default_group_name will be freed in scst_free_tgt() */ #endif tgt->tgt_name = kmalloc(strlen(target_name) + 1, GFP_KERNEL); @@ -426,11 +422,7 @@ struct scst_tgt *scst_register_target(struct scst_tgt_template *vtt, 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 + goto out_free_tgt; } strcpy(tgt->tgt_name, target_name); } else { @@ -443,34 +435,29 @@ struct scst_tgt *scst_register_target(struct scst_tgt_template *vtt, 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 + goto out_free_tgt; } sprintf(tgt->tgt_name, "%s%s%d", vtt->name, SCST_DEFAULT_TGT_NAME_SUFFIX, tgt_num++); } - tgt->default_acg = scst_alloc_add_acg(NULL, tgt->tgt_name); - if (tgt->default_acg == NULL) - goto out_free_tgt_name; - - INIT_LIST_HEAD(&tgt->tgt_acg_list); + if (mutex_lock_interruptible(&scst_mutex) != 0) { + rc = -EINTR; + goto out_free_tgt; + } #ifdef CONFIG_SCST_PROC rc = scst_build_proc_target_entries(tgt); if (rc < 0) - goto out_clear_acg; -#endif - - rc = scst_create_tgt_sysfs(tgt); - if (rc < 0) -#ifdef CONFIG_SCST_PROC - goto out_clean_proc; + goto out_unlock; #else - goto out_clear_acg; + rc = scst_tgt_sysfs_create(tgt); + if (rc < 0) + goto out_unlock; + + tgt->default_acg = scst_alloc_add_acg(NULL, tgt->tgt_name); + if (tgt->default_acg == NULL) + goto out_sysfs_del; #endif mutex_lock(&scst_mutex2); @@ -478,11 +465,10 @@ struct scst_tgt *scst_register_target(struct scst_tgt_template *vtt, mutex_unlock(&scst_mutex2); mutex_unlock(&scst_mutex); - scst_resume_activity(); #ifdef CONFIG_SCST_PROC - PRINT_INFO("Target %s (rel ID %d) for template %s registered successfully", - tgt->tgt_name, tgt->rel_tgt_id, vtt->name); + PRINT_INFO("Target %s (rel ID %d) for template %s registered " + "successfully", tgt->tgt_name, tgt->rel_tgt_id, vtt->name); #else PRINT_INFO("Target %s for template %s registered successfully", tgt->tgt_name, vtt->name); @@ -494,36 +480,20 @@ out: TRACE_EXIT(); return tgt; -#ifdef CONFIG_SCST_PROC -out_clean_proc: - scst_cleanup_proc_target_entries(tgt); +#ifndef CONFIG_SCST_PROC +out_sysfs_del: + scst_tgt_sysfs_del(tgt); #endif -out_clear_acg: - scst_clear_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: +out_unlock: mutex_unlock(&scst_mutex); -out_resume_free: - scst_resume_activity(); - -out_free_tgt_err: - scst_tgt_sysfs_put(tgt); /* must not be called under scst_mutex */ +out_free_tgt: + /* + * In case of error tgt_name will be freed in scst_free_tgt(). + */ + scst_free_tgt(tgt); tgt = NULL; - -out_err: - PRINT_ERROR("Failed to register target %s for template %s (error %d)", - (tgt->tgt_name != NULL) ? tgt->tgt_name : target_name, - vtt->name, rc); goto out; } EXPORT_SYMBOL(scst_register_target); @@ -538,18 +508,21 @@ static inline int test_sess_list(struct scst_tgt *tgt) } /** - * scst_unregister_target() - unregister target + * scst_unregister_target() - unregister target. + * + * It is supposed that no attepts to create new sessions for this + * target will be done in a race with this function. */ void scst_unregister_target(struct scst_tgt *tgt) { struct scst_session *sess; struct scst_tgt_template *vtt = tgt->tgtt; +#ifndef CONFIG_SCST_PROC struct scst_acg *acg, *acg_tmp; +#endif TRACE_ENTRY(); - scst_tgt_sysfs_prepare_put(tgt); - TRACE_DBG("%s", "Calling target driver's release()"); tgt->tgtt->release(tgt); TRACE_DBG("%s", "Target driver's release() returned"); @@ -560,8 +533,7 @@ again: if (sess->shut_phase == SCST_SESS_SPH_READY) { /* * Sometimes it's hard for target driver to track all - * its sessions (see scst_local, for example), so let's - * help it. + * its sessions (see scst_local, eg), so let's help it. */ mutex_unlock(&scst_mutex); scst_unregister_session(sess, 0, NULL); @@ -582,32 +554,28 @@ again: list_del(&tgt->tgt_list_entry); mutex_unlock(&scst_mutex2); + del_timer_sync(&tgt->retry_timer); + #ifdef CONFIG_SCST_PROC scst_cleanup_proc_target_entries(tgt); -#endif +#else + scst_del_free_acg(tgt->default_acg); - /* - * There's no more any activity in this target, hence the lock and - * suspending aren't needed as soon as later we are going to clean up - * only local to this target entries. - */ + list_for_each_entry_safe(acg, acg_tmp, &tgt->tgt_acg_list, + acg_list_entry) { + scst_del_free_acg(acg); + } + + scst_tgt_sysfs_del(tgt); +#endif mutex_unlock(&scst_mutex); scst_resume_activity(); - scst_clear_acg(tgt->default_acg); - - list_for_each_entry_safe(acg, acg_tmp, &tgt->tgt_acg_list, - acg_list_entry) { - scst_acg_sysfs_put(acg); - } - - del_timer_sync(&tgt->retry_timer); - PRINT_INFO("Target %s for template %s unregistered successfully", tgt->tgt_name, vtt->name); - scst_tgt_sysfs_put(tgt); /* must not be called under scst_mutex */ + scst_free_tgt(tgt); TRACE_DBG("Unregistering tgt %p finished", tgt); @@ -809,7 +777,7 @@ static int scst_register_device(struct scsi_device *scsidp) res = scst_suspend_activity(true); if (res != 0) - goto out_err; + goto out; if (mutex_lock_interruptible(&scst_mutex) != 0) { res = -EINTR; @@ -818,7 +786,7 @@ static int scst_register_device(struct scsi_device *scsidp) res = scst_alloc_device(GFP_KERNEL, &dev); if (res != 0) - goto out_up; + goto out_unlock; dev->type = scsidp->type; @@ -855,10 +823,6 @@ 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; - #ifdef CONFIG_SCST_PROC /* * Let's don't attach to dev handler by default, but keep this code in @@ -868,43 +832,42 @@ static int scst_register_device(struct scsi_device *scsidp) if (dt->type == scsidp->type) { res = scst_assign_dev_handler(dev, dt); if (res != 0) - goto out_free; + goto out_del; break; } } +#else + res = scst_dev_sysfs_create(dev); + if (res != 0) + goto out_del; #endif -out_up: mutex_unlock(&scst_mutex); - -out_resume: scst_resume_activity(); -out_err: - if (res == 0) { - PRINT_INFO("Attached to scsi%d, channel %d, id %d, lun %d, " - "type %d", scsidp->host->host_no, scsidp->channel, - scsidp->id, scsidp->lun, scsidp->type); - } else { - 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); - } + PRINT_INFO("Attached to scsi%d, channel %d, id %d, lun %d, " + "type %d", scsidp->host->host_no, scsidp->channel, + scsidp->id, scsidp->lun, scsidp->type); +out: TRACE_EXIT_RES(res); return res; -out_free: +out_del: list_del(&dev->dev_list_entry); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) put_disk(dev->rq_disk); #endif out_free_dev: + scst_free_device(dev); + +out_unlock: mutex_unlock(&scst_mutex); + +out_resume: scst_resume_activity(); - scst_device_sysfs_put(dev); /* must not be called under scst_mutex */ - goto out_err; + goto out; } static void scst_unregister_device(struct scsi_device *scsidp) @@ -920,42 +883,47 @@ static void scst_unregister_device(struct scsi_device *scsidp) list_for_each_entry(d, &scst_dev_list, dev_list_entry) { if (d->scsi_dev == scsidp) { dev = d; - TRACE_DBG("Target device %p found", dev); + TRACE_DBG("Device %p found", dev); break; } } if (dev == NULL) { - PRINT_ERROR("%s", "Target device not found"); - goto out_resume; + PRINT_ERROR("SCST device for SCSI device %d:%d:%d:%d not found", + scsidp->host->host_no, scsidp->channel, scsidp->id, + scsidp->lun); + goto out_unlock; } list_del(&dev->dev_list_entry); + scst_assign_dev_handler(dev, &scst_null_devtype); + list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list, dev_acg_dev_list_entry) { - scst_acg_remove_dev(acg_dev->acg, dev, true); + scst_acg_del_lun(acg_dev->acg, acg_dev->lun, true); } - scst_assign_dev_handler(dev, &scst_null_devtype); + scst_dev_sysfs_del(dev); + + mutex_unlock(&scst_mutex); #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) put_disk(dev->rq_disk); #endif - mutex_unlock(&scst_mutex); scst_resume_activity(); - scst_device_sysfs_put(dev); /* must not be called under scst_mutex */ - PRINT_INFO("Detached from scsi%d, channel %d, id %d, lun %d, type %d", scsidp->host->host_no, scsidp->channel, scsidp->id, scsidp->lun, scsidp->type); + scst_free_device(dev); + out: TRACE_EXIT(); return; -out_resume: +out_unlock: mutex_unlock(&scst_mutex); scst_resume_activity(); goto out; @@ -1023,7 +991,7 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler, const char *dev_name) { int res, rc; - struct scst_device *dev; + struct scst_device *dev, *d; TRACE_ENTRY(); @@ -1057,17 +1025,17 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler, goto out_resume; } - list_for_each_entry(dev, &scst_dev_list, dev_list_entry) { - if (strcmp(dev->virt_name, dev_name) == 0) { + list_for_each_entry(d, &scst_dev_list, dev_list_entry) { + if (strcmp(d->virt_name, dev_name) == 0) { PRINT_ERROR("Device %s already exists", dev_name); res = -EEXIST; - goto out_up; + goto out_unlock; } } res = scst_alloc_device(GFP_KERNEL, &dev); if (res != 0) - goto out_up; + goto out_unlock; dev->type = dev_handler->type; dev->scsi_dev = NULL; @@ -1076,58 +1044,63 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler, PRINT_ERROR("Unable to allocate virt_name for dev %s", dev_name); res = -ENOMEM; - goto out_release; + goto out_free_dev; } dev->virt_id = scst_virt_dev_last_id++; + if (dev->virt_id <= 0) { + scst_virt_dev_last_id = 1; + 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_pr_init_dev(dev); if (rc != 0) { res = rc; - goto out_free_del; + goto out_del; } + res = scst_dev_sysfs_create(dev); + if (res != 0) + goto out_pr_clear_dev; + rc = scst_assign_dev_handler(dev, dev_handler); if (rc != 0) { res = rc; - goto out_pr_clear_dev; + goto out_sysfs_del; } -out_up: mutex_unlock(&scst_mutex); - -out_resume: scst_resume_activity(); -out: - if (res > 0) - PRINT_INFO("Attached to virtual device %s (id %d)", - dev_name, dev->virt_id); - else - PRINT_INFO("Failed to attach to virtual device %s", dev_name); + res = dev->virt_id; + PRINT_INFO("Attached to virtual device %s (id %d)", + dev_name, res); + +out: TRACE_EXIT_RES(res); return res; +out_sysfs_del: + scst_dev_sysfs_del(dev); + out_pr_clear_dev: scst_pr_clear_dev(dev); -out_free_del: +out_del: list_del(&dev->dev_list_entry); -out_release: +out_free_dev: + scst_free_device(dev); + +out_unlock: mutex_unlock(&scst_mutex); + +out_resume: scst_resume_activity(); - scst_device_sysfs_put(dev); /* must not be called under scst_mutex */ goto out; } EXPORT_SYMBOL(scst_register_virtual_device); @@ -1149,37 +1122,44 @@ void scst_unregister_virtual_device(int id) list_for_each_entry(d, &scst_dev_list, dev_list_entry) { if (d->virt_id == id) { dev = d; - TRACE_DBG("Target device %p (id %d) found", dev, id); + TRACE_DBG("Virtual device %p (id %d) found", dev, id); break; } } if (dev == NULL) { - PRINT_ERROR("Target virtual device (id %d) not found", id); - goto out_unblock; + PRINT_ERROR("Virtual device (id %d) not found", id); + goto out_unlock; } list_del(&dev->dev_list_entry); - list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list, - dev_acg_dev_list_entry) { - scst_acg_remove_dev(acg_dev->acg, dev, true); - } - scst_pr_clear_dev(dev); scst_assign_dev_handler(dev, &scst_null_devtype); - PRINT_INFO("Detached from virtual device %s (id %d)", - dev->virt_name, dev->virt_id); + list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list, + dev_acg_dev_list_entry) { + scst_acg_del_lun(acg_dev->acg, acg_dev->lun, true); + } + + scst_dev_sysfs_del(dev); -out_unblock: mutex_unlock(&scst_mutex); scst_resume_activity(); - scst_device_sysfs_put(dev); /* must not be called under scst_mutex */ + PRINT_INFO("Detached from virtual device %s (id %d)", + dev->virt_name, dev->virt_id); + scst_free_device(dev); + +out: TRACE_EXIT(); return; + +out_unlock: + mutex_unlock(&scst_mutex); + scst_resume_activity(); + goto out; } EXPORT_SYMBOL(scst_unregister_virtual_device); @@ -1193,18 +1173,15 @@ EXPORT_SYMBOL(scst_unregister_virtual_device); * Description: * Registers a pass-through dev handler driver. Returns 0 on success * or appropriate error code otherwise. - * - * Note: *dev_type must be static! */ int __scst_register_dev_driver(struct scst_dev_type *dev_type, const char *version) { + int res, exist; struct scst_dev_type *dt; #ifdef CONFIG_SCST_PROC struct scst_device *dev; #endif - int res; - int exist; TRACE_ENTRY(); @@ -1212,12 +1189,12 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type, PRINT_ERROR("Incorrect version of dev handler %s", dev_type->name); res = -EINVAL; - goto out_error; + goto out; } res = scst_dev_handler_check(dev_type); if (res != 0) - goto out_error; + goto out; #if !defined(SCSI_EXEC_REQ_FIFO_DEFINED) if (dev_type->exec == NULL) { @@ -1228,25 +1205,25 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type, "scst_exec_req_fifo- or define " "CONFIG_SCST_STRICT_SERIALIZING", dev_type->name); res = -EINVAL; - goto out_error; + goto out; #endif /* !defined(CONFIG_SCST_STRICT_SERIALIZING) */ #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */ PRINT_ERROR("Pass-through dev handlers (handler \"%s\") not " "supported. Consider applying on your kernel patch " "scst_exec_req_fifo-", dev_type->name); res = -EINVAL; - goto out_error; + goto out; #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */ } #endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */ res = scst_suspend_activity(true); if (res != 0) - goto out_error; + goto out; if (mutex_lock_interruptible(&scst_mutex) != 0) { res = -EINTR; - goto out_err_res; + goto out_resume; } exist = 0; @@ -1259,19 +1236,19 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type, } } if (exist) - goto out_up; + goto out_unlock; #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; + goto out_unlock; } -#endif - - res = scst_create_devt_sysfs(dev_type); +#else + res = scst_devt_sysfs_create(dev_type); if (res < 0) - goto out_free; + goto out_unlock; +#endif list_add_tail(&dev_type->dev_type_list_entry, &scst_dev_type_list); @@ -1291,31 +1268,18 @@ int __scst_register_dev_driver(struct scst_dev_type *dev_type, mutex_unlock(&scst_mutex); scst_resume_activity(); - if (res == 0) { - PRINT_INFO("Device handler \"%s\" for type %d registered " - "successfully", dev_type->name, dev_type->type); - } + PRINT_INFO("Device handler \"%s\" for type %d registered " + "successfully", dev_type->name, dev_type->type); 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: +out_unlock: mutex_unlock(&scst_mutex); -out_err_res: +out_resume: scst_resume_activity(); - -out_error: - PRINT_ERROR("Failed to register device handler \"%s\" for type %d", - dev_type->name, dev_type->type); goto out; } EXPORT_SYMBOL(__scst_register_dev_driver); @@ -1360,10 +1324,10 @@ void scst_unregister_dev_driver(struct scst_dev_type *dev_type) #ifdef CONFIG_SCST_PROC scst_cleanup_proc_dev_handler_dir_entries(dev_type); +#else + scst_devt_sysfs_del(dev_type); #endif - scst_devt_sysfs_put(dev_type); - PRINT_INFO("Device handler \"%s\" for type %d unloaded", dev_type->name, dev_type->type); @@ -1388,8 +1352,6 @@ EXPORT_SYMBOL(scst_unregister_dev_driver); * Description: * Registers a virtual dev handler driver. Returns 0 on success or * appropriate error code otherwise. - * - * Note: *dev_type must be static! */ int __scst_register_virtual_dev_driver(struct scst_dev_type *dev_type, const char *version) @@ -1402,24 +1364,24 @@ int __scst_register_virtual_dev_driver(struct scst_dev_type *dev_type, PRINT_ERROR("Incorrect version of virtual dev handler %s", dev_type->name); res = -EINVAL; - goto out_err; + goto out; } res = scst_dev_handler_check(dev_type); if (res != 0) - goto out_err; + goto out; #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; + goto out; } -#endif - - res = scst_create_devt_sysfs(dev_type); +#else + res = scst_devt_sysfs_create(dev_type); if (res < 0) - goto out_free; + goto out; +#endif if (dev_type->type != -1) { PRINT_INFO("Virtual device handler %s for type %d " @@ -1433,19 +1395,6 @@ int __scst_register_virtual_dev_driver(struct scst_dev_type *dev_type, 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); - goto out; } EXPORT_SYMBOL(__scst_register_virtual_dev_driver); @@ -1459,10 +1408,10 @@ void scst_unregister_virtual_dev_driver(struct scst_dev_type *dev_type) #ifdef CONFIG_SCST_PROC if (!dev_type->no_proc) scst_cleanup_proc_dev_handler_dir_entries(dev_type); +#else + scst_devt_sysfs_del(dev_type); #endif - scst_devt_sysfs_put(dev_type); - PRINT_INFO("Device handler \"%s\" unloaded", dev_type->name); TRACE_EXIT(); @@ -1697,7 +1646,7 @@ int scst_assign_dev_handler(struct scst_device *dev, scst_stop_dev_threads(dev); - scst_devt_dev_sysfs_put(dev); + scst_devt_dev_sysfs_del(dev); assign: dev->handler = handler; @@ -1708,7 +1657,7 @@ assign: dev->threads_num = handler->threads_num; dev->threads_pool_type = handler->threads_pool_type; - res = scst_create_devt_dev_sysfs(dev); + res = scst_devt_dev_sysfs_create(dev); if (res != 0) goto out_null; @@ -1765,10 +1714,12 @@ out_err_detach_tgt: } out_remove_sysfs: - scst_devt_dev_sysfs_put(dev); + scst_devt_dev_sysfs_del(dev); out_null: dev->handler = &scst_null_devtype; + dev->threads_num = scst_null_devtype.threads_num; + dev->threads_pool_type = scst_null_devtype.threads_pool_type; goto out; } @@ -2104,7 +2055,6 @@ static int __init init_scst(void) init_waitqueue_head(&scst_dev_cmd_waitQ); mutex_init(&scst_suspend_mutex); INIT_LIST_HEAD(&scst_cmd_threads_list); - scst_virt_dev_last_id = 1; scst_init_threads(&scst_main_cmd_threads); @@ -2281,7 +2231,7 @@ out_thread_free: #ifdef CONFIG_SCST_PROC out_free_acg: - scst_destroy_acg(scst_default_acg); + scst_del_free_acg(scst_default_acg); #endif out_destroy_sgv_pool: @@ -2346,7 +2296,6 @@ static void __exit exit_scst(void) #ifdef CONFIG_SCST_PROC scst_proc_cleanup_module(); #endif - scst_sysfs_cleanup(); scst_stop_all_threads(); @@ -2354,11 +2303,13 @@ static void __exit exit_scst(void) scsi_unregister_interface(&scst_interface); #ifdef CONFIG_SCST_PROC - scst_destroy_acg(scst_default_acg); + scst_del_free_acg(scst_default_acg); #endif scst_sgv_pools_deinit(); + scst_sysfs_cleanup(); + #define DEINIT_CACHEP(p) do { \ kmem_cache_destroy(p); \ p = NULL; \ diff --git a/scst/src/scst_mem.c b/scst/src/scst_mem.c index 272d5a206..28565e429 100644 --- a/scst/src/scst_mem.c +++ b/scst/src/scst_mem.c @@ -1452,12 +1452,21 @@ static int sgv_pool_init(struct sgv_pool *pool, const char *name, list_add_tail(&pool->sgv_pools_list_entry, &sgv_pools_list); spin_unlock_bh(&sgv_pools_lock); + res = scst_sgv_sysfs_create(pool); + if (res != 0) + goto out_del; + res = 0; out: TRACE_EXIT_RES(res); return res; +out_del: + spin_lock_bh(&sgv_pools_lock); + list_del(&pool->sgv_pools_list_entry); + spin_unlock_bh(&sgv_pools_lock); + out_free: for (i = 0; i < pool->max_caches; i++) { if (pool->caches[i]) { @@ -1520,8 +1529,10 @@ void sgv_pool_flush(struct sgv_pool *pool) } EXPORT_SYMBOL(sgv_pool_flush); -static void sgv_pool_deinit_put(struct sgv_pool *pool) +static void sgv_pool_destroy(struct sgv_pool *pool) { + int i; + TRACE_ENTRY(); cancel_delayed_work_sync(&pool->sgv_purge_work); @@ -1534,9 +1545,15 @@ static void sgv_pool_deinit_put(struct sgv_pool *pool) spin_unlock_bh(&sgv_pools_lock); mutex_unlock(&sgv_pools_mutex); - scst_sgv_sysfs_put(pool); + scst_sgv_sysfs_del(pool); - /* pool can be dead here */ + for (i = 0; i < pool->max_caches; i++) { + if (pool->caches[i]) + kmem_cache_destroy(pool->caches[i]); + pool->caches[i] = NULL; + } + + kfree(pool); TRACE_EXIT(); return; @@ -1596,6 +1613,7 @@ struct sgv_pool *sgv_pool_create(const char *name, TRACE_ENTRY(); mutex_lock(&sgv_pools_mutex); + list_for_each_entry(pool, &sgv_pools_list, sgv_pools_list_entry) { if (strcmp(pool->name, name) == 0) { if (shared) { @@ -1603,13 +1621,14 @@ struct sgv_pool *sgv_pool_create(const char *name, PRINT_ERROR("Attempt of a shared use " "of SGV pool %s with " "different MM", name); - goto out_err_unlock; + goto out_unlock; } sgv_pool_get(pool); goto out_unlock; } else { PRINT_ERROR("SGV pool %s already exists", name); - goto out_err_unlock; + pool = NULL; + goto out_unlock; } } } @@ -1623,11 +1642,7 @@ struct sgv_pool *sgv_pool_create(const char *name, rc = sgv_pool_init(pool, name, clustering_type, single_alloc_pages, purge_interval); if (rc != 0) - goto out_free_unlock; - - rc = scst_create_sgv_sysfs(pool); - if (rc != 0) - goto out_err_unlock_put; + goto out_free; out_unlock: mutex_unlock(&sgv_pools_mutex); @@ -1635,38 +1650,12 @@ out_unlock: TRACE_EXIT_RES(pool != NULL); return pool; -out_free_unlock: +out_free: kfree(pool); - -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); -void sgv_pool_destroy(struct sgv_pool *pool) -{ - int i; - - TRACE_ENTRY(); - - for (i = 0; i < pool->max_caches; i++) { - if (pool->caches[i]) - kmem_cache_destroy(pool->caches[i]); - pool->caches[i] = NULL; - } - - kfree(pool); - - TRACE_EXIT(); - return; -} - /** * sgv_pool_get - increase ref counter for the corresponding SGV pool * @@ -1692,7 +1681,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_deinit_put(pool); + sgv_pool_destroy(pool); return; } EXPORT_SYMBOL(sgv_pool_put); @@ -1755,10 +1744,10 @@ out: return res; out_free_clust: - sgv_pool_deinit_put(sgv_norm_clust_pool); + sgv_pool_destroy(sgv_norm_clust_pool); out_free_norm: - sgv_pool_deinit_put(sgv_norm_pool); + sgv_pool_destroy(sgv_norm_pool); out_err: res = -ENOMEM; @@ -1775,9 +1764,9 @@ void scst_sgv_pools_deinit(void) unregister_shrinker(&sgv_shrinker); #endif - sgv_pool_deinit_put(sgv_dma_pool); - sgv_pool_deinit_put(sgv_norm_pool); - sgv_pool_deinit_put(sgv_norm_clust_pool); + sgv_pool_destroy(sgv_dma_pool); + sgv_pool_destroy(sgv_norm_pool); + sgv_pool_destroy(sgv_norm_clust_pool); flush_scheduled_work(); diff --git a/scst/src/scst_mem.h b/scst/src/scst_mem.h index 81d30ddcc..54a96c646 100644 --- a/scst/src/scst_mem.h +++ b/scst/src/scst_mem.h @@ -90,7 +90,6 @@ struct sgv_pool { /* 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; @@ -128,6 +127,9 @@ struct sgv_pool { struct list_head sgv_pools_list_entry; struct kobject sgv_kobj; + + /* sysfs release completion */ + struct completion sgv_kobj_release_cmpl; }; static inline struct scatterlist *sgv_pool_sg(struct sgv_pool_obj *obj) @@ -138,8 +140,6 @@ static inline struct scatterlist *sgv_pool_sg(struct sgv_pool_obj *obj) 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); #else diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index cbd49b287..ed7f14075 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -35,10 +35,6 @@ #include "scst_debug.h" -#ifdef CONFIG_SCST_PROC -void sgv_pool_destroy(struct sgv_pool *pool); -#endif - #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) #define SCST_MAJOR 177 #endif @@ -302,9 +298,6 @@ void scst_adjust_resp_data_len(struct scst_cmd *cmd); 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) { } - int scst_alloc_tgt(struct scst_tgt_template *tgtt, struct scst_tgt **tgt); void scst_free_tgt(struct scst_tgt *tgt); @@ -313,12 +306,9 @@ void scst_free_device(struct scst_device *dev); struct scst_acg *scst_alloc_add_acg(struct scst_tgt *tgt, const char *acg_name); -void scst_clear_acg(struct scst_acg *acg); -void scst_destroy_acg(struct scst_acg *acg); -void scst_free_acg(struct scst_acg *acg); +void scst_del_free_acg(struct scst_acg *acg); struct scst_acg *scst_tgt_find_acg(struct scst_tgt *tgt, const char *name); - struct scst_acg *scst_find_acg(const struct scst_session *sess); void scst_check_reassign_sessions(void); @@ -327,19 +317,18 @@ 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, - uint64_t lun, int read_only, bool gen_scst_report_luns_changed); -int scst_acg_remove_dev(struct scst_acg *acg, struct scst_device *dev, +int scst_acg_add_lun(struct scst_acg *acg, struct kobject *parent, + struct scst_device *dev, uint64_t lun, int read_only, + bool gen_scst_report_luns_changed, struct scst_acg_dev **out_acg_dev); +int scst_acg_del_lun(struct scst_acg *acg, uint64_t lun, 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_add_acn(struct scst_acg *acg, const char *name); #ifdef CONFIG_SCST_PROC int scst_acg_remove_name(struct scst_acg *acg, const char *name, bool reassign); #endif -void scst_acg_remove_acn(struct scst_acn *acn); -struct scst_acn *scst_acg_find_name(struct scst_acg *acg, const char *name); +void scst_del_free_acn(struct scst_acn *acn, bool reassign); +struct scst_acn *scst_find_acn(struct scst_acg *acg, const char *name); /* The activity supposed to be suspended and scst_mutex held */ static inline bool scst_acg_sess_is_empty(struct scst_acg *acg) @@ -358,7 +347,6 @@ 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); @@ -434,6 +422,8 @@ 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); +static inline void scst_devt_cleanup(struct scst_dev_type *devt) { } + #ifdef CONFIG_SCST_PROC int scst_proc_init_module(void); @@ -451,107 +441,72 @@ static inline int scst_sysfs_init(void) } static inline void scst_sysfs_cleanup(void) { } -static inline int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt) +static inline int scst_devt_dev_sysfs_create(struct scst_device *dev) { return 0; } -static inline void scst_tgtt_sysfs_put(struct scst_tgt_template *tgtt) -{ - scst_tgtt_cleanup(tgtt); -} +static inline void scst_devt_dev_sysfs_del(struct scst_device *dev) { } -static inline int scst_create_tgt_sysfs(struct scst_tgt *tgt) +static inline int scst_dev_sysfs_create(struct scst_device *dev) { return 0; } -static inline void scst_tgt_sysfs_prepare_put(struct scst_tgt *tgt) { } -static inline void scst_tgt_sysfs_put(struct scst_tgt *tgt) -{ - scst_free_tgt(tgt); -} +static inline void scst_dev_sysfs_del(struct scst_device *dev) { } -static inline int scst_create_sess_sysfs(struct scst_session *sess) +static inline int scst_sess_sysfs_create(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_recreate_sess_luns_link(struct scst_session *sess) +static inline void scst_sess_sysfs_del(struct scst_session *sess) { } + +static inline int scst_acg_dev_sysfs_create(struct scst_acg_dev *acg_dev, + struct kobject *parent) { return 0; } -static inline int scst_create_sgv_sysfs(struct sgv_pool *pool) +static inline void scst_acg_dev_sysfs_del(struct scst_acg_dev *acg_dev) { } + +static inline int scst_acn_sysfs_create(struct scst_acn *acn) { return 0; } -static inline void scst_sgv_sysfs_put(struct sgv_pool *pool) -{ - sgv_pool_destroy(pool); -} +static inline void scst_acn_sysfs_del(struct scst_acn *acn) { } -static inline int scst_create_devt_sysfs(struct scst_dev_type *devt) +static inline int scst_sgv_sysfs_create(struct sgv_pool *pool) { 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) { } - -static inline int scst_create_acn_sysfs(struct scst_acg *acg, - struct scst_acn *acn) -{ - return 0; -} - -static inline void scst_acn_sysfs_del(struct scst_acg *acg, - struct scst_acn *acn, bool reassign) { } - -static inline void scst_acg_sysfs_put(struct scst_acg *acg) { } +static inline void scst_sgv_sysfs_del(struct sgv_pool *pool) { } #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); +int scst_tgtt_sysfs_create(struct scst_tgt_template *tgtt); +void scst_tgtt_sysfs_del(struct scst_tgt_template *tgtt); +int scst_tgt_sysfs_create(struct scst_tgt *tgt); void scst_tgt_sysfs_prepare_put(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); +void scst_tgt_sysfs_del(struct scst_tgt *tgt); +int scst_sess_sysfs_create(struct scst_session *sess); +void scst_sess_sysfs_del(struct scst_session *sess); int scst_recreate_sess_luns_link(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); -int scst_create_acn_sysfs(struct scst_acg *acg, struct scst_acn *acn); -void scst_acn_sysfs_del(struct scst_acg *acg, struct scst_acn *acn, - bool reassign); - -void scst_acg_sysfs_put(struct scst_acg *acg); +int scst_sgv_sysfs_create(struct sgv_pool *pool); +void scst_sgv_sysfs_del(struct sgv_pool *pool); +int scst_devt_sysfs_create(struct scst_dev_type *devt); +void scst_devt_sysfs_del(struct scst_dev_type *devt); +int scst_dev_sysfs_create(struct scst_device *dev); +void scst_dev_sysfs_del(struct scst_device *dev); +int scst_devt_dev_sysfs_create(struct scst_device *dev); +void scst_devt_dev_sysfs_del(struct scst_device *dev); +int scst_acg_sysfs_create(struct scst_tgt *tgt, + struct scst_acg *acg); +void scst_acg_sysfs_del(struct scst_acg *acg); +int scst_acg_dev_sysfs_create(struct scst_acg_dev *acg_dev, + struct kobject *parent); +void scst_acg_dev_sysfs_del(struct scst_acg_dev *acg_dev); +int scst_acn_sysfs_create(struct scst_acn *acn); +void scst_acn_sysfs_del(struct scst_acn *acn); #endif /* CONFIG_SCST_PROC */ diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index 816691222..8208a92c4 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -977,7 +977,7 @@ static int scst_proc_del_free_acg(struct scst_acg *acg, int remove_proc) } if (remove_proc) scst_proc_del_acg_tree(acg_proc_root, acg->acg_name); - scst_destroy_acg(acg); + scst_del_free_acg(acg); } out: TRACE_EXIT_RES(res); @@ -2066,7 +2066,7 @@ static ssize_t scst_proc_groups_devices_write(struct file *file, goto out_free_up; } else { /* Replace */ - rc = scst_acg_remove_dev(acg, acg_dev->dev, + rc = scst_acg_del_lun(acg, acg_dev->lun, false); if (rc) { res = rc; @@ -2076,13 +2076,16 @@ static ssize_t scst_proc_groups_devices_write(struct file *file, } } - rc = scst_acg_add_dev(acg, dev, virt_lun, read_only, - action == SCST_PROC_ACTION_ADD); + rc = scst_acg_add_lun(acg, NULL, dev, virt_lun, read_only, + false, NULL); if (rc) { res = rc; goto out_free_up; } + if (action == SCST_PROC_ACTION_ADD) + scst_report_luns_changed(acg); + if (dev_replaced) { struct scst_tgt_dev *tgt_dev; @@ -2100,17 +2103,32 @@ static ssize_t scst_proc_groups_devices_write(struct file *file, break; } case SCST_PROC_ACTION_DEL: - rc = scst_acg_remove_dev(acg, dev, true); - if (rc) { - res = rc; - goto out_free_up; + { + /* + * This code doesn't handle if there are >1 LUNs for the same + * device in the group. Instead, it always deletes the first + * entry. It wasn't fixed for compatibility reasons, because + * procfs is now obsoleted. + */ + struct scst_acg_dev *a; + list_for_each_entry(a, &acg->acg_dev_list, acg_dev_list_entry) { + if (a->dev == dev) { + rc = scst_acg_del_lun(acg, a->lun, true); + if (rc) { + res = rc; + goto out_free_up; + } + break; + } } + PRINT_ERROR("Device is not found in group %s", acg->acg_name); break; + } case SCST_PROC_ACTION_CLEAR: list_for_each_entry_safe(acg_dev, acg_dev_tmp, &acg->acg_dev_list, acg_dev_list_entry) { - rc = scst_acg_remove_dev(acg, acg_dev->dev, + rc = scst_acg_del_lun(acg, acg_dev->lun, list_is_last(&acg_dev->acg_dev_list_entry, &acg->acg_dev_list)); if (rc) { @@ -2235,7 +2253,7 @@ static ssize_t scst_proc_groups_names_write(struct file *file, switch (action) { case SCST_PROC_ACTION_ADD: - rc = scst_acg_add_name(acg, p); + rc = scst_acg_add_acn(acg, p); break; case SCST_PROC_ACTION_DEL: rc = scst_acg_remove_name(acg, p, true); @@ -2274,15 +2292,15 @@ static ssize_t scst_proc_groups_names_write(struct file *file, rc = scst_acg_remove_name(acg, name, false); if (rc != 0) goto out_free_unlock; - rc = scst_acg_add_name(new_acg, name); + rc = scst_acg_add_acn(new_acg, name); if (rc != 0) - scst_acg_add_name(acg, name); + scst_acg_add_acn(acg, name); break; } case SCST_PROC_ACTION_CLEAR: list_for_each_entry_safe(n, nn, &acg->acn_list, acn_list_entry) { - scst_acg_remove_acn(n); + scst_del_free_acn(n, false); } scst_check_reassign_sessions(); break; diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 13b5fd110..6d32fd696 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -37,14 +37,6 @@ static struct kobject *scst_devices_kobj; static struct kobject *scst_sgv_kobj; static struct kobject *scst_handlers_kobj; -/* Regular SCST sysfs operations */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) -const struct sysfs_ops scst_sysfs_ops; -#else -struct sysfs_ops scst_sysfs_ops; -#endif -EXPORT_SYMBOL(scst_sysfs_ops); - static const char *scst_dev_handler_types[] = { "Direct-access device (e.g., magnetic disk)", "Sequential-access device (e.g., magnetic tape)", @@ -162,11 +154,49 @@ static ssize_t scst_acg_io_grouping_type_store(struct kobject *kobj, static ssize_t scst_acn_file_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); -static void scst_sysfs_release(struct kobject *kobj) +/** + ** Regilar SCST sysfs ops + **/ +static ssize_t scst_show(struct kobject *kobj, struct attribute *attr, + char *buf) { - kfree(kobj); + 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); + + if (kobj_attr->store) + return kobj_attr->store(kobj, kobj_attr, buf, count); + else + return -EIO; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) +static const struct sysfs_ops scst_sysfs_ops = { +#else +static struct sysfs_ops scst_sysfs_ops = { +#endif + .show = scst_show, + .store = scst_store, +}; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) +const struct sysfs_ops *scst_sysfs_get_sysfs_ops(void) +#else +struct sysfs_ops *scst_sysfs_get_sysfs_ops(void) +#endif +{ + return &scst_sysfs_ops; +} +EXPORT_SYMBOL(scst_sysfs_get_sysfs_ops); + /* * Target Template */ @@ -178,11 +208,8 @@ static void scst_tgtt_release(struct kobject *kobj) 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; } @@ -354,7 +381,7 @@ static struct kobj_attribute scst_tgtt_mgmt = __ATTR(mgmt, S_IRUGO | S_IWUSR, scst_tgtt_mgmt_show, scst_tgtt_mgmt_store); -int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt) +int scst_tgtt_sysfs_create(struct scst_tgt_template *tgtt) { int retval = 0; const struct attribute **pattr; @@ -363,8 +390,6 @@ int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt) 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) { @@ -372,18 +397,13 @@ int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt) 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 (tgtt->add_target != NULL) { retval = sysfs_create_file(&tgtt->tgtt_kobj, &scst_tgtt_mgmt.attr); if (retval != 0) { PRINT_ERROR("Can't add mgmt attr for target driver %s", tgtt->name); - goto out; + goto out_del; } } @@ -397,7 +417,7 @@ int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt) PRINT_ERROR("Can't add attr %s for target " "driver %s", (*pattr)->name, tgtt->name); - goto out; + goto out_del; } pattr++; } @@ -410,7 +430,7 @@ int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt) if (retval != 0) { PRINT_ERROR("Can't add trace_flag for target " "driver %s", tgtt->name); - goto out; + goto out_del; } } #endif @@ -418,36 +438,38 @@ int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt) out: TRACE_EXIT_RES(retval); return retval; + +out_del: + scst_tgtt_sysfs_del(tgtt); + goto out; } -void scst_tgtt_sysfs_put(struct scst_tgt_template *tgtt) +void scst_tgtt_sysfs_del(struct scst_tgt_template *tgtt) { + int rc; + TRACE_ENTRY(); - if (tgtt->tgtt_kobj_initialized) { - int rc; + kobject_del(&tgtt->tgtt_kobj); + kobject_put(&tgtt->tgtt_kobj); - 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); + 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 (%d refs)...", tgtt->name, + atomic_read(&tgtt->tgtt_kobj.kref.refcount)); + wait_for_completion(&tgtt->tgtt_kobj_release_cmpl); + PRINT_INFO("Done waiting for releasing sysfs " + "entry for target template %s", tgtt->name); + } TRACE_EXIT(); return; } -/* - * Target directory implementation - */ +/** + ** Target directory implementation + **/ static void scst_tgt_release(struct kobject *kobj) { @@ -456,74 +478,14 @@ static void scst_tgt_release(struct kobject *kobj) TRACE_ENTRY(); tgt = container_of(kobj, struct scst_tgt, tgt_kobj); - - /* Let's make lockdep happy */ - up_write(&tgt->tgt_attr_rwsem); - - scst_free_tgt(tgt); + complete_all(&tgt->tgt_kobj_release_cmpl); TRACE_EXIT(); return; } -static ssize_t scst_tgt_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - int res; - struct kobj_attribute *kobj_attr; - struct scst_tgt *tgt; - - tgt = container_of(kobj, struct scst_tgt, tgt_kobj); - - if (down_read_trylock(&tgt->tgt_attr_rwsem) == 0) { - res = -ENOENT; - goto out; - } - - kobj_attr = container_of(attr, struct kobj_attribute, attr); - - res = kobj_attr->show(kobj, kobj_attr, buf); - - up_read(&tgt->tgt_attr_rwsem); - -out: - return res; -} - -static ssize_t scst_tgt_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, size_t count) -{ - int res; - struct kobj_attribute *kobj_attr; - struct scst_tgt *tgt; - - tgt = container_of(kobj, struct scst_tgt, tgt_kobj); - - if (down_read_trylock(&tgt->tgt_attr_rwsem) == 0) { - res = -ENOENT; - goto out; - } - - kobj_attr = container_of(attr, struct kobj_attribute, attr); - - if (kobj_attr->store) - res = kobj_attr->store(kobj, kobj_attr, buf, count); - else - res = -EIO; - - up_read(&tgt->tgt_attr_rwsem); - -out: - return res; -} - -static struct sysfs_ops scst_tgt_sysfs_ops = { - .show = scst_tgt_attr_show, - .store = scst_tgt_attr_store, -}; - static struct kobj_type tgt_ktype = { - .sysfs_ops = &scst_tgt_sysfs_ops, + .sysfs_ops = &scst_sysfs_ops, .release = scst_tgt_release, }; @@ -534,8 +496,7 @@ static void scst_acg_release(struct kobject *kobj) TRACE_ENTRY(); acg = container_of(kobj, struct scst_acg, acg_kobj); - - scst_destroy_acg(acg); + complete_all(&acg->acg_kobj_release_cmpl); TRACE_EXIT(); return; @@ -633,7 +594,7 @@ static ssize_t scst_tgt_enable_store(struct kobject *kobj, "%s", tgt->rel_tgt_id, tgt->tgt_name); } else { if (!scst_is_relative_target_port_id_unique( - tgt->rel_tgt_id, tgt)) { + tgt->rel_tgt_id, tgt)) { PRINT_ERROR("Relative port id %d is not unique", tgt->rel_tgt_id); res = -EBADSLT; @@ -662,16 +623,14 @@ 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 scst_tgt_sysfs_create(struct scst_tgt *tgt) { int retval; const struct attribute **pattr; TRACE_ENTRY(); - init_rwsem(&tgt->tgt_attr_rwsem); - - tgt->tgt_kobj_initialized = 1; + init_completion(&tgt->tgt_kobj_release_cmpl); retval = kobject_init_and_add(&tgt->tgt_kobj, &tgt_ktype, &tgt->tgtt->tgtt_kobj, tgt->tgt_name); @@ -680,11 +639,6 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) 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_target != NULL) && (tgt->tgtt->is_target_enabled != NULL)) { retval = sysfs_create_file(&tgt->tgt_kobj, @@ -692,7 +646,7 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) if (retval != 0) { PRINT_ERROR("Can't add attr %s to sysfs", tgt_enable_attr.attr.name); - goto out; + goto out_err; } } @@ -712,7 +666,7 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) if (retval != 0) { PRINT_ERROR("Can't add attribute %s for tgt %s", scst_luns_mgmt.attr.name, tgt->tgt_name); - goto out; + goto out_err; } tgt->tgt_ini_grp_kobj = kobject_create_and_add("ini_groups", @@ -728,7 +682,7 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) if (retval != 0) { PRINT_ERROR("Can't add attribute %s for tgt %s", scst_ini_group_mgmt.attr.name, tgt->tgt_name); - goto out; + goto out_err; } retval = sysfs_create_file(&tgt->tgt_kobj, @@ -736,7 +690,7 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) if (retval != 0) { PRINT_ERROR("Can't add attribute %s for tgt %s", scst_rel_tgt_id.attr.name, tgt->tgt_name); - goto out; + goto out_err; } retval = sysfs_create_file(&tgt->tgt_kobj, @@ -744,7 +698,7 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) if (retval != 0) { PRINT_ERROR("Can't add attribute %s for tgt %s", scst_tgt_addr_method.attr.name, tgt->tgt_name); - goto out; + goto out_err; } retval = sysfs_create_file(&tgt->tgt_kobj, @@ -752,7 +706,7 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) if (retval != 0) { PRINT_ERROR("Can't add attribute %s for tgt %s", scst_tgt_io_grouping_type.attr.name, tgt->tgt_name); - goto out; + goto out_err; } pattr = tgt->tgtt->tgt_attrs; @@ -764,7 +718,7 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt) if (retval != 0) { PRINT_ERROR("Can't add tgt attr %s for tgt %s", (*pattr)->name, tgt->tgt_name); - goto out; + goto out_err; } pattr++; } @@ -776,54 +730,49 @@ out: out_nomem: retval = -ENOMEM; + +out_err: + scst_tgt_sysfs_del(tgt); goto out; } -/* - * Must not be called under scst_mutex or there can be a deadlock with - * tgt_attr_rwsem - */ -void scst_tgt_sysfs_prepare_put(struct scst_tgt *tgt) +void scst_tgt_sysfs_del(struct scst_tgt *tgt) { - if (tgt->tgt_kobj_initialized) { - down_write(&tgt->tgt_attr_rwsem); - tgt->tgt_kobj_put_prepared = 1; + int rc; + + TRACE_ENTRY(); + + kobject_del(tgt->tgt_sess_kobj); + kobject_put(tgt->tgt_sess_kobj); + + 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); + + rc = wait_for_completion_timeout(&tgt->tgt_kobj_release_cmpl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing sysfs entry " + "for target %s (%d refs)...", tgt->tgt_name, + atomic_read(&tgt->tgt_kobj.kref.refcount)); + wait_for_completion(&tgt->tgt_kobj_release_cmpl); + PRINT_INFO("Done waiting for releasing sysfs " + "entry for target %s", tgt->tgt_name); } + TRACE_EXIT(); return; } -/* - * Must not be called under scst_mutex or there can be a deadlock with - * tgt_attr_rwsem - */ -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); +/** + ** Devices directory implementation + **/ - 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); - - if (!tgt->tgt_kobj_put_prepared) - down_write(&tgt->tgt_attr_rwsem); - kobject_put(&tgt->tgt_kobj); - } else - scst_free_tgt(tgt); - return; -} - -/* - * Devices directory implementation - */ - -static ssize_t scst_device_sysfs_type_show(struct kobject *kobj, +static ssize_t scst_dev_sysfs_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int pos = 0; @@ -840,11 +789,11 @@ static ssize_t scst_device_sysfs_type_show(struct kobject *kobj, } static struct kobj_attribute device_type_attr = - __ATTR(type, S_IRUGO, scst_device_sysfs_type_show, NULL); + __ATTR(type, S_IRUGO, scst_dev_sysfs_type_show, NULL); #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) -static ssize_t scst_device_sysfs_dump_prs(struct kobject *kobj, +static ssize_t scst_dev_sysfs_dump_prs(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct scst_device *dev; @@ -860,11 +809,11 @@ static ssize_t scst_device_sysfs_dump_prs(struct kobject *kobj, } static struct kobj_attribute device_dump_prs_attr = - __ATTR(dump_prs, S_IWUSR, NULL, scst_device_sysfs_dump_prs); + __ATTR(dump_prs, S_IWUSR, NULL, scst_dev_sysfs_dump_prs); #endif /* defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */ -static ssize_t scst_device_sysfs_threads_num_show(struct kobject *kobj, +static ssize_t scst_dev_sysfs_threads_num_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int pos = 0; @@ -882,7 +831,7 @@ static ssize_t scst_device_sysfs_threads_num_show(struct kobject *kobj, return pos; } -static ssize_t scst_device_sysfs_threads_data_store(struct scst_device *dev, +static ssize_t scst_dev_sysfs_threads_data_store(struct scst_device *dev, int threads_num, enum scst_dev_type_threads_pool_type threads_pool_type) { int res = 0; @@ -929,7 +878,7 @@ out: return res; } -static ssize_t scst_device_sysfs_threads_num_store(struct kobject *kobj, +static ssize_t scst_dev_sysfs_threads_num_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int res; @@ -952,7 +901,7 @@ static ssize_t scst_device_sysfs_threads_num_store(struct kobject *kobj, goto out; } - res = scst_device_sysfs_threads_data_store(dev, newtn, + res = scst_dev_sysfs_threads_data_store(dev, newtn, dev->threads_pool_type); if (res != 0) goto out; @@ -968,10 +917,10 @@ out: static struct kobj_attribute device_threads_num_attr = __ATTR(threads_num, S_IRUGO | S_IWUSR, - scst_device_sysfs_threads_num_show, - scst_device_sysfs_threads_num_store); + scst_dev_sysfs_threads_num_show, + scst_dev_sysfs_threads_num_store); -static ssize_t scst_device_sysfs_threads_pool_type_show(struct kobject *kobj, +static ssize_t scst_dev_sysfs_threads_pool_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int pos = 0; @@ -1010,7 +959,7 @@ out: return pos; } -static ssize_t scst_device_sysfs_threads_pool_type_store(struct kobject *kobj, +static ssize_t scst_dev_sysfs_threads_pool_type_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int res; @@ -1030,7 +979,7 @@ static ssize_t scst_device_sysfs_threads_pool_type_store(struct kobject *kobj, TRACE_DBG("buf %s, count %zd, newtpt %d", buf, count, newtpt); - res = scst_device_sysfs_threads_data_store(dev, dev->threads_num, + res = scst_dev_sysfs_threads_data_store(dev, dev->threads_num, newtpt); if (res != 0) goto out; @@ -1047,37 +996,30 @@ out: static struct kobj_attribute device_threads_pool_type_attr = __ATTR(threads_pool_type, S_IRUGO | S_IWUSR, - scst_device_sysfs_threads_pool_type_show, - scst_device_sysfs_threads_pool_type_store); + scst_dev_sysfs_threads_pool_type_show, + scst_dev_sysfs_threads_pool_type_store); -static struct attribute *scst_device_attrs[] = { +static struct attribute *scst_dev_attrs[] = { &device_type_attr.attr, -#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) - &device_dump_prs_attr.attr, -#endif &device_threads_num_attr.attr, &device_threads_pool_type_attr.attr, NULL, }; -static void scst_sysfs_device_release(struct kobject *kobj) +static void scst_sysfs_dev_release(struct kobject *kobj) { struct scst_device *dev; TRACE_ENTRY(); dev = container_of(kobj, struct scst_device, dev_kobj); - - /* Let's make lockdep happy */ - up_write(&dev->dev_attr_rwsem); - - scst_free_device(dev); + complete_all(&dev->dev_kobj_release_cmpl); TRACE_EXIT(); return; } -int scst_create_devt_dev_sysfs(struct scst_device *dev) +int scst_devt_dev_sysfs_create(struct scst_device *dev) { int retval = 0; const struct attribute **pattr; @@ -1087,13 +1029,6 @@ int scst_create_devt_dev_sysfs(struct scst_device *dev) 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) { @@ -1107,7 +1042,7 @@ int scst_create_devt_dev_sysfs(struct scst_device *dev) if (retval != 0) { PRINT_ERROR("Can't create handler link for dev %s", dev->virt_name); - goto out; + goto out_err; } pattr = dev->handler->dev_attrs; @@ -1117,7 +1052,7 @@ int scst_create_devt_dev_sysfs(struct scst_device *dev) if (retval != 0) { PRINT_ERROR("Can't add dev attr %s for dev %s", (*pattr)->name, dev->virt_name); - goto out; + goto out_err; } pattr++; } @@ -1126,9 +1061,13 @@ int scst_create_devt_dev_sysfs(struct scst_device *dev) out: TRACE_EXIT_RES(retval); return retval; + +out_err: + scst_devt_dev_sysfs_del(dev); + goto out; } -void scst_devt_dev_sysfs_put(struct scst_device *dev) +void scst_devt_dev_sysfs_del(struct scst_device *dev) { const struct attribute **pattr; @@ -1137,8 +1076,6 @@ void scst_devt_dev_sysfs_put(struct scst_device *dev) 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) { @@ -1155,97 +1092,34 @@ out: return; } -static ssize_t scst_dev_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - int res; - struct kobj_attribute *kobj_attr; - struct scst_device *dev; - - dev = container_of(kobj, struct scst_device, dev_kobj); - - if (down_read_trylock(&dev->dev_attr_rwsem) == 0) { - res = -ENOENT; - goto out; - } - - kobj_attr = container_of(attr, struct kobj_attribute, attr); - - res = kobj_attr->show(kobj, kobj_attr, buf); - - up_read(&dev->dev_attr_rwsem); - -out: - return res; -} - -static ssize_t scst_dev_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - int res; - struct kobj_attribute *kobj_attr; - struct scst_device *dev; - - dev = container_of(kobj, struct scst_device, dev_kobj); - - if (down_read_trylock(&dev->dev_attr_rwsem) == 0) { - res = -ENOENT; - goto out; - } - - kobj_attr = container_of(attr, struct kobj_attribute, attr); - - if (kobj_attr->store) - res = kobj_attr->store(kobj, kobj_attr, buf, count); - else - res = -EIO; - - up_read(&dev->dev_attr_rwsem); - -out: - return res; -} - -static struct sysfs_ops scst_dev_sysfs_ops = { - .show = scst_dev_attr_show, - .store = scst_dev_attr_store, +static struct kobj_type scst_dev_ktype = { + .sysfs_ops = &scst_sysfs_ops, + .release = scst_sysfs_dev_release, + .default_attrs = scst_dev_attrs, }; -static struct kobj_type scst_device_ktype = { - .sysfs_ops = &scst_dev_sysfs_ops, - .release = scst_sysfs_device_release, - .default_attrs = scst_device_attrs, -}; - -int scst_create_device_sysfs(struct scst_device *dev) +int scst_dev_sysfs_create(struct scst_device *dev) { int retval = 0; TRACE_ENTRY(); - init_rwsem(&dev->dev_attr_rwsem); + init_completion(&dev->dev_kobj_release_cmpl); - dev->dev_kobj_initialized = 1; - - retval = kobject_init_and_add(&dev->dev_kobj, &scst_device_ktype, + retval = kobject_init_and_add(&dev->dev_kobj, &scst_dev_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; + goto out_del; } if (dev->scsi_dev != NULL) { @@ -1254,41 +1128,60 @@ int scst_create_device_sysfs(struct scst_device *dev) if (retval != 0) { PRINT_ERROR("Can't create scsi_device link for dev %s", dev->virt_name); - goto out; + goto out_del; } } +#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) + if (dev->scsi_dev == NULL) { + retval = sysfs_create_file(&dev->dev_kobj, + &device_dump_prs_attr.attr); + if (retval != 0) { + PRINT_ERROR("Can't create attr %s for dev %s", + device_dump_prs_attr.attr.name, dev->virt_name); + goto out_del; + } + } +#endif + out: TRACE_EXIT_RES(retval); return retval; + +out_del: + scst_dev_sysfs_del(dev); + goto out; } -/* - * Must not be called under scst_mutex or there can be a deadlock with - * dev_attr_rwsem - */ -void scst_device_sysfs_put(struct scst_device *dev) +void scst_dev_sysfs_del(struct scst_device *dev) { + int rc; + TRACE_ENTRY(); - if (dev->dev_kobj_initialized) { - kobject_del(dev->dev_exp_kobj); - kobject_put(dev->dev_exp_kobj); + kobject_del(dev->dev_exp_kobj); + kobject_put(dev->dev_exp_kobj); - kobject_del(&dev->dev_kobj); + kobject_del(&dev->dev_kobj); + kobject_put(&dev->dev_kobj); - down_write(&dev->dev_attr_rwsem); - kobject_put(&dev->dev_kobj); - } else - scst_free_device(dev); + rc = wait_for_completion_timeout(&dev->dev_kobj_release_cmpl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing sysfs entry " + "for device %s (%d refs)...", dev->virt_name, + atomic_read(&dev->dev_kobj.kref.refcount)); + wait_for_completion(&dev->dev_kobj_release_cmpl); + PRINT_INFO("Done waiting for releasing sysfs " + "entry for device %s", dev->virt_name); + } TRACE_EXIT(); return; } -/* - * Target sessions directory implementation - */ +/** + ** Target/sessions directory implementation + **/ static ssize_t scst_sess_sysfs_commands_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -1367,74 +1260,14 @@ static void scst_sysfs_session_release(struct kobject *kobj) TRACE_ENTRY(); sess = container_of(kobj, struct scst_session, sess_kobj); - - /* Let's make lockdep happy */ - up_write(&sess->sess_attr_rwsem); - - scst_release_session(sess); + complete_all(&sess->sess_kobj_release_cmpl); TRACE_EXIT(); return; } -static ssize_t scst_sess_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - int res; - struct kobj_attribute *kobj_attr; - struct scst_session *sess; - - sess = container_of(kobj, struct scst_session, sess_kobj); - - if (down_read_trylock(&sess->sess_attr_rwsem) == 0) { - res = -ENOENT; - goto out; - } - - kobj_attr = container_of(attr, struct kobj_attribute, attr); - - res = kobj_attr->show(kobj, kobj_attr, buf); - - up_read(&sess->sess_attr_rwsem); - -out: - return res; -} - -static ssize_t scst_sess_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - int res; - struct kobj_attribute *kobj_attr; - struct scst_session *sess; - - sess = container_of(kobj, struct scst_session, sess_kobj); - - if (down_read_trylock(&sess->sess_attr_rwsem) == 0) { - res = -ENOENT; - goto out; - } - - kobj_attr = container_of(attr, struct kobj_attribute, attr); - - if (kobj_attr->store) - res = kobj_attr->store(kobj, kobj_attr, buf, count); - else - res = -EIO; - - up_read(&sess->sess_attr_rwsem); - -out: - return res; -} - -static struct sysfs_ops scst_sess_sysfs_ops = { - .show = scst_sess_attr_show, - .store = scst_sess_attr_store, -}; - static struct kobj_type scst_session_ktype = { - .sysfs_ops = &scst_sess_sysfs_ops, + .sysfs_ops = &scst_sysfs_ops, .release = scst_sysfs_session_release, .default_attrs = scst_session_attrs, }; @@ -1443,6 +1276,11 @@ static int scst_create_sess_luns_link(struct scst_session *sess) { int retval; + /* + * No locks are needed, because sess supposed to be in acg->acg_sess_list + * and tgt->sess_list, so blocking them from disappearing. + */ + if (sess->acg == sess->tgt->default_acg) retval = sysfs_create_link(&sess->sess_kobj, sess->tgt->tgt_luns_kobj, "luns"); @@ -1463,8 +1301,7 @@ int scst_recreate_sess_luns_link(struct scst_session *sess) return scst_create_sess_luns_link(sess); } -/* scst_mutex supposed to be locked */ -int scst_create_sess_sysfs(struct scst_session *sess) +int scst_sess_sysfs_create(struct scst_session *sess) { int retval = 0; struct scst_session *s; @@ -1474,11 +1311,12 @@ int scst_create_sess_sysfs(struct scst_session *sess) TRACE_ENTRY(); + mutex_lock(&scst_mutex); + restart: list_for_each_entry(s, &sess->tgt->sess_list, sess_list_entry) { - if (!s->sess_kobj_initialized) + if (!sess->sess_kobj_ready) continue; - if (strcmp(name, kobject_name(&s->sess_kobj)) == 0) { if (s == sess) continue; @@ -1503,9 +1341,9 @@ restart: } } - init_rwsem(&sess->sess_attr_rwsem); + mutex_unlock(&scst_mutex); - sess->sess_kobj_initialized = 1; + init_completion(&sess->sess_kobj_release_cmpl); retval = kobject_init_and_add(&sess->sess_kobj, &scst_session_ktype, sess->tgt->tgt_sess_kobj, name); @@ -1514,10 +1352,7 @@ restart: goto out_free; } - /* - * In case of errors there's no need for additional cleanup, because - * it will be done by the _put function() called by the caller. - */ + sess->sess_kobj_ready = 1; pattr = sess->tgt->tgtt->sess_attrs; if (pattr != NULL) { @@ -1543,29 +1378,36 @@ out_free: return retval; } -/* - * Must not be called under scst_mutex or there can be a deadlock with - * sess_attr_rwsem - */ -void scst_sess_sysfs_put(struct scst_session *sess) +void scst_sess_sysfs_del(struct scst_session *sess) { + int rc; + TRACE_ENTRY(); - if (sess->sess_kobj_initialized) { - kobject_del(&sess->sess_kobj); + if (sess->sess_kobj_ready) + goto out; - down_write(&sess->sess_attr_rwsem); - kobject_put(&sess->sess_kobj); - } else - scst_release_session(sess); + kobject_del(&sess->sess_kobj); + kobject_put(&sess->sess_kobj); + rc = wait_for_completion_timeout(&sess->sess_kobj_release_cmpl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing sysfs entry " + "for session from %s (%d refs)...", sess->initiator_name, + atomic_read(&sess->sess_kobj.kref.refcount)); + wait_for_completion(&sess->sess_kobj_release_cmpl); + PRINT_INFO("Done waiting for releasing sysfs " + "entry for session %s", sess->initiator_name); + } + +out: TRACE_EXIT(); return; } -/* - * Target luns directory implementation - */ +/** + ** Target luns directory implementation + **/ static void scst_acg_dev_release(struct kobject *kobj) { @@ -1574,8 +1416,7 @@ static void scst_acg_dev_release(struct kobject *kobj) TRACE_ENTRY(); acg_dev = container_of(kobj, struct scst_acg_dev, acg_dev_kobj); - - scst_acg_dev_destroy(acg_dev); + complete_all(&acg_dev->acg_dev_kobj_release_cmpl); TRACE_EXIT(); return; @@ -1609,63 +1450,78 @@ static struct kobj_type acg_dev_ktype = { .default_attrs = lun_attrs, }; -static int scst_create_acg_dev_sysfs(struct scst_acg *acg, - unsigned int virt_lun, struct kobject *parent) +void scst_acg_dev_sysfs_del(struct scst_acg_dev *acg_dev) { - int retval; - struct scst_acg_dev *acg_dev = NULL, *acg_dev_tmp; - char str[20]; + int rc; 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; + if (acg_dev->dev != NULL) { + sysfs_remove_link(acg_dev->dev->dev_exp_kobj, + acg_dev->acg_dev_link_name); + kobject_put(&acg_dev->dev->dev_kobj); } - snprintf(str, sizeof(str), "export%u", - acg_dev->dev->dev_exported_lun_num++); + kobject_del(&acg_dev->acg_dev_kobj); + kobject_put(&acg_dev->acg_dev_kobj); + + rc = wait_for_completion_timeout(&acg_dev->acg_dev_kobj_release_cmpl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing sysfs entry " + "for acg_dev %p (%d refs)...", acg_dev, + atomic_read(&acg_dev->acg_dev_kobj.kref.refcount)); + wait_for_completion(&acg_dev->acg_dev_kobj_release_cmpl); + PRINT_INFO("Done waiting for releasing sysfs " + "entry for acg_dev %p", acg_dev); + } + + TRACE_EXIT(); + return; +} + +int scst_acg_dev_sysfs_create(struct scst_acg_dev *acg_dev, + struct kobject *parent) +{ + int retval; + + TRACE_ENTRY(); + + init_completion(&acg_dev->acg_dev_kobj_release_cmpl); + + retval = kobject_init_and_add(&acg_dev->acg_dev_kobj, &acg_dev_ktype, + parent, "%u", acg_dev->lun); + if (retval != 0) { + PRINT_ERROR("Can't add acg_dev %p to sysfs", acg_dev); + goto out; + } kobject_get(&acg_dev->dev->dev_kobj); - acg_dev->acg_dev_kobj_initialized = 1; - - retval = kobject_init_and_add(&acg_dev->acg_dev_kobj, &acg_dev_ktype, - 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. - */ + snprintf(acg_dev->acg_dev_link_name, sizeof(acg_dev->acg_dev_link_name), + "export%u", acg_dev->dev->dev_exported_lun_num++); retval = sysfs_create_link(acg_dev->dev->dev_exp_kobj, - &acg_dev->acg_dev_kobj, str); + &acg_dev->acg_dev_kobj, acg_dev->acg_dev_link_name); if (retval != 0) { - PRINT_ERROR("Can't create acg %s LUN link", acg->acg_name); - goto out; + PRINT_ERROR("Can't create acg %s LUN link", + acg_dev->acg->acg_name); + goto out_del; } 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; + PRINT_ERROR("Can't create acg %s device link", + acg_dev->acg->acg_name); + goto out_del; } out: return retval; + +out_del: + scst_acg_dev_sysfs_del(acg_dev); + goto out; } static ssize_t __scst_luns_mgmt_store(struct scst_acg *acg, @@ -1724,7 +1580,8 @@ static ssize_t __scst_luns_mgmt_store(struct scst_acg *acg, goto out_free_resume; } - if (action != SCST_LUN_ACTION_CLEAR) { + if ((action != SCST_LUN_ACTION_CLEAR) && + (action != SCST_LUN_ACTION_DEL)) { if (!isspace(*p)) { PRINT_ERROR("%s", "Syntax error"); res = -EINVAL; @@ -1860,7 +1717,7 @@ static ssize_t __scst_luns_mgmt_store(struct scst_acg *acg, goto out_free_up; } else { /* Replace */ - res = scst_acg_remove_dev(acg, acg_dev->dev, + res = scst_acg_del_lun(acg, acg_dev->lun, false); if (res != 0) goto out_free_up; @@ -1869,17 +1726,11 @@ static ssize_t __scst_luns_mgmt_store(struct scst_acg *acg, } } - res = scst_acg_add_dev(acg, dev, virt_lun, read_only, - !dev_replaced); + res = scst_acg_add_lun(acg, kobj, dev, virt_lun, read_only, + !dev_replaced, NULL); 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; - } - if (dev_replaced) { struct scst_tgt_dev *tgt_dev; @@ -1898,7 +1749,11 @@ static ssize_t __scst_luns_mgmt_store(struct scst_acg *acg, break; } case SCST_LUN_ACTION_DEL: - res = scst_acg_remove_dev(acg, dev, true); + while (isspace(*p) && *p != '\0') + p++; + virt_lun = simple_strtoul(p, &p, 0); + + res = scst_acg_del_lun(acg, virt_lun, true); if (res != 0) goto out_free_up; break; @@ -1908,7 +1763,7 @@ static ssize_t __scst_luns_mgmt_store(struct scst_acg *acg, list_for_each_entry_safe(acg_dev, acg_dev_tmp, &acg->acg_dev_list, acg_dev_list_entry) { - res = scst_acg_remove_dev(acg, acg_dev->dev, + res = scst_acg_del_lun(acg, acg_dev->lun, list_is_last(&acg_dev->acg_dev_list_entry, &acg->acg_dev_list)); if (res) @@ -1932,10 +1787,6 @@ 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 #undef SCST_LUN_ACTION_REPLACE @@ -1946,14 +1797,11 @@ 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 [parameters]\" " - ">mgmt\n" - " echo \"add|del VNAME lun [parameters]\" " - ">mgmt\n" - " echo \"replace H:C:I:L lun [parameters]\" " - ">mgmt\n" - " echo \"replace VNAME lun [parameters]\" " - ">mgmt\n" + static char *help = "Usage: echo \"add|del H:C:I:L lun [parameters]\" >mgmt\n" + " echo \"add VNAME lun [parameters]\" >mgmt\n" + " echo \"del lun\" >mgmt\n" + " echo \"replace H:C:I:L lun [parameters]\" >mgmt\n" + " echo \"replace VNAME lun [parameters]\" >mgmt\n" " echo \"clear\" >mgmt\n" "\n" "where parameters are one or more " @@ -2161,14 +2009,43 @@ out: return res; } -static int scst_create_acg_sysfs(struct scst_tgt *tgt, +void scst_acg_sysfs_del(struct scst_acg *acg) +{ + int rc; + + TRACE_ENTRY(); + + kobject_del(acg->luns_kobj); + kobject_put(acg->luns_kobj); + + kobject_del(acg->initiators_kobj); + kobject_put(acg->initiators_kobj); + + kobject_del(&acg->acg_kobj); + kobject_put(&acg->acg_kobj); + + rc = wait_for_completion_timeout(&acg->acg_kobj_release_cmpl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing sysfs entry " + "for acg %s (%d refs)...", acg->acg_name, + atomic_read(&acg->acg_kobj.kref.refcount)); + wait_for_completion(&acg->acg_kobj_release_cmpl); + PRINT_INFO("Done waiting for releasing sysfs " + "entry for acg %s", acg->acg_name); + } + + TRACE_EXIT(); + return; +} + +int scst_acg_sysfs_create(struct scst_tgt *tgt, struct scst_acg *acg) { int retval = 0; TRACE_ENTRY(); - acg->acg_kobj_initialized = 1; + init_completion(&acg->acg_kobj_release_cmpl); retval = kobject_init_and_add(&acg->acg_kobj, &acg_ktype, tgt->tgt_ini_grp_kobj, acg->acg_name); @@ -2182,72 +2059,54 @@ static int scst_create_acg_sysfs(struct scst_tgt *tgt, PRINT_ERROR("Can't create luns kobj for tgt %s", tgt->tgt_name); retval = -ENOMEM; - goto out; + goto out_del; } retval = sysfs_create_file(acg->luns_kobj, &scst_acg_luns_mgmt.attr); if (retval != 0) { PRINT_ERROR("Can't add tgt attr %s for tgt %s", scst_acg_luns_mgmt.attr.name, tgt->tgt_name); - goto out; + goto out_del; } acg->initiators_kobj = kobject_create_and_add("initiators", - &acg->acg_kobj); + &acg->acg_kobj); if (acg->initiators_kobj == NULL) { PRINT_ERROR("Can't create initiators kobj for tgt %s", tgt->tgt_name); retval = -ENOMEM; - goto out; + goto out_del; } retval = sysfs_create_file(acg->initiators_kobj, - &scst_acg_ini_mgmt.attr); + &scst_acg_ini_mgmt.attr); if (retval != 0) { PRINT_ERROR("Can't add tgt attr %s for tgt %s", scst_acg_ini_mgmt.attr.name, tgt->tgt_name); - goto out; + goto out_del; } retval = sysfs_create_file(&acg->acg_kobj, &scst_acg_addr_method.attr); if (retval != 0) { PRINT_ERROR("Can't add tgt attr %s for tgt %s", scst_acg_addr_method.attr.name, tgt->tgt_name); - goto out; + goto out_del; } retval = sysfs_create_file(&acg->acg_kobj, &scst_acg_io_grouping_type.attr); if (retval != 0) { PRINT_ERROR("Can't add tgt attr %s for tgt %s", scst_acg_io_grouping_type.attr.name, tgt->tgt_name); - goto out; + goto out_del; } out: TRACE_EXIT_RES(retval); return retval; -} -void scst_acg_sysfs_put(struct scst_acg *acg) -{ - TRACE_ENTRY(); - - if (acg->acg_kobj_initialized) { - scst_clear_acg(acg); - - kobject_del(acg->luns_kobj); - kobject_put(acg->luns_kobj); - - kobject_del(acg->initiators_kobj); - kobject_put(acg->initiators_kobj); - - kobject_del(&acg->acg_kobj); - kobject_put(&acg->acg_kobj); - } else - scst_destroy_acg(acg); - - TRACE_EXIT(); - return; +out_del: + scst_acg_sysfs_del(acg); + goto out; } static ssize_t scst_acg_addr_method_show(struct kobject *kobj, @@ -2407,10 +2266,6 @@ static ssize_t scst_ini_group_mgmt_store(struct kobject *kobj, kfree(name); if (acg == NULL) goto out_free_up; - - res = scst_create_acg_sysfs(tgt, acg); - if (res != 0) - goto out_free_acg; break; case SCST_INI_GROUP_ACTION_DEL: TRACE_DBG("Deleting group '%s'", p); @@ -2424,7 +2279,7 @@ static ssize_t scst_ini_group_mgmt_store(struct kobject *kobj, res = -EBUSY; goto out_free_up; } - scst_acg_sysfs_put(acg); + scst_del_free_acg(acg); break; } @@ -2443,10 +2298,6 @@ out: TRACE_EXIT_RES(res); return res; -out_free_acg: - scst_acg_sysfs_put(acg); - goto out_free_up; - #undef SCST_LUN_ACTION_CREATE #undef SCST_LUN_ACTION_DEL } @@ -2525,10 +2376,11 @@ out_err: goto out; } -int scst_create_acn_sysfs(struct scst_acg *acg, struct scst_acn *acn) +int scst_acn_sysfs_create(struct scst_acn *acn) { int retval = 0; int len; + struct scst_acg *acg = acn->acg; struct kobj_attribute *attr = NULL; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) #ifdef CONFIG_DEBUG_LOCK_ALLOC @@ -2588,9 +2440,10 @@ out_free: goto out; } -void scst_acn_sysfs_del(struct scst_acg *acg, struct scst_acn *acn, - bool reassign) +void scst_acn_sysfs_del(struct scst_acn *acn) { + struct scst_acg *acg = acn->acg; + TRACE_ENTRY(); if (acn->acn_attr != NULL) { @@ -2599,9 +2452,6 @@ void scst_acn_sysfs_del(struct scst_acg *acg, struct scst_acn *acn, kfree(acn->acn_attr->attr.name); kfree(acn->acn_attr); } - scst_acg_remove_acn(acn); - if (reassign) - scst_check_reassign_sessions(); TRACE_EXIT(); return; @@ -2728,7 +2578,7 @@ static ssize_t scst_acg_ini_mgmt_store(struct kobject *kobj, goto out_free_up; } - res = scst_acg_add_name(acg, name); + res = scst_acg_add_acn(acg, name); if (res != 0) goto out_free_up; break; @@ -2745,7 +2595,7 @@ static ssize_t scst_acg_ini_mgmt_store(struct kobject *kobj, goto out_free_up; } - acn = scst_acg_find_name(acg, name); + acn = scst_find_acn(acg, name); if (acn == NULL) { PRINT_ERROR("Unable to find " "initiator '%s' in group '%s'", @@ -2753,12 +2603,12 @@ static ssize_t scst_acg_ini_mgmt_store(struct kobject *kobj, res = -EINVAL; goto out_free_up; } - scst_acn_sysfs_del(acg, acn, true); + scst_del_free_acn(acn, true); break; case SCST_ACG_ACTION_INI_CLEAR: list_for_each_entry_safe(acn, acn_tmp, &acg->acn_list, acn_list_entry) { - scst_acn_sysfs_del(acg, acn, false); + scst_del_free_acn(acn, false); } scst_check_reassign_sessions(); break; @@ -2827,7 +2677,7 @@ static ssize_t scst_acg_ini_mgmt_store(struct kobject *kobj, tgt = container_of(k, struct scst_tgt, tgt_kobj); } - acn = scst_acg_find_name(acg, name); + acn = scst_find_acn(acg, name); if (acn == NULL) { PRINT_ERROR("Unable to find " "initiator '%s' in group '%s'", @@ -2842,15 +2692,15 @@ static ssize_t scst_acg_ini_mgmt_store(struct kobject *kobj, res = -EINVAL; goto out_free_up; } - if (scst_acg_find_name(acg_dest, name) != NULL) { + if (scst_find_acn(acg_dest, name) != NULL) { PRINT_ERROR("Initiator '%s' already exists in group '%s'", name, acg_dest->acg_name); res = -EEXIST; goto out_free_up; } - scst_acn_sysfs_del(acg, acn, false); + scst_del_free_acn(acn, false); - res = scst_acg_add_name(acg_dest, name); + res = scst_acg_add_acn(acg_dest, name); if (res != 0) goto out_free_up; break; @@ -2877,9 +2727,9 @@ out: #undef SCST_ACG_ACTION_INI_MOVE } -/* - * SGV directory implementation - */ +/** + ** SGV directory implementation + **/ static struct kobj_attribute sgv_stat_attr = __ATTR(stats, S_IRUGO | S_IWUSR, sgv_sysfs_stat_show, @@ -2897,8 +2747,7 @@ static void sgv_kobj_release(struct kobject *kobj) TRACE_ENTRY(); pool = container_of(kobj, struct sgv_pool, sgv_kobj); - - sgv_pool_destroy(pool); + complete_all(&pool->sgv_kobj_release_cmpl); TRACE_EXIT(); return; @@ -2910,13 +2759,13 @@ static struct kobj_type sgv_pool_ktype = { .default_attrs = sgv_attrs, }; -int scst_create_sgv_sysfs(struct sgv_pool *pool) +int scst_sgv_sysfs_create(struct sgv_pool *pool) { int retval; TRACE_ENTRY(); - pool->sgv_kobj_initialized = 1; + init_completion(&pool->sgv_kobj_release_cmpl); retval = kobject_init_and_add(&pool->sgv_kobj, &sgv_pool_ktype, scst_sgv_kobj, pool->name); @@ -2930,14 +2779,26 @@ out: return retval; } -/* pool can be dead upon exit from this function! */ -void scst_sgv_sysfs_put(struct sgv_pool *pool) +void scst_sgv_sysfs_del(struct sgv_pool *pool) { - if (pool->sgv_kobj_initialized) { - kobject_del(&pool->sgv_kobj); - kobject_put(&pool->sgv_kobj); - } else - sgv_pool_destroy(pool); + int rc; + + TRACE_ENTRY(); + + kobject_del(&pool->sgv_kobj); + kobject_put(&pool->sgv_kobj); + + rc = wait_for_completion_timeout(&pool->sgv_kobj_release_cmpl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing sysfs entry " + "for SGV pool %s (%d refs)...", pool->name, + atomic_read(&pool->sgv_kobj.kref.refcount)); + wait_for_completion(&pool->sgv_kobj_release_cmpl); + PRINT_INFO("Done waiting for releasing sysfs " + "entry for SGV pool %s", pool->name); + } + + TRACE_EXIT(); return; } @@ -2950,15 +2811,20 @@ static struct attribute *sgv_default_attrs[] = { NULL, }; +static void scst_sysfs_release(struct kobject *kobj) +{ + kfree(kobj); +} + 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 - */ +/** + ** SCST sysfs root directory implementation + **/ static ssize_t scst_threads_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -3384,54 +3250,25 @@ 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); - - if (kobj_attr->store) - return kobj_attr->store(kobj, kobj_attr, buf, count); - else - return -EIO; -} - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) -const struct sysfs_ops scst_sysfs_ops = { -#else -struct sysfs_ops scst_sysfs_ops = { -#endif - .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) +/* + * Dev handlers + */ + +static void scst_devt_release(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; } @@ -3506,7 +3343,7 @@ static struct attribute *scst_devt_default_attrs[] = { static struct kobj_type scst_devt_ktype = { .sysfs_ops = &scst_sysfs_ops, - .release = scst_devt_free, + .release = scst_devt_release, .default_attrs = scst_devt_default_attrs, }; @@ -3771,7 +3608,7 @@ static struct kobj_attribute scst_devt_pass_through_mgmt = __ATTR(mgmt, S_IRUGO | S_IWUSR, scst_devt_pass_through_mgmt_show, scst_devt_pass_through_mgmt_store); -int scst_create_devt_sysfs(struct scst_dev_type *devt) +int scst_devt_sysfs_create(struct scst_dev_type *devt) { int retval; struct kobject *parent; @@ -3786,8 +3623,6 @@ int scst_create_devt_sysfs(struct scst_dev_type *devt) 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) { @@ -3795,11 +3630,6 @@ int scst_create_devt_sysfs(struct scst_dev_type *devt) 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 (devt->add_device != NULL) { retval = sysfs_create_file(&devt->devt_kobj, &scst_devt_mgmt.attr); @@ -3810,7 +3640,7 @@ int scst_create_devt_sysfs(struct scst_dev_type *devt) if (retval != 0) { PRINT_ERROR("Can't add mgmt attr for dev handler %s", devt->name); - goto out; + goto out_err; } pattr = devt->devt_attrs; @@ -3821,7 +3651,7 @@ int scst_create_devt_sysfs(struct scst_dev_type *devt) PRINT_ERROR("Can't add devt attr %s for dev " "handler %s", (*pattr)->name, devt->name); - goto out; + goto out_err; } pattr++; } @@ -3834,7 +3664,7 @@ int scst_create_devt_sysfs(struct scst_dev_type *devt) if (retval != 0) { PRINT_ERROR("Can't add devt trace_flag for dev " "handler %s", devt->name); - goto out; + goto out_err; } } #endif @@ -3842,33 +3672,39 @@ int scst_create_devt_sysfs(struct scst_dev_type *devt) out: TRACE_EXIT_RES(retval); return retval; + +out_err: + scst_devt_sysfs_del(devt); + goto out; } -void scst_devt_sysfs_put(struct scst_dev_type *devt) +void scst_devt_sysfs_del(struct scst_dev_type *devt) { + int rc; + TRACE_ENTRY(); - if (devt->devt_kobj_initialized) { - int rc; + kobject_del(&devt->devt_kobj); + kobject_put(&devt->devt_kobj); - 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); + rc = wait_for_completion_timeout(&devt->devt_kobj_release_compl, HZ); + if (rc == 0) { + PRINT_INFO("Waiting for releasing of sysfs entry " + "for dev handler template %s (%d refs)...", devt->name, + atomic_read(&devt->devt_kobj.kref.refcount)); + wait_for_completion(&devt->devt_kobj_release_compl); + PRINT_INFO("Done waiting for releasing sysfs entry " + "for dev handler template %s", devt->name); + } TRACE_EXIT(); return; } +/* + * Sysfs user info + */ + static DEFINE_MUTEX(scst_sysfs_user_info_mutex); /* All protected by scst_sysfs_user_info_mutex */ diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index cd47f9ba1..3ac25c398 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -6286,15 +6286,14 @@ 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; - failed: mutex_unlock(&scst_mutex); + /* Let's always create sysfs to simplify code */ + rc = scst_sess_sysfs_create(sess); + if (res == 0) + res = rc; + if (sess->init_result_fn) { TRACE_DBG("Calling init_result_fn(%p)", sess); sess->init_result_fn(sess, sess->reg_sess_data, res);