Add implicit ALUA support (merged r3446, r3451, r3457 and r3513 from the trunk).

git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/2.1.0.x@3634 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Bart Van Assche
2011-06-27 16:16:03 +00:00
parent d0cce4dd52
commit 2d400c4102
26 changed files with 2103 additions and 1 deletions

View File

@@ -288,7 +288,8 @@ scst_proc="scst/src/scst_proc.c"
scst_10_sgv="scst/include/scst_sgv.h scst/src/scst_mem.h scst/src/scst_mem.c doc/sgv_cache.sgml"
scst_user="scst/include/scst_user.h scst/src/dev_handlers/scst_user.c"
scst_13_vdisk="scst/src/dev_handlers/scst_vdisk.c"
separate_patches="scst_03_public_headers scst_04_main scst_05_targ scst_06_lib scst_07_pres scst_08_sysfs scst_09_debug scst_10_sgv scst_user scst_13_vdisk"
scst_14_tg="scst/src/scst_tg.c"
separate_patches="scst_03_public_headers scst_04_main scst_05_targ scst_06_lib scst_07_pres scst_08_sysfs scst_09_debug scst_10_sgv scst_user scst_13_vdisk scst_14_tg"
if [ "${generating_upstream_patch}" = "false" ]; then
separate_patches+=" scst_proc"

View File

