mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-17 02:31:27 +00:00
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:
@@ -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"
|
||||
|
||||
259
scst/README
259
scst/README
@@ -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
|
||||
-------
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*************************************************************/
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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/ \
|
||||
|
||||
@@ -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)/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 { \
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
835
scst/src/scst_tg.c
Normal 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);
|
||||
Reference in New Issue
Block a user