@@ -1101,6 +1101,265 @@ persistent reservations from this device are released, upon reconnect
the initiators will see it.
Implicit ALUA Support
---------------------
SCST supports implicit asymmetric logical unit access (ALUA). Implicit ALUA is
a feature defined by the ANSI T10 SCSI committee that allows a target to tell
the initiator which path to use in a multipath setup. The redundant paths
between initiator and target can be used either for redundancy or for load
sharing purposes. The target can either be a single target system running SCST
with multiple communication interfaces or two target systems each running SCST
and configured in a high availability setup.
In the SPC-4 standard the following concepts are defined related to ALUA:
* Relative target port ID. A number between 1 and 65535 that uniquely
identifies a target port. These numbers must be unique over the target as
a whole, even if that target consists of multiple systems each running SCST.
* Target port group asymmetric access state. One of active/optimized,
active/non-optimized, standby, unavailable, logical block dependent or
offline. The access state of a port defines which (if any) SCSI commands
will be processed by the target port.
* Target port preference indicator. This indicator is additional information
next to the asymmetric access state that is provided by the target to an
initiator and that may impact the decision taken by the initiator about
which path that will be choosen.
More detailed information about ALUA can be found in section 5.11.2 of the
ANSI T10 standard called SPC-4.
ALUA support in SCST
....................
SCST allows to define implicit ALUA settings for each unique combination of
SCST device and SCST target. An initiator however queries ALUA settings by
sending an appropriate SCSI command to a specific LUN of an SCST target. Each
such LUN maps uniquely to an SCST device. For hardware SCST target drivers,
e.g. ib_srpt, there is a one-to-one correspondence between SCST target and
SCSI target port. With other SCST targets, e.g. iSCSI-SCST, by default the
only relationship between SCST targets and SCSI target ports is that all SCST
targets defined on a system are visible via all SCSI target ports. See also
the iSCSI-SCST documentation about the allowed_portal attribute for
information about how to associate iSCSI targets with a single physical
interface.
Notes:
- In a H.A. setup it is the responsibility of the user to synchronize ALUA
information between the individual systems running SCST. There are no
provisions in SCST to exchange ALUA information automatically between
individual systems.
- In order to support H.A. setups it is possible to let one SCST system
report information about target ports present in other SCST systems.
- With SCST, and certainly in a H.A. setup, it is possible to configure ALUA
such that an initiator receives information that is not standard compliant,
e.g. setting all target ports in the offline state. It is the responsibility
of the user to make sure that the information queried by an initiator is
consistent independent of the LUN and the target port used by the initiator
to query this information.
Configuring ALUA in SCST
........................
SCST allows to configure the following settings related to implicit ALUA
for each unique combination of SCST target and virtual SCST device
(vdisk_fileio, vdisk_blockio, vcdrom, ...):
* The target port group asymmetric access state. SCST supports all ALUA port
states except logical block dependent.
* The preference indicator for a target port group.
* The relative target port ID associated with the SCST target.
It is possible to configure the following ALUA-related information via the
sysfs interface of SCST:
* Device groups, where each device group has a name and contains zero or more
SCST devices. If a device group contains only a single SCST device, the name
of the group may be identical to the device name. See also
/sys/kernel/scst_tgt/device_groups/mgmt.
* Which devices are inside a device group. See also
/sys/kernel/scst_tgt/device_groups/<device group name>/devices/mgmt.
* Target groups, where each target group has a name and contains zero or more
SCST target names. See also
/sys/kernel/scst_tgt/device_groups/<device group name>/target_groups/mgmt.
* Target port group identifier. This is a number in the range 0..65535 and is
called the TARGET PORT GROUP in SPC-4. See also
/sys/kernel/scst_tgt/device_groups/<device group name>/target_groups/<target
group name>/group_id.
* Target port group preference indicator. This is a boolean value called the
PREF bit in SPC-4. See also /sys/kernel/scst_tgt/device_groups/<device group
name>/target_groups/<target group name>/preferred.
* Target port group state name. One of active, nonoptimized, standby,
unavailable, offline or transitioning. See also
/sys/kernel/scst_tgt/device_groups/<device group name>/target_groups/<target
group name>/state.
* Target group contents - zero or more target names. The target names either
exist on the local system or on a remote system in a H.A. setup. For target
names that refer to SCST targets on another system only the relative target
port identifier matters, not the assigned name. See also
/sys/kernel/scst_tgt/device_groups/<device group name>/target_groups/<target
group name>/mgmt.
* Relative target identifier. See also
/sys/kernel/scst_tgt/device_groups/<device group name>/target_groups/<target
group name>/<target name>/rel_tgt_id.
The steps involved in configuring ALUA are:
* Identify the SCST devices that will always share the same ALUA settings and
state. Assign a name to each such group of SCST devices. If a device group
only contains a single device, the group name may be identical to the device
name.
* Configure that device group in SCST via sysfs.
* Identify the SCSI target ports that will always share the same ALUA settings
and state. Assign a name, a group ID and preference indicator to each such
SCSI target port group.
* Configure the target port group information in SCST via sysfs.
* Identify all SCST targets that can be accessed via a target port group.
* Assign all these SCST target names to the target group via sysfs.
* Assign a relative target port identifier to each target.
As an example, in a H.A. setup with two systems each having one InfiniBand
HCA controlled by the ib_srpt driver and where each system exports two LUNs
could be configured as follows:
own_tgt_id=1
other_tgt_id=2
cd /sys/kernel/scst_tgt/device_groups
echo del dgroup1 >mgmt
echo del dgroup2 >mgmt
echo add dgroup1 >mgmt
echo add disk01 >dgroup1/devices/mgmt
echo add tgroup1 >dgroup1/target_groups/mgmt
echo ${own_tgt_id} >dgroup1/target_groups/tgroup1/group_id
echo add ib_srpt_0 >dgroup1/target_groups/tgroup1/mgmt
echo ${own_tgt_id} >dgroup1/target_groups/tgroup1/ib_srpt_0/rel_tgt_id
if [ ${own_tgt_id} = 1 ]; then
echo 1 >dgroup1/target_groups/tgroup1/preferred
fi
echo add tgroup2 >dgroup1/target_groups/mgmt
echo ${other_tgt_id} >dgroup1/target_groups/tgroup2/group_id
echo add ib_srpt_0-other >dgroup1/target_groups/tgroup2/mgmt
echo ${other_tgt_id} >dgroup1/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id
if [ ${other_tgt_id} = 1 ]; then
echo 1 >dgroup1/target_groups/tgroup1/preferred
fi
echo add dgroup2 >mgmt
echo add disk02 >dgroup2/devices/mgmt
echo add tgroup1 >dgroup2/target_groups/mgmt
echo ${own_tgt_id} >dgroup2/target_groups/tgroup1/group_id
echo add ib_srpt_0 >dgroup2/target_groups/tgroup1/mgmt
echo ${own_tgt_id} >dgroup2/target_groups/tgroup1/ib_srpt_0/rel_tgt_id
if [ ${own_tgt_id} = 2 ]; then
echo 1 >dgroup2/target_groups/tgroup1/preferred
fi
echo add tgroup2 >dgroup2/target_groups/mgmt
echo ${other_tgt_id} >dgroup2/target_groups/tgroup2/group_id
echo add ib_srpt_0-other >dgroup2/target_groups/tgroup2/mgmt
echo ${other_tgt_id} >dgroup2/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id
if [ ${other_tgt_id} = 2 ]; then
echo 1 >dgroup2/target_groups/tgroup1/preferred
fi
The second system in the same H.A. setup can be configured with the same
commands but with the values of ${own_rel_tgt_id} and ${other_rel_tgt_id}
swapped.
The result of the above commands is:
$ find -type f | grep -v '/mgmt$' | cut -c3- | sort | \
while read f; do echo $f = $(head -n 1 $f); done
dgroup1/target_groups/tgroup1/group_id = 1
dgroup1/target_groups/tgroup1/ib_srpt_0/rel_tgt_id = 1
dgroup1/target_groups/tgroup1/preferred = 1
dgroup1/target_groups/tgroup1/state = active
dgroup1/target_groups/tgroup2/group_id = 2
dgroup1/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id = 2
dgroup1/target_groups/tgroup2/preferred = 0
dgroup1/target_groups/tgroup2/state = active
dgroup2/target_groups/tgroup1/group_id = 1
dgroup2/target_groups/tgroup1/ib_srpt_0/rel_tgt_id = 1
dgroup2/target_groups/tgroup1/preferred = 1
dgroup2/target_groups/tgroup1/state = active
dgroup2/target_groups/tgroup2/group_id = 2
dgroup2/target_groups/tgroup2/ib_srpt_0-other/rel_tgt_id = 2
dgroup2/target_groups/tgroup2/preferred = 0
dgroup2/target_groups/tgroup2/state = active
Checking the Target Configuration
.................................
One way to verify the implicit ALUA configuration from a Linux initiator is
via the commands provided in the sg3_utils package. The first step is to
verify whether for a certain LUN implicit ALUA has been configured on the
target. This is possible by checking whether the TPGS=1 text appears in the
sg_inq output, where /dev/sdb is a device node created by the ib_srp initiator:
# sg_inq /dev/sdb
standard INQUIRY:
PQual=0 Device_type=0 RMB=0 version=0x05 [SPC-3]
[AERC=0] [TrmTsk=0] NormACA=0 HiSUP=1 Resp_data_format=2
SCCS=0 ACC=0 TPGS=1 3PC=0 Protect=0 BQue=0
EncServ=0 MultiP=0 [MChngr=0] [ACKREQQ=0] Addr16=1
[RelAdr=0] WBus16=0 Sync=0 Linked=0 [TranDis=0] CmdQue=1
[SPI: Clocking=0x0 QAS=0 IUS=0]
length=66 (0x42) Peripheral device type: disk
Vendor identification: SCST_FIO
Product identification: disk01
Product revision level: 300
Unit serial number: 27cddc71
The next step is to verify the target group configuration. That is possible
by verifying whether the output of the sg_rtpg command matches the values
configured on the target:
# sg_rtpg /dev/sdb
Report target port groups:
target port group id : 0x1 , Pref=1
target port group asymmetric access state : 0x00
T_SUP : 0, O_SUP : 0, LBD_SUP : 0, U_SUP : 1, S_SUP : 1, AN_SUP : 1, AO_SUP : 1
status code : 0x02
vendor unique status : 0x00
target port count : 01
Relative target port ids:
0x01
target port group id : 0x2 , Pref=0
target port group asymmetric access state : 0x00
T_SUP : 0, O_SUP : 0, LBD_SUP : 0, U_SUP : 1, S_SUP : 1, AN_SUP : 1, AO_SUP : 1
status code : 0x02
vendor unique status : 0x00
target port count : 01
Relative target port ids:
0x02
Initiator Support
.................
On Linux systems implicit ALUA support is provided by the scsi_dh_alua driver
of the device mapper. You will have to modify at least the following in
/etc/multipath.conf:
* path_checker scsi_dh_alua
* prio_callout "/sbin/mpath_prio_alua /dev/%n"
If your distribution does not provide a /sbin/mpath_prio_alua script, you can
use the following implementation:
$ cat /sbin/mpath_prio_alua
#!/bin/bash
# Given a SCSI device node, query the target port group asymmetric access
# state and report it in numeric form.
tpg_id="$(sg_vpd --page=di "$1" | sed -n 's/.*Target port group: //p')"
aas="$(sg_rtpg "$1" \
| grep -A1 "target port group id : $tpg_id" \
| tail -n 1 \
| sed 's/.*target port group asymmetric access state : //')"
echo $((aas))
More information about how to configure the device mapper and the scsi_dh_alua
driver can be found in the manual of your Linux distribution.
Windows initiator systems support ALUA from Windows Server 2008 on. For more
information, see also:
* Microsoft, Multipathing Support in Windows Server 2008, MSDN
(http://blogs.msdn.com/b/san/archive/2008/07/27/multipathing-support-in-windows-server-2008.aspx).
* Microsoft, ALUA MPIO Logo Test, MSDN
(http://msdn.microsoft.com/en-us/library/gg607458%28v=vs.85%29.aspx).
Caching
-------

View File

@@ -5,6 +5,7 @@
* Copyright (C) 2004 - 2005 Leonid Stoljar
* Copyright (C) 2007 - 2010 ID7 Ltd.
* Copyright (C) 2010 - 2011 SCST Ltd.
* Copyright (C) 2010 - 2011 Bart Van Assche <bvanassche@acm.org>.
*
* Main SCSI target mid-level include file.
*
@@ -2589,6 +2590,65 @@ struct scst_acn {
struct kobj_attribute *acn_attr;
};
/**
* struct scst_dev_group - A group of SCST devices (struct scst_device).
*
* Each device is member of zero or one device groups. With each device group
* there are zero or more target groups associated.
*/
struct scst_dev_group {
char *name;
struct list_head entry;
struct list_head dev_list;
struct list_head tg_list;
struct kobject kobj;
struct kobject *dev_kobj;
struct kobject *tg_kobj;
};
/**
* struct scst_dg_dev - A node in scst_dev_group.dev_list.
*/
struct scst_dg_dev {
struct list_head entry;
struct scst_device *dev;
};
/**
* struct scst_target_group - A group of SCSI targets (struct scst_tgt).
*
* Such a group is either a primary target port group or a secondary
* port group. See also SPC-4 for more information.
*/
struct scst_target_group {
struct scst_dev_group *dg;
char *name;
uint16_t group_id;
enum scst_tg_state state;
bool preferred;
struct list_head entry;
struct list_head tgt_list;
struct kobject kobj;
};
/**
* struct scst_tg_tgt - A node in scst_target_group.tgt_list.
*
* Such a node can either represent a local storage target (struct scst_tgt)
* or a storage target on another system running SCST. In the former case tgt
* != NULL and rel_tgt_id is ignored. In the latter case tgt == NULL and
* rel_tgt_id is relevant.
*/
struct scst_tg_tgt {
struct list_head entry;
struct scst_target_group *tg;
struct kobject kobj;
struct scst_tgt *tgt;
char *name;
uint16_t rel_tgt_id;
};
/*
* Used to store per-session UNIT ATTENTIONs
*/
@@ -2869,6 +2929,11 @@ static inline void scst_sess_set_tgt_priv(struct scst_session *sess,
sess->tgt_priv = val;
}
uint16_t scst_lookup_tg_id(struct scst_device *dev, struct scst_tgt *tgt);
bool scst_impl_alua_configured(struct scst_device *dev);
int scst_tg_get_group_info(void **buf, uint32_t *response_length,
struct scst_device *dev, uint8_t data_format);
/**
* Returns TRUE if cmd is being executed in atomic context.
*
@@ -4113,6 +4178,9 @@ struct scst_sysfs_work_item {
struct scst_tgt *tgt_r;
unsigned long rel_tgt_id;
};
struct {
struct kobject *kobj;
};
};
int work_res;
char *res_buf;

View File

@@ -266,6 +266,7 @@ enum scst_cdb_flags {
#define scst_sense_reservation_preempted UNIT_ATTENTION, 0x2A, 0x03
#define scst_sense_reservation_released UNIT_ATTENTION, 0x2A, 0x04
#define scst_sense_registrations_preempted UNIT_ATTENTION, 0x2A, 0x05
#define scst_sense_asym_access_state_changed UNIT_ATTENTION, 0x2A, 0x06
#define scst_sense_reported_luns_data_changed UNIT_ATTENTION, 0x3F, 0xE
#define scst_sense_inquery_data_changed UNIT_ATTENTION, 0x3F, 0x3
@@ -368,6 +369,14 @@ enum scst_cdb_flags {
#define SCST_INQ_NORMACA_BIT 0x20
/*************************************************************
** TPGS field in byte 5 of the INQUIRY response (SPC-4).
*************************************************************/
enum {
SCST_INQ_TPGS_MODE_IMPLICIT = 0x10,
SCST_INQ_TPGS_MODE_EXPLICIT = 0x20,
};
/*************************************************************
** Byte 2 in RESERVE_10 CDB
*************************************************************/
@@ -402,6 +411,45 @@ enum scst_cdb_flags {
#define SCSI_TRANSPORTID_PROTOCOLID_ISCSI 5
#define SCSI_TRANSPORTID_PROTOCOLID_SAS 6
/**
* enum scst_tg_state - SCSI target port group asymmetric access state.
*
* See also the documentation of the REPORT TARGET PORT GROUPS command in SPC-4.
*/
enum scst_tg_state {
SCST_TG_STATE_OPTIMIZED = 0x0,
SCST_TG_STATE_NONOPTIMIZED = 0x1,
SCST_TG_STATE_STANDBY = 0x2,
SCST_TG_STATE_UNAVAILABLE = 0x3,
SCST_TG_STATE_LBA_DEPENDENT = 0x4,
SCST_TG_STATE_OFFLINE = 0xe,
SCST_TG_STATE_TRANSITIONING = 0xf,
};
/**
* Target port group preferred bit.
*
* See also the documentation of the REPORT TARGET PORT GROUPS command in SPC-4.
*/
enum {
SCST_TG_PREFERRED = 0x80,
};
/**
* enum scst_tg_sup - Supported SCSI target port group states.
*
* See also the documentation of the REPORT TARGET PORT GROUPS command in SPC-4.
*/
enum scst_tg_sup {
SCST_TG_SUP_OPTIMIZED = 0x01,
SCST_TG_SUP_NONOPTIMIZED = 0x02,
SCST_TG_SUP_STANDBY = 0x04,
SCST_TG_SUP_UNAVAILABLE = 0x08,
SCST_TG_SUP_LBA_DEPENDENT = 0x10,
SCST_TG_SUP_OFFLINE = 0x40,
SCST_TG_SUP_TRANSITION = 0x80,
};
/*************************************************************
** Misc SCSI constants
*************************************************************/

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_proc.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ iscsi-scst/ qla2xxx-target/ srpt/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \

View File

@@ -6,6 +6,7 @@ scst-y += scst_targ.o
scst-y += scst_lib.o
scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_tg.o
scst-y += scst_debug.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/ fcst/ iscsi-scst/ qla2xxx-target/ \

View File

@@ -45,6 +45,7 @@ scst-y += scst_sysfs.o
scst-y += scst_mem.o
scst-y += scst_debug.o
scst-y += scst_pres.o
scst-y += scst_tg.o
obj-$(CONFIG_SCST) += scst.o dev_handlers/
obj-$(BUILD_DEV) += $(DEV_HANDLERS_DIR)/

View File

@@ -211,6 +211,7 @@ static void vdisk_exec_verify(struct scst_cmd *cmd,
struct scst_vdisk_thr *thr, loff_t loff);
static void vdisk_exec_read_capacity(struct scst_cmd *cmd);
static void vdisk_exec_read_capacity16(struct scst_cmd *cmd);
static void vdisk_exec_report_tpgs(struct scst_cmd *cmd);
static void vdisk_exec_inquiry(struct scst_cmd *cmd);
static void vdisk_exec_request_sense(struct scst_cmd *cmd);
static void vdisk_exec_mode_sense(struct scst_cmd *cmd);
@@ -1137,6 +1138,15 @@ static int vdisk_do_job(struct scst_cmd *cmd)
case UNMAP:
vdisk_exec_unmap(cmd, thr);
break;
case MAINTENANCE_IN:
switch (cmd->cdb[1] & 0x1f) {
case MI_REPORT_TARGET_PGS:
vdisk_exec_report_tpgs(cmd);
break;
default:
goto out_invalid_opcode;
}
break;
case REPORT_LUNS:
default:
goto out_invalid_opcode;
@@ -1376,6 +1386,7 @@ static void vdisk_exec_inquiry(struct scst_cmd *cmd)
uint8_t *address;
uint8_t *buf;
struct scst_vdisk_dev *virt_dev = cmd->dev->dh_priv;
uint16_t tg_id;
TRACE_ENTRY();
@@ -1476,6 +1487,22 @@ static void vdisk_exec_inquiry(struct scst_cmd *cmd)
num += 4;
tg_id = scst_lookup_tg_id(cmd->dev, cmd->tgt);
if (tg_id) {
/*
* Target port group designator
*/
buf[num + 0] = 0x01; /* binary */
/* Target port group id */
buf[num + 1] = 0x10 | 0x05;
put_unaligned(cpu_to_be16(tg_id),
(__be16 *)&buf[num + 4 + 2]);
buf[num + 3] = 4;
num += 4 + buf[num + 3];
}
/*
* IEEE id
*/
@@ -1574,6 +1601,8 @@ static void vdisk_exec_inquiry(struct scst_cmd *cmd)
if (cmd->tgtt->fake_aca)
buf[3] |= 0x20;
buf[4] = 31;/* n - 4 = 35 - 4 = 31 for full 36 byte data */
if (scst_impl_alua_configured(cmd->dev))
buf[5] = SCST_INQ_TPGS_MODE_IMPLICIT;
buf[6] = 0x10; /* MultiP 1 */
buf[7] = 2; /* CMDQUE 1, BQue 0 => commands queuing supported */
@@ -2336,6 +2365,61 @@ out:
return;
}
/* SPC-4 REPORT TARGET PORT GROUPS command */
static void vdisk_exec_report_tpgs(struct scst_cmd *cmd)
{
struct scst_device *dev;
uint8_t *address;
void *buf;
int32_t buf_len;
uint32_t allocation_length, data_length, length;
uint8_t data_format;
int res;
TRACE_ENTRY();
buf_len = scst_get_buf_full(cmd, &address);
if (buf_len < 0) {
PRINT_ERROR("scst_get_buf_full() failed: %d", buf_len);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_hardw_error));
goto out;
}
if (cmd->cdb_len < 12)
PRINT_WARNING("received invalid REPORT TARGET PORT GROUPS "
"command - length %d is too small (should be at "
"least 12 bytes)", cmd->cdb_len);
dev = cmd->dev;
data_format = cmd->cdb_len > 1 ? cmd->cdb[1] >> 5 : 0;
allocation_length = cmd->cdb_len >= 10 ?
be32_to_cpu(get_unaligned((__be32 *)(cmd->cdb + 6))) : 1024;
res = scst_tg_get_group_info(&buf, &data_length, dev, data_format);
if (res == -ENOMEM) {
scst_set_busy(cmd);
goto out_put;
} else if (res < 0) {
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_field_in_cdb));
goto out_put;
}
length = min_t(uint32_t, min(allocation_length, data_length), buf_len);
memcpy(address, buf, length);
kfree(buf);
if (length < cmd->resp_data_len)
scst_set_resp_data_len(cmd, length);
out_put:
scst_put_buf_full(cmd, address);
out:
TRACE_EXIT();
return;
}
static void vdisk_exec_read_toc(struct scst_cmd *cmd)
{
int32_t length, off = 0;

View File

@@ -645,6 +645,8 @@ again:
del_timer_sync(&tgt->retry_timer);
scst_tg_tgt_remove_by_tgt(tgt);
#ifdef CONFIG_SCST_PROC
scst_cleanup_proc_target_entries(tgt);
#else
@@ -1006,6 +1008,8 @@ static void scst_unregister_device(struct scsi_device *scsidp)
list_del(&dev->dev_list_entry);
scst_dg_dev_remove_by_dev(dev);
scst_assign_dev_handler(dev, &scst_null_devtype);
list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list,
@@ -1254,6 +1258,8 @@ void scst_unregister_virtual_device(int id)
scst_pr_clear_dev(dev);
scst_dg_dev_remove_by_dev(dev);
scst_assign_dev_handler(dev, &scst_null_devtype);
list_for_each_entry_safe(acg_dev, aa, &dev->dev_acg_dev_list,
@@ -2277,6 +2283,8 @@ static int __init init_scst(void)
if (res != 0)
goto out_destroy_aen_mempool;
scst_tg_init();
if (scst_max_cmd_mem == 0) {
struct sysinfo si;
si_meminfo(&si);
@@ -2366,6 +2374,7 @@ out_free_acg:
out_destroy_sgv_pool:
scst_sgv_pools_deinit();
scst_tg_cleanup();
out_sysfs_cleanup:
scst_sysfs_cleanup();
@@ -2441,6 +2450,8 @@ static void __exit exit_scst(void)
scst_sgv_pools_deinit();
scst_tg_cleanup();
scst_sysfs_cleanup();
#define DEINIT_CACHEP(p) do { \

View File

@@ -409,6 +409,70 @@ void scst_done_cmd_mgmt(struct scst_cmd *cmd);
static inline void scst_devt_cleanup(struct scst_dev_type *devt) { }
void scst_tg_init(void);
void scst_tg_cleanup(void);
int scst_dg_add(struct kobject *parent, const char *name);
int scst_dg_remove(const char *name);
struct scst_dev_group *scst_lookup_dg_by_kobj(struct kobject *kobj);
int scst_dg_dev_add(struct scst_dev_group *dg, const char *name);
int scst_dg_dev_remove_by_name(struct scst_dev_group *dg, const char *name);
int scst_dg_dev_remove_by_dev(struct scst_device *dev);
int scst_tg_add(struct scst_dev_group *dg, const char *name);
int scst_tg_remove_by_name(struct scst_dev_group *dg, const char *name);
int scst_tg_set_state(struct scst_target_group *tg, enum scst_tg_state state);
int scst_tg_tgt_add(struct scst_target_group *tg, const char *name);
int scst_tg_tgt_remove_by_name(struct scst_target_group *tg, const char *name);
void scst_tg_tgt_remove_by_tgt(struct scst_tgt *tgt);
#ifndef CONFIG_SCST_PROC
int scst_dg_sysfs_add(struct kobject *parent, struct scst_dev_group *dg);
void scst_dg_sysfs_del(struct scst_dev_group *dg);
int scst_dg_dev_sysfs_add(struct scst_dev_group *dg, struct scst_dg_dev *dgdev);
void scst_dg_dev_sysfs_del(struct scst_dev_group *dg,
struct scst_dg_dev *dgdev);
int scst_tg_sysfs_add(struct scst_dev_group *dg,
struct scst_target_group *tg);
void scst_tg_sysfs_del(struct scst_target_group *tg);
int scst_tg_tgt_sysfs_add(struct scst_target_group *tg,
struct scst_tg_tgt *tg_tgt);
void scst_tg_tgt_sysfs_del(struct scst_target_group *tg,
struct scst_tg_tgt *tg_tgt);
#else
static inline int scst_dg_sysfs_add(struct kobject *parent,
struct scst_dev_group *dg)
{
return 0;
}
static inline void scst_dg_sysfs_del(struct scst_dev_group *dg)
{
}
static inline int scst_dg_dev_sysfs_add(struct scst_dev_group *dg,
struct scst_dg_dev *dgdev)
{
return 0;
}
static inline void scst_dg_dev_sysfs_del(struct scst_dev_group *dg,
struct scst_dg_dev *dgdev)
{
}
static inline int scst_tg_sysfs_add(struct scst_dev_group *dg,
struct scst_target_group *tg)
{
return 0;
}
static inline void scst_tg_sysfs_del(struct scst_target_group *tg)
{
}
static inline int scst_tg_tgt_sysfs_add(struct scst_target_group *tg,
struct scst_tg_tgt *tg_tgt)
{
return 0;
}
static inline void scst_tg_tgt_sysfs_del(struct scst_target_group *tg,
struct scst_tg_tgt *tg_tgt)
{
}
#endif
#ifdef CONFIG_SCST_PROC
int scst_proc_init_module(void);

View File

@@ -39,6 +39,7 @@ static DECLARE_COMPLETION(scst_sysfs_root_release_completion);
static struct kobject *scst_targets_kobj;
static struct kobject *scst_devices_kobj;
static struct kobject *scst_handlers_kobj;
static struct kobject *scst_device_groups_kobj;
static const char *const scst_dev_handler_types[] = {
"Direct-access device (e.g., magnetic disk)",
@@ -4884,6 +4885,698 @@ void scst_devt_sysfs_del(struct scst_dev_type *devt)
return;
}
/**
** SCST sysfs device_groups/<dg>/devices/<dev> implementation.
**/
int scst_dg_dev_sysfs_add(struct scst_dev_group *dg, struct scst_dg_dev *dgdev)
{
int res;
TRACE_ENTRY();
res = sysfs_create_link(dg->dev_kobj, &dgdev->dev->dev_kobj,
dgdev->dev->virt_name);
TRACE_EXIT_RES(res);
return res;
}
void scst_dg_dev_sysfs_del(struct scst_dev_group *dg, struct scst_dg_dev *dgdev)
{
TRACE_ENTRY();
sysfs_remove_link(dg->dev_kobj, dgdev->dev->virt_name);
TRACE_EXIT();
}
/**
** SCST sysfs device_groups/<dg>/devices directory implementation.
**/
static ssize_t scst_dg_devs_mgmt_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
static const char help[] =
"Usage: echo \"add device\" >mgmt\n"
" echo \"del device\" >mgmt\n";
return scnprintf(buf, PAGE_SIZE, help);
}
static int scst_dg_devs_mgmt_store_work_fn(struct scst_sysfs_work_item *w)
{
struct scst_dev_group *dg;
char *cmd, *p, *pp, *dev_name;
int res;
TRACE_ENTRY();
cmd = w->buf;
dg = scst_lookup_dg_by_kobj(w->kobj);
WARN_ON(!dg);
p = strchr(cmd, '\n');
if (p)
*p = '\0';
res = -EINVAL;
pp = cmd;
p = scst_get_next_lexem(&pp);
if (strcasecmp(p, "add") == 0) {
dev_name = scst_get_next_lexem(&pp);
if (!*dev_name)
goto out;
res = scst_dg_dev_add(dg, dev_name);
} else if (strcasecmp(p, "del") == 0) {
dev_name = scst_get_next_lexem(&pp);
if (!*dev_name)
goto out;
res = scst_dg_dev_remove_by_name(dg, dev_name);
}
out:
kobject_put(w->kobj);
TRACE_EXIT_RES(res);
return res;
}
static ssize_t scst_dg_devs_mgmt_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *cmd;
struct scst_sysfs_work_item *work;
int res;
TRACE_ENTRY();
res = -ENOMEM;
cmd = kasprintf(GFP_KERNEL, "%.*s", (int)count, buf);
if (!cmd)
goto out;
res = scst_alloc_sysfs_work(scst_dg_devs_mgmt_store_work_fn, false,
&work);
if (res)
goto out;
work->buf = cmd;
work->kobj = kobj;
kobject_get(kobj);
res = scst_sysfs_queue_wait_work(work);
out:
if (res == 0)
res = count;
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute scst_dg_devs_mgmt =
__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_dg_devs_mgmt_show,
scst_dg_devs_mgmt_store);
static const struct attribute *scst_dg_devs_attrs[] = {
&scst_dg_devs_mgmt.attr,
NULL,
};
/**
** SCST sysfs device_groups/<dg>/target_groups/<tg>/<tgt> implementation.
**/
static ssize_t scst_tg_tgt_rel_tgt_id_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct scst_tg_tgt *tg_tgt;
tg_tgt = container_of(kobj, struct scst_tg_tgt, kobj);
return scnprintf(buf, PAGE_SIZE, "%u\n" SCST_SYSFS_KEY_MARK "\n",
tg_tgt->rel_tgt_id);
}
static ssize_t scst_tg_tgt_rel_tgt_id_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct scst_tg_tgt *tg_tgt;
unsigned long rel_tgt_id;
char ch[8];
int res;
TRACE_ENTRY();
tg_tgt = container_of(kobj, struct scst_tg_tgt, kobj);
snprintf(ch, sizeof(ch), "%.*s", min_t(int, count, sizeof(ch)-1), buf);
res = strict_strtoul(ch, 0, &rel_tgt_id);
if (res)
goto out;
res = -EINVAL;
if (rel_tgt_id == 0 || rel_tgt_id > 0xffff)
goto out;
tg_tgt->rel_tgt_id = rel_tgt_id;
res = count;
out:
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute scst_tg_tgt_rel_tgt_id =
__ATTR(rel_tgt_id, S_IRUGO | S_IWUSR, scst_tg_tgt_rel_tgt_id_show,
scst_tg_tgt_rel_tgt_id_store);
static const struct attribute *scst_tg_tgt_attrs[] = {
&scst_tg_tgt_rel_tgt_id.attr,
NULL,
};
int scst_tg_tgt_sysfs_add(struct scst_target_group *tg,
struct scst_tg_tgt *tg_tgt)
{
int res;
TRACE_ENTRY();
BUG_ON(!tg);
BUG_ON(!tg_tgt);
BUG_ON(!tg_tgt->name);
if (tg_tgt->tgt)
res = sysfs_create_link(&tg->kobj, &tg_tgt->tgt->tgt_kobj,
tg_tgt->name);
else {
res = kobject_add(&tg_tgt->kobj, &tg->kobj, "%s", tg_tgt->name);
if (res)
goto err;
res = sysfs_create_files(&tg_tgt->kobj, scst_tg_tgt_attrs);
if (res)
goto err;
}
out:
TRACE_EXIT_RES(res);
return res;
err:
scst_tg_tgt_sysfs_del(tg, tg_tgt);
goto out;
}
void scst_tg_tgt_sysfs_del(struct scst_target_group *tg,
struct scst_tg_tgt *tg_tgt)
{
TRACE_ENTRY();
if (tg_tgt->tgt)
sysfs_remove_link(&tg->kobj, tg_tgt->name);
else {
sysfs_remove_files(&tg_tgt->kobj, scst_tg_tgt_attrs);
kobject_del(&tg_tgt->kobj);
}
TRACE_EXIT();
}
/**
** SCST sysfs device_groups/<dg>/target_groups/<tg> directory implementation.
**/
static ssize_t scst_tg_group_id_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct scst_target_group *tg;
tg = container_of(kobj, struct scst_target_group, kobj);
return scnprintf(buf, PAGE_SIZE, "%u\n" SCST_SYSFS_KEY_MARK "\n",
tg->group_id);
}
static ssize_t scst_tg_group_id_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct scst_target_group *tg;
unsigned long group_id;
char ch[8];
int res;
TRACE_ENTRY();
tg = container_of(kobj, struct scst_target_group, kobj);
snprintf(ch, sizeof(ch), "%.*s", min_t(int, count, sizeof(ch)-1), buf);
res = strict_strtoul(ch, 0, &group_id);
if (res)
goto out;
res = -EINVAL;
if (group_id == 0 || group_id > 0xffff)
goto out;
tg->group_id = group_id;
res = count;
out:
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute scst_tg_group_id =
__ATTR(group_id, S_IRUGO | S_IWUSR, scst_tg_group_id_show,
scst_tg_group_id_store);
static ssize_t scst_tg_preferred_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct scst_target_group *tg;
tg = container_of(kobj, struct scst_target_group, kobj);
return scnprintf(buf, PAGE_SIZE, "%u\n%s",
tg->preferred, SCST_SYSFS_KEY_MARK "\n");
}
static ssize_t scst_tg_preferred_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct scst_target_group *tg;
unsigned long preferred;
char ch[8];
int res;
TRACE_ENTRY();
tg = container_of(kobj, struct scst_target_group, kobj);
snprintf(ch, sizeof(ch), "%.*s", min_t(int, count, sizeof(ch)-1), buf);
res = strict_strtoul(ch, 0, &preferred);
if (res)
goto out;
res = -EINVAL;
if (preferred != 0 && preferred != 1)
goto out;
tg->preferred = preferred;
res = count;
out:
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute scst_tg_preferred =
__ATTR(preferred, S_IRUGO | S_IWUSR, scst_tg_preferred_show,
scst_tg_preferred_store);
static struct { enum scst_tg_state s; const char *n; } scst_tg_state_names[] = {
{ SCST_TG_STATE_OPTIMIZED, "active" },
{ SCST_TG_STATE_NONOPTIMIZED, "nonoptimized" },
{ SCST_TG_STATE_STANDBY, "standby" },
{ SCST_TG_STATE_UNAVAILABLE, "unavailable" },
{ SCST_TG_STATE_OFFLINE, "offline" },
{ SCST_TG_STATE_TRANSITIONING, "transitioning" },
};
static ssize_t scst_tg_state_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
struct scst_target_group *tg;
int i;
tg = container_of(kobj, struct scst_target_group, kobj);
for (i = ARRAY_SIZE(scst_tg_state_names) - 1; i >= 0; i--)
if (scst_tg_state_names[i].s == tg->state)
break;
return scnprintf(buf, PAGE_SIZE, "%s\n" SCST_SYSFS_KEY_MARK "\n",
i >= 0 ? scst_tg_state_names[i].n : "???");
}
static int scst_tg_state_store_work_fn(struct scst_sysfs_work_item *w)
{
struct scst_target_group *tg;
char *cmd, *p;
int i, res;
TRACE_ENTRY();
cmd = w->buf;
tg = container_of(w->kobj, struct scst_target_group, kobj);
p = strchr(cmd, '\n');
if (p)
*p = '\0';
for (i = ARRAY_SIZE(scst_tg_state_names) - 1; i >= 0; i--)
if (strcmp(scst_tg_state_names[i].n, cmd) == 0)
break;
res = -EINVAL;
if (i < 0)
goto out;
res = scst_tg_set_state(tg, scst_tg_state_names[i].s);
out:
kobject_put(w->kobj);
TRACE_EXIT_RES(res);
return res;
}
static ssize_t scst_tg_state_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *cmd;
struct scst_sysfs_work_item *work;
int res;
TRACE_ENTRY();
res = -ENOMEM;
cmd = kasprintf(GFP_KERNEL, "%.*s", (int)count, buf);
if (!cmd)
goto out;
res = scst_alloc_sysfs_work(scst_tg_state_store_work_fn, false,
&work);
if (res)
goto out;
work->buf = cmd;
work->kobj = kobj;
kobject_get(kobj);
res = scst_sysfs_queue_wait_work(work);
out:
if (res == 0)
res = count;
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute scst_tg_state =
__ATTR(state, S_IRUGO | S_IWUSR, scst_tg_state_show,
scst_tg_state_store);
static ssize_t scst_tg_mgmt_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
static const char help[] =
"Usage: echo \"add target\" >mgmt\n"
" echo \"del target\" >mgmt\n";
return scnprintf(buf, PAGE_SIZE, help);
}
static int scst_tg_mgmt_store_work_fn(struct scst_sysfs_work_item *w)
{
struct scst_target_group *tg;
char *cmd, *p, *pp, *target_name;
int res;
TRACE_ENTRY();
cmd = w->buf;
tg = container_of(w->kobj, struct scst_target_group, kobj);
p = strchr(cmd, '\n');
if (p)
*p = '\0';
res = -EINVAL;
pp = cmd;
p = scst_get_next_lexem(&pp);
if (strcasecmp(p, "add") == 0) {
target_name = scst_get_next_lexem(&pp);
if (!*target_name)
goto out;
res = scst_tg_tgt_add(tg, target_name);
} else if (strcasecmp(p, "del") == 0) {
target_name = scst_get_next_lexem(&pp);
if (!*target_name)
goto out;
res = scst_tg_tgt_remove_by_name(tg, target_name);
}
out:
kobject_put(w->kobj);
TRACE_EXIT_RES(res);
return res;
}
static ssize_t scst_tg_mgmt_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *cmd;
struct scst_sysfs_work_item *work;
int res;
TRACE_ENTRY();
res = -ENOMEM;
cmd = kasprintf(GFP_KERNEL, "%.*s", (int)count, buf);
if (!cmd)
goto out;
res = scst_alloc_sysfs_work(scst_tg_mgmt_store_work_fn, false,
&work);
if (res)
goto out;
work->buf = cmd;
work->kobj = kobj;
kobject_get(kobj);
res = scst_sysfs_queue_wait_work(work);
out:
if (res == 0)
res = count;
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute scst_tg_mgmt =
__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_tg_mgmt_show,
scst_tg_mgmt_store);
static const struct attribute *scst_tg_attrs[] = {
&scst_tg_mgmt.attr,
&scst_tg_group_id.attr,
&scst_tg_preferred.attr,
&scst_tg_state.attr,
NULL,
};
int scst_tg_sysfs_add(struct scst_dev_group *dg, struct scst_target_group *tg)
{
int res;
TRACE_ENTRY();
res = kobject_add(&tg->kobj, dg->tg_kobj, "%s", tg->name);
if (res)
goto err;
res = sysfs_create_files(&tg->kobj, scst_tg_attrs);
if (res)
goto err;
out:
TRACE_EXIT_RES(res);
return res;
err:
scst_tg_sysfs_del(tg);
goto out;
}
void scst_tg_sysfs_del(struct scst_target_group *tg)
{
TRACE_ENTRY();
sysfs_remove_files(&tg->kobj, scst_tg_attrs);
kobject_del(&tg->kobj);
TRACE_EXIT();
}
/**
** SCST sysfs device_groups/<dg>/target_groups directory implementation.
**/
static ssize_t scst_dg_tgs_mgmt_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
static const char help[] =
"Usage: echo \"add group_name\" >mgmt\n"
" echo \"del group_name\" >mgmt\n";
return scnprintf(buf, PAGE_SIZE, help);
}
static int scst_dg_tgs_mgmt_store_work_fn(struct scst_sysfs_work_item *w)
{
struct scst_dev_group *dg;
char *cmd, *p, *pp, *dev_name;
int res;
TRACE_ENTRY();
cmd = w->buf;
dg = scst_lookup_dg_by_kobj(w->kobj);
WARN_ON(!dg);
p = strchr(cmd, '\n');
if (p)
*p = '\0';
res = -EINVAL;
pp = cmd;
p = scst_get_next_lexem(&pp);
if (strcasecmp(p, "add") == 0) {
dev_name = scst_get_next_lexem(&pp);
if (!*dev_name)
goto out;
res = scst_tg_add(dg, dev_name);
} else if (strcasecmp(p, "del") == 0) {
dev_name = scst_get_next_lexem(&pp);
if (!*dev_name)
goto out;
res = scst_tg_remove_by_name(dg, dev_name);
}
out:
kobject_put(w->kobj);
TRACE_EXIT_RES(res);
return res;
}
static ssize_t scst_dg_tgs_mgmt_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
char *cmd;
struct scst_sysfs_work_item *work;
int res;
TRACE_ENTRY();
res = -ENOMEM;
cmd = kasprintf(GFP_KERNEL, "%.*s", (int)count, buf);
if (!cmd)
goto out;
res = scst_alloc_sysfs_work(scst_dg_tgs_mgmt_store_work_fn, false,
&work);
if (res)
goto out;
work->buf = cmd;
work->kobj = kobj;
kobject_get(kobj);
res = scst_sysfs_queue_wait_work(work);
out:
if (res == 0)
res = count;
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute scst_dg_tgs_mgmt =
__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_dg_tgs_mgmt_show,
scst_dg_tgs_mgmt_store);
static const struct attribute *scst_dg_tgs_attrs[] = {
&scst_dg_tgs_mgmt.attr,
NULL,
};
/**
** SCST sysfs device_groups directory implementation.
**/
int scst_dg_sysfs_add(struct kobject *parent, struct scst_dev_group *dg)
{
int res;
dg->dev_kobj = NULL;
dg->tg_kobj = NULL;
res = kobject_add(&dg->kobj, parent, "%s", dg->name);
if (res)
goto err;
res = -EEXIST;
dg->dev_kobj = kobject_create_and_add("devices", &dg->kobj);
if (!dg->dev_kobj)
goto err;
res = sysfs_create_files(dg->dev_kobj, scst_dg_devs_attrs);
if (res)
goto err;
dg->tg_kobj = kobject_create_and_add("target_groups", &dg->kobj);
if (!dg->tg_kobj)
goto err;
res = sysfs_create_files(dg->tg_kobj, scst_dg_tgs_attrs);
if (res)
goto err;
out:
return res;
err:
scst_dg_sysfs_del(dg);
goto out;
}
void scst_dg_sysfs_del(struct scst_dev_group *dg)
{
if (dg->tg_kobj) {
sysfs_remove_files(dg->tg_kobj, scst_dg_tgs_attrs);
kobject_del(dg->tg_kobj);
kobject_put(dg->tg_kobj);
dg->tg_kobj = NULL;
}
if (dg->dev_kobj) {
sysfs_remove_files(dg->dev_kobj, scst_dg_devs_attrs);
kobject_del(dg->dev_kobj);
kobject_put(dg->dev_kobj);
dg->dev_kobj = NULL;
}
kobject_del(&dg->kobj);
}
static ssize_t scst_device_groups_mgmt_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
static const char help[] =
"Usage: echo \"add group_name\" >mgmt\n"
" echo \"del group_name\" >mgmt\n";
return scnprintf(buf, PAGE_SIZE, help);
}
static ssize_t scst_device_groups_mgmt_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
int res;
char *p, *pp, *input, *group_name;
TRACE_ENTRY();
input = kasprintf(GFP_KERNEL, "%.*s", (int)count, buf);
pp = input;
p = strchr(input, '\n');
if (p)
*p = '\0';
res = -EINVAL;
p = scst_get_next_lexem(&pp);
if (strcasecmp(p, "add") == 0) {
group_name = scst_get_next_lexem(&pp);
if (!*group_name)
goto out;
res = scst_dg_add(scst_device_groups_kobj, group_name);
} else if (strcasecmp(p, "del") == 0) {
group_name = scst_get_next_lexem(&pp);
if (!*group_name)
goto out;
res = scst_dg_remove(group_name);
}
out:
kfree(input);
if (res == 0)
res = count;
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute scst_device_groups_mgmt =
__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_device_groups_mgmt_show,
scst_device_groups_mgmt_store);
static const struct attribute *scst_device_groups_attrs[] = {
&scst_device_groups_mgmt.attr,
NULL,
};
/**
** SCST sysfs root directory implementation
**/
@@ -5461,10 +6154,27 @@ int __init scst_sysfs_init(void)
if (scst_handlers_kobj == NULL)
goto handlers_kobj_error;
scst_device_groups_kobj = kobject_create_and_add("device_groups",
&scst_sysfs_root_kobj);
if (scst_device_groups_kobj == NULL)
goto device_groups_kobj_error;
if (sysfs_create_files(scst_device_groups_kobj,
scst_device_groups_attrs))
goto device_groups_attrs_error;
out:
TRACE_EXIT_RES(res);
return res;
device_groups_attrs_error:
kobject_del(scst_device_groups_kobj);
kobject_put(scst_device_groups_kobj);
device_groups_kobj_error:
kobject_del(scst_handlers_kobj);
kobject_put(scst_handlers_kobj);
handlers_kobj_error:
scst_del_put_sgv_kobj();
@@ -5507,6 +6217,11 @@ void scst_sysfs_cleanup(void)
kobject_del(scst_handlers_kobj);
kobject_put(scst_handlers_kobj);
sysfs_remove_files(scst_device_groups_kobj, scst_device_groups_attrs);
kobject_del(scst_device_groups_kobj);
kobject_put(scst_device_groups_kobj);
kobject_del(&scst_sysfs_root_kobj);
kobject_put(&scst_sysfs_root_kobj);

835
scst/src/scst_tg.c Normal file
View File

@@ -0,0 +1,835 @@
/*
* scst_tg.c
*
* SCSI target group related code.
*
* Copyright (C) 2011 Bart Van Assche <bvanassche@acm.org>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <asm/unaligned.h>
#ifdef INSIDE_KERNEL_TREE
#include <scst/scst.h>
#else
#include "scst.h"
#endif
#include "scst_priv.h"
static struct list_head scst_dev_group_list;
/* Look up a device by name. */
static struct scst_device *__lookup_dev(const char *name)
{
struct scst_device *dev;
list_for_each_entry(dev, &scst_dev_list, dev_list_entry)
if (strcmp(dev->virt_name, name) == 0)
return dev;
return NULL;
}
/* Look up a target by name. */
static struct scst_tgt *__lookup_tgt(const char *name)
{
struct scst_tgt_template *t;
struct scst_tgt *tgt;
list_for_each_entry(t, &scst_template_list, scst_template_list_entry)
list_for_each_entry(tgt, &t->tgt_list, tgt_list_entry)
if (strcmp(tgt->tgt_name, name) == 0)
return tgt;
return NULL;
}
/* Look up a target by name in the given device group. */
static struct scst_tg_tgt *__lookup_dg_tgt(struct scst_dev_group *dg,
const char *tgt_name)
{
struct scst_target_group *tg;
struct scst_tg_tgt *tg_tgt;
BUG_ON(!dg);
BUG_ON(!tgt_name);
list_for_each_entry(tg, &dg->tg_list, entry)
list_for_each_entry(tg_tgt, &tg->tgt_list, entry)
if (strcmp(tg_tgt->name, tgt_name) == 0)
return tg_tgt;
return NULL;
}
/* Look up a target group by name in the given device group. */
static struct scst_target_group *
__lookup_tg_by_name(struct scst_dev_group *dg, const char *name)
{
struct scst_target_group *tg;
list_for_each_entry(tg, &dg->tg_list, entry)
if (strcmp(tg->name, name) == 0)
return tg;
return NULL;
}
/* Look up a device node by device pointer in the given device group. */
static struct scst_dg_dev *__lookup_dg_dev_by_dev(struct scst_dev_group *dg,
struct scst_device *dev)
{
struct scst_dg_dev *dgd;
list_for_each_entry(dgd, &dg->dev_list, entry)
if (dgd->dev == dev)
return dgd;
return NULL;
}
/* Look up a device node by name in the given device group. */
static struct scst_dg_dev *__lookup_dg_dev_by_name(struct scst_dev_group *dg,
const char *name)
{
struct scst_dg_dev *dgd;
list_for_each_entry(dgd, &dg->dev_list, entry)
if (strcmp(dgd->dev->virt_name, name) == 0)
return dgd;
return NULL;
}
/* Look up a device node by name in any device group. */
static struct scst_dg_dev *__global_lookup_dg_dev_by_name(const char *name)
{
struct scst_dev_group *dg;
struct scst_dg_dev *dgd;
list_for_each_entry(dg, &scst_dev_group_list, entry) {
dgd = __lookup_dg_dev_by_name(dg, name);
if (dgd)
return dgd;
}
return NULL;
}
/* Look up a device group by name. */
static struct scst_dev_group *__lookup_dg_by_name(const char *name)
{
struct scst_dev_group *dg;
list_for_each_entry(dg, &scst_dev_group_list, entry)
if (strcmp(dg->name, name) == 0)
return dg;
return NULL;
}
/* Look up a device group by device pointer. */
static struct scst_dev_group *__lookup_dg_by_dev(struct scst_device *dev)
{
struct scst_dev_group *dg;
list_for_each_entry(dg, &scst_dev_group_list, entry)
if (__lookup_dg_dev_by_dev(dg, dev))
return dg;
return NULL;
}
/*
* Target group contents management.
*/
static void scst_release_tg_tgt(struct kobject *kobj)
{
struct scst_tg_tgt *tg_tgt;
tg_tgt = container_of(kobj, struct scst_tg_tgt, kobj);
kfree(tg_tgt->name);
kfree(tg_tgt);
}
static struct kobj_type scst_tg_tgt_ktype = {
#ifndef CONFIG_SCST_PROC
.sysfs_ops = &scst_sysfs_ops,
#endif
.release = scst_release_tg_tgt,
};
/**
* scst_tg_tgt_add() - Add a target to a target group.
*/
int scst_tg_tgt_add(struct scst_target_group *tg, const char *name)
{
struct scst_tg_tgt *tg_tgt;
struct scst_tgt *tgt;
int res;
TRACE_ENTRY();
BUG_ON(!tg);
BUG_ON(!name);
res = -ENOMEM;
tg_tgt = kzalloc(sizeof *tg_tgt, GFP_KERNEL);
if (!tg_tgt)
goto out;
tg_tgt->tg = tg;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24)
kobject_init(&tg_tgt->kobj, &scst_tg_tgt_ktype);
#else
kobject_init(&tg_tgt->kobj);
tg_tgt->kobj.ktype = &scst_tg_tgt_ktype;
#endif
tg_tgt->name = kstrdup(name, GFP_KERNEL);
if (!tg_tgt->name)
goto out_put;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out_put;
res = -EEXIST;
tgt = __lookup_tgt(name);
if (__lookup_dg_tgt(tg->dg, name))
goto out_unlock;
tg_tgt->tgt = tgt;
res = scst_tg_tgt_sysfs_add(tg, tg_tgt);
if (res)
goto out_unlock;
list_add_tail(&tg_tgt->entry, &tg->tgt_list);
res = 0;
mutex_unlock(&scst_mutex);
out:
TRACE_EXIT_RES(res);
return res;
out_unlock:
mutex_unlock(&scst_mutex);
out_put:
kobject_put(&tg_tgt->kobj);
goto out;
}
static void __scst_tg_tgt_remove(struct scst_target_group *tg,
struct scst_tg_tgt *tg_tgt)
{
TRACE_ENTRY();
list_del(&tg_tgt->entry);
scst_tg_tgt_sysfs_del(tg, tg_tgt);
kobject_put(&tg_tgt->kobj);
TRACE_EXIT();
}
/**
* scst_tg_tgt_remove_by_name() - Remove a target from a target group.
*/
int scst_tg_tgt_remove_by_name(struct scst_target_group *tg, const char *name)
{
struct scst_tg_tgt *tg_tgt;
int res;
TRACE_ENTRY();
BUG_ON(!tg);
BUG_ON(!name);
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out;
res = -EINVAL;
tg_tgt = __lookup_dg_tgt(tg->dg, name);
if (!tg_tgt)
goto out_unlock;
__scst_tg_tgt_remove(tg, tg_tgt);
res = 0;
out_unlock:
mutex_unlock(&scst_mutex);
out:
TRACE_EXIT_RES(res);
return res;
}
/* Caller must hold scst_mutex. Called from the target removal code. */
void scst_tg_tgt_remove_by_tgt(struct scst_tgt *tgt)
{
struct scst_dev_group *dg;
struct scst_target_group *tg;
struct scst_tg_tgt *t, *t2;
BUG_ON(!tgt);
list_for_each_entry(dg, &scst_dev_group_list, entry)
list_for_each_entry(tg, &dg->tg_list, entry)
list_for_each_entry_safe(t, t2, &tg->tgt_list, entry)
if (t->tgt == tgt)
__scst_tg_tgt_remove(tg, t);
}
/*
* Target group management.
*/
static void scst_release_tg(struct kobject *kobj)
{
struct scst_target_group *tg;
tg = container_of(kobj, struct scst_target_group, kobj);
kfree(tg->name);
kfree(tg);
}
static struct kobj_type scst_tg_ktype = {
#ifndef CONFIG_SCST_PROC
.sysfs_ops = &scst_sysfs_ops,
#endif
.release = scst_release_tg,
};
/**
* scst_tg_add() - Add a target group.
*/
int scst_tg_add(struct scst_dev_group *dg, const char *name)
{
struct scst_target_group *tg;
int res;
TRACE_ENTRY();
res = -ENOMEM;
tg = kzalloc(sizeof *tg, GFP_KERNEL);
if (!tg)
goto out;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24)
kobject_init(&tg->kobj, &scst_tg_ktype);
#else
kobject_init(&tg->kobj);
tg->kobj.ktype = &scst_tg_ktype;
#endif
tg->name = kstrdup(name, GFP_KERNEL);
if (!tg->name)
goto out_put;
tg->dg = dg;
tg->state = SCST_TG_STATE_OPTIMIZED;
INIT_LIST_HEAD(&tg->tgt_list);
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out_put;
res = -EEXIST;
if (__lookup_tg_by_name(dg, name))
goto out_unlock;
res = scst_tg_sysfs_add(dg, tg);
if (res)
goto out_unlock;
list_add_tail(&tg->entry, &dg->tg_list);
mutex_unlock(&scst_mutex);
out:
TRACE_EXIT_RES(res);
return res;
out_unlock:
mutex_unlock(&scst_mutex);
out_put:
kobject_put(&tg->kobj);
goto out;
}
static void __scst_tg_remove(struct scst_dev_group *dg,
struct scst_target_group *tg)
{
struct scst_tg_tgt *tg_tgt;
TRACE_ENTRY();
BUG_ON(!dg);
BUG_ON(!tg);
while (!list_empty(&tg->tgt_list)) {
tg_tgt = list_first_entry(&tg->tgt_list, struct scst_tg_tgt,
entry);
__scst_tg_tgt_remove(tg, tg_tgt);
}
list_del(&tg->entry);
scst_tg_sysfs_del(tg);
kobject_put(&tg->kobj);
TRACE_EXIT();
}
/**
* scst_tg_remove_by_name() - Remove a target group.
*/
int scst_tg_remove_by_name(struct scst_dev_group *dg, const char *name)
{
struct scst_target_group *tg;
int res;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out;
res = -EINVAL;
tg = __lookup_tg_by_name(dg, name);
if (!tg)
goto out_unlock;
__scst_tg_remove(dg, tg);
res = 0;
out_unlock:
mutex_unlock(&scst_mutex);
out:
return res;
}
int scst_tg_set_state(struct scst_target_group *tg, enum scst_tg_state state)
{
struct scst_dg_dev *dg_dev;
struct scst_device *dev;
struct scst_tgt_dev *tgt_dev;
int res;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out;
tg->state = state;
list_for_each_entry(dg_dev, &tg->dg->dev_list, entry) {
dev = dg_dev->dev;
list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
dev_tgt_dev_list_entry) {
TRACE_MGMT_DBG("ALUA state of tgt_dev %p has changed",
tgt_dev);
scst_gen_aen_or_ua(tgt_dev,
SCST_LOAD_SENSE(scst_sense_asym_access_state_changed));
}
}
mutex_unlock(&scst_mutex);
out:
return res;
}
/*
* Device group contents manipulation.
*/
/**
* scst_dg_dev_add() - Add a device to a device group.
*
* It is verified whether 'name' refers to an existing device and whether that
* device has not yet been added to any other device group.
*/
int scst_dg_dev_add(struct scst_dev_group *dg, const char *name)
{
struct scst_dg_dev *dgdev;
struct scst_device *dev;
int res;
res = -ENOMEM;
dgdev = kzalloc(sizeof *dgdev, GFP_KERNEL);
if (!dgdev)
goto out;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out_free;
res = -EEXIST;
if (__global_lookup_dg_dev_by_name(name))
goto out_unlock;
res = -EINVAL;
dev = __lookup_dev(name);
if (!dev)
goto out_unlock;
dgdev->dev = dev;
res = scst_dg_dev_sysfs_add(dg, dgdev);
if (res)
goto out_unlock;
list_add_tail(&dgdev->entry, &dg->dev_list);
mutex_unlock(&scst_mutex);
out:
return res;
out_unlock:
mutex_unlock(&scst_mutex);
out_free:
kfree(dgdev);
goto out;
}
static void __scst_dg_dev_remove(struct scst_dev_group *dg,
struct scst_dg_dev *dgdev)
{
list_del(&dgdev->entry);
scst_dg_dev_sysfs_del(dg, dgdev);
kfree(dgdev);
}
/**
* scst_dg_dev_remove_by_name() - Remove a device from a device group.
*/
int scst_dg_dev_remove_by_name(struct scst_dev_group *dg, const char *name)
{
struct scst_dg_dev *dgdev;
int res;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out;
res = -EINVAL;
dgdev = __lookup_dg_dev_by_name(dg, name);
if (!dgdev)
goto out_unlock;
__scst_dg_dev_remove(dg, dgdev);
res = 0;
out_unlock:
mutex_unlock(&scst_mutex);
out:
return res;
}
/* Caller must hold scst_mutex. Called from the device removal code. */
int scst_dg_dev_remove_by_dev(struct scst_device *dev)
{
struct scst_dev_group *dg;
struct scst_dg_dev *dgdev;
int res;
res = -EINVAL;
dg = __lookup_dg_by_dev(dev);
if (!dg)
goto out;
dgdev = __lookup_dg_dev_by_dev(dg, dev);
BUG_ON(!dgdev);
__scst_dg_dev_remove(dg, dgdev);
res = 0;
out:
return res;
}
/*
* Device group management.
*/
static void scst_release_dg(struct kobject *kobj)
{
struct scst_dev_group *dg;
dg = container_of(kobj, struct scst_dev_group, kobj);
kfree(dg->name);
kfree(dg);
}
static struct kobj_type scst_dg_ktype = {
#ifndef CONFIG_SCST_PROC
.sysfs_ops = &scst_sysfs_ops,
#endif
.release = scst_release_dg,
};
/**
* scst_dg_add() - Add a new device group object and make it visible in sysfs.
*/
int scst_dg_add(struct kobject *parent, const char *name)
{
struct scst_dev_group *dg;
int res;
TRACE_ENTRY();
res = -ENOMEM;
dg = kzalloc(sizeof(*dg), GFP_KERNEL);
if (!dg)
goto out;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24)
kobject_init(&dg->kobj, &scst_dg_ktype);
#else
kobject_init(&dg->kobj);
dg->kobj.ktype = &scst_dg_ktype;
#endif
dg->name = kstrdup(name, GFP_KERNEL);
if (!dg->name)
goto out_put;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out_put;
res = -EEXIST;
if (__lookup_dg_by_name(name))
goto out_unlock;
res = -ENOMEM;
INIT_LIST_HEAD(&dg->dev_list);
INIT_LIST_HEAD(&dg->tg_list);
res = scst_dg_sysfs_add(parent, dg);
if (res)
goto out_unlock;
list_add_tail(&dg->entry, &scst_dev_group_list);
mutex_unlock(&scst_mutex);
out:
TRACE_EXIT_RES(res);
return res;
out_unlock:
mutex_unlock(&scst_mutex);
out_put:
kobject_put(&dg->kobj);
goto out;
}
static void __scst_dg_remove(struct scst_dev_group *dg)
{
struct scst_dg_dev *dgdev;
struct scst_target_group *tg;
list_del(&dg->entry);
scst_dg_sysfs_del(dg);
while (!list_empty(&dg->dev_list)) {
dgdev = list_first_entry(&dg->dev_list, struct scst_dg_dev,
entry);
__scst_dg_dev_remove(dg, dgdev);
}
while (!list_empty(&dg->tg_list)) {
tg = list_first_entry(&dg->tg_list, struct scst_target_group,
entry);
__scst_tg_remove(dg, tg);
}
kobject_put(&dg->kobj);
}
int scst_dg_remove(const char *name)
{
struct scst_dev_group *dg;
int res;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out;
res = -EINVAL;
dg = __lookup_dg_by_name(name);
if (!dg)
goto out_unlock;
__scst_dg_remove(dg);
res = 0;
out_unlock:
mutex_unlock(&scst_mutex);
out:
return res;
}
/*
* Given a pointer to a device_groups/<dg>/devices or
* device_groups/<dg>/target_groups kobject, return the pointer to the
* corresponding device group.
*
* Note: The caller must hold a reference on the kobject to avoid that the
* object disappears before the caller stops using the device group pointer.
*/
struct scst_dev_group *scst_lookup_dg_by_kobj(struct kobject *kobj)
{
int res;
struct scst_dev_group *dg;
dg = NULL;
res = mutex_lock_interruptible(&scst_mutex);
if (res)
goto out;
list_for_each_entry(dg, &scst_dev_group_list, entry)
if (dg->dev_kobj == kobj || dg->tg_kobj == kobj)
goto out_unlock;
dg = NULL;
out_unlock:
mutex_unlock(&scst_mutex);
out:
return dg;
}
/*
* Target group module management.
*/
void scst_tg_init(void)
{
INIT_LIST_HEAD(&scst_dev_group_list);
}
void scst_tg_cleanup(void)
{
struct scst_dev_group *tg;
mutex_lock(&scst_mutex);
while (!list_empty(&scst_dev_group_list)) {
tg = list_first_entry(&scst_dev_group_list,
struct scst_dev_group, entry);
__scst_dg_remove(tg);
}
mutex_unlock(&scst_mutex);
}
/*
* Functions for target group related SCSI command support.
*/
/**
* scst_lookup_tg_id() - Look up a target port group identifier.
* @dev: SCST device.
* @tgt: SCST target.
*
* Returns a non-zero number if the lookup was successful and zero if not.
*/
uint16_t scst_lookup_tg_id(struct scst_device *dev, struct scst_tgt *tgt)
{
struct scst_dev_group *dg;
struct scst_target_group *tg;
struct scst_tg_tgt *tg_tgt;
uint16_t tg_id = 0;
TRACE_ENTRY();
mutex_lock(&scst_mutex);
dg = __lookup_dg_by_dev(dev);
if (!dg)
goto out_unlock;
tg_tgt = __lookup_dg_tgt(dg, tgt->tgt_name);
if (!tg_tgt)
goto out_unlock;
tg = tg_tgt->tg;
BUG_ON(!tg);
tg_id = tg->group_id;
out_unlock:
mutex_unlock(&scst_mutex);
TRACE_EXIT_RES(tg_id);
return tg_id;
}
EXPORT_SYMBOL_GPL(scst_lookup_tg_id);
/**
* scst_impl_alua_configured() - Whether implicit ALUA has been configured.
* @dev: Pointer to the SCST device to verify.
*/
bool scst_impl_alua_configured(struct scst_device *dev)
{
struct scst_dev_group *dg;
bool res;
mutex_lock(&scst_mutex);
dg = __lookup_dg_by_dev(dev);
res = dg != NULL;
mutex_unlock(&scst_mutex);
return res;
}
EXPORT_SYMBOL_GPL(scst_impl_alua_configured);
/**
* scst_tg_get_group_info() - Build REPORT TARGET GROUPS response.
* @buf: Pointer to a pointer to which the result buffer pointer will be set.
* @length: Response length, including the "RETURN DATA LENGTH" field.
* @dev: Pointer to the SCST device for which to obtain group information.
* @data_format: Three-bit response data format specification.
*/
int scst_tg_get_group_info(void **buf, uint32_t *length,
struct scst_device *dev, uint8_t data_format)
{
struct scst_dev_group *dg;
struct scst_target_group *tg;
struct scst_tg_tgt *tgtgt;
struct scst_tgt *tgt;
uint8_t *p;
uint32_t ret_data_len;
uint16_t rel_tgt_id;
int res;
TRACE_ENTRY();
BUG_ON(!buf);
BUG_ON(!length);
ret_data_len = 0;
res = -EINVAL;
switch (data_format) {
case 0:
break;
case 1:
/* Extended header */
ret_data_len += 4;
break;
default:
goto out;
}
*length = 4;
mutex_lock(&scst_mutex);
dg = __lookup_dg_by_dev(dev);
if (dg) {
list_for_each_entry(tg, &dg->tg_list, entry) {
/* Target port group descriptor header. */
ret_data_len += 8;
list_for_each_entry(tgtgt, &tg->tgt_list, entry) {
/* Target port descriptor. */
ret_data_len += 4;
}
}
}
*length += ret_data_len;
res = -ENOMEM;
*buf = kzalloc(*length, GFP_KERNEL);
if (!*buf)
goto out_unlock;
p = *buf;
/* Return data length. */
put_unaligned(cpu_to_be32(ret_data_len), (__be32 *)p);
p += 4;
if (data_format == 1) {
/* Extended header */
*p++ = 0x10; /* format = 1 */
*p++ = 0x00; /* implicit transition time = 0 */
p += 2; /* reserved */
}
if (!dg)
goto done;
list_for_each_entry(tg, &dg->tg_list, entry) {
/* Target port group descriptor header. */
*p++ = (tg->preferred ? SCST_TG_PREFERRED : 0) | tg->state;
*p++ = SCST_TG_SUP_OPTIMIZED
| SCST_TG_SUP_NONOPTIMIZED
| SCST_TG_SUP_STANDBY
| SCST_TG_SUP_UNAVAILABLE;
put_unaligned(cpu_to_be16(tg->group_id), (__be16 *)p);
p += 2;
p++; /* reserved */
*p++ = 2; /* status code: implicit transition */
p++; /* vendor specific */
list_for_each_entry(tgtgt, &tg->tgt_list, entry)
(*p)++; /* target port count */
p++;
list_for_each_entry(tgtgt, &tg->tgt_list, entry) {
tgt = tgtgt->tgt;
rel_tgt_id = tgt ? tgt->rel_tgt_id : tgtgt->rel_tgt_id;
/* Target port descriptor. */
p += 2; /* reserved */
/* Relative target port identifier. */
put_unaligned(cpu_to_be16(rel_tgt_id),
(__be16 *)p);
p += 2;
}
}
done:
WARN_ON(p - (uint8_t *)*buf != *length);
res = 0;
out_unlock:
mutex_unlock(&scst_mutex);
out:
TRACE_EXIT_RES(res);
return res;
}
EXPORT_SYMBOL_GPL(scst_tg_get_group_info);