2019-05-28 19:57:05 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2008-02-04 00:48:56 +03:00
|
|
|
/*
|
|
|
|
* SCSI Enclosure Services
|
|
|
|
*
|
|
|
|
* Copyright (C) 2008 James Bottomley <James.Bottomley@HansenPartnership.com>
|
2019-05-01 19:14:13 +03:00
|
|
|
*/
|
2008-02-04 00:48:56 +03:00
|
|
|
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 11:04:11 +03:00
|
|
|
#include <linux/slab.h>
|
2008-02-04 00:48:56 +03:00
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/enclosure.h>
|
2014-03-15 12:51:51 +04:00
|
|
|
#include <asm/unaligned.h>
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
#include <scsi/scsi.h>
|
|
|
|
#include <scsi/scsi_cmnd.h>
|
|
|
|
#include <scsi/scsi_dbg.h>
|
|
|
|
#include <scsi/scsi_device.h>
|
|
|
|
#include <scsi/scsi_driver.h>
|
|
|
|
#include <scsi/scsi_host.h>
|
|
|
|
|
2015-12-09 23:56:07 +03:00
|
|
|
#include <scsi/scsi_transport_sas.h>
|
|
|
|
|
2008-02-04 00:48:56 +03:00
|
|
|
struct ses_device {
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char *page1;
|
2011-03-18 18:24:23 +03:00
|
|
|
unsigned char *page1_types;
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char *page2;
|
|
|
|
unsigned char *page10;
|
2008-02-04 00:48:56 +03:00
|
|
|
short page1_len;
|
2011-03-18 18:24:23 +03:00
|
|
|
short page1_num_types;
|
2008-02-04 00:48:56 +03:00
|
|
|
short page2_len;
|
|
|
|
short page10_len;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ses_component {
|
|
|
|
u64 addr;
|
|
|
|
};
|
|
|
|
|
2017-08-15 11:21:43 +03:00
|
|
|
static bool ses_page2_supported(struct enclosure_device *edev)
|
|
|
|
{
|
|
|
|
struct ses_device *ses_dev = edev->scratch;
|
|
|
|
|
|
|
|
return (ses_dev->page2 != NULL);
|
|
|
|
}
|
|
|
|
|
2008-02-04 00:48:56 +03:00
|
|
|
static int ses_probe(struct device *dev)
|
|
|
|
{
|
|
|
|
struct scsi_device *sdev = to_scsi_device(dev);
|
|
|
|
int err = -ENODEV;
|
|
|
|
|
|
|
|
if (sdev->type != TYPE_ENCLOSURE)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
sdev_printk(KERN_NOTICE, sdev, "Attached Enclosure device\n");
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-06-23 19:14:31 +04:00
|
|
|
#define SES_TIMEOUT (30 * HZ)
|
2008-02-04 00:48:56 +03:00
|
|
|
#define SES_RETRIES 3
|
|
|
|
|
2014-12-31 01:46:18 +03:00
|
|
|
static void init_device_slot_control(unsigned char *dest_desc,
|
|
|
|
struct enclosure_component *ecomp,
|
|
|
|
unsigned char *status)
|
|
|
|
{
|
|
|
|
memcpy(dest_desc, status, 4);
|
|
|
|
dest_desc[0] = 0;
|
|
|
|
/* only clear byte 1 for ENCLOSURE_COMPONENT_DEVICE */
|
|
|
|
if (ecomp->type == ENCLOSURE_COMPONENT_DEVICE)
|
|
|
|
dest_desc[1] = 0;
|
|
|
|
dest_desc[2] &= 0xde;
|
|
|
|
dest_desc[3] &= 0x3c;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-04 00:48:56 +03:00
|
|
|
static int ses_recv_diag(struct scsi_device *sdev, int page_code,
|
|
|
|
void *buf, int bufflen)
|
|
|
|
{
|
2015-12-08 20:00:31 +03:00
|
|
|
int ret;
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char cmd[] = {
|
2008-02-04 00:48:56 +03:00
|
|
|
RECEIVE_DIAGNOSTIC,
|
|
|
|
1, /* Set PCV bit */
|
|
|
|
page_code,
|
|
|
|
bufflen >> 8,
|
|
|
|
bufflen & 0xff,
|
|
|
|
0
|
|
|
|
};
|
2015-12-08 20:00:31 +03:00
|
|
|
unsigned char recv_page_code;
|
2021-09-17 06:24:21 +03:00
|
|
|
unsigned int retries = SES_RETRIES;
|
|
|
|
struct scsi_sense_hdr sshdr;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ret = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen,
|
|
|
|
&sshdr, SES_TIMEOUT, 1, NULL);
|
|
|
|
} while (ret > 0 && --retries && scsi_sense_valid(&sshdr) &&
|
|
|
|
(sshdr.sense_key == NOT_READY ||
|
|
|
|
(sshdr.sense_key == UNIT_ATTENTION && sshdr.asc == 0x29)));
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2017-08-01 21:45:36 +03:00
|
|
|
if (unlikely(ret))
|
2015-12-08 20:00:31 +03:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
recv_page_code = ((unsigned char *)buf)[0];
|
|
|
|
|
|
|
|
if (likely(recv_page_code == page_code))
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* successful diagnostic but wrong page code. This happens to some
|
|
|
|
* USB devices, just print a message and pretend there was an error */
|
|
|
|
|
|
|
|
sdev_printk(KERN_ERR, sdev,
|
|
|
|
"Wrong diagnostic page; asked for %d got %u\n",
|
|
|
|
page_code, recv_page_code);
|
|
|
|
|
|
|
|
return -EINVAL;
|
2008-02-04 00:48:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ses_send_diag(struct scsi_device *sdev, int page_code,
|
|
|
|
void *buf, int bufflen)
|
|
|
|
{
|
2021-09-24 12:51:53 +03:00
|
|
|
int result;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char cmd[] = {
|
2008-02-04 00:48:56 +03:00
|
|
|
SEND_DIAGNOSTIC,
|
|
|
|
0x10, /* Set PF bit */
|
|
|
|
0,
|
|
|
|
bufflen >> 8,
|
|
|
|
bufflen & 0xff,
|
|
|
|
0
|
|
|
|
};
|
2021-09-17 06:24:21 +03:00
|
|
|
struct scsi_sense_hdr sshdr;
|
|
|
|
unsigned int retries = SES_RETRIES;
|
|
|
|
|
|
|
|
do {
|
|
|
|
result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, buf, bufflen,
|
|
|
|
&sshdr, SES_TIMEOUT, 1, NULL);
|
|
|
|
} while (result > 0 && --retries && scsi_sense_valid(&sshdr) &&
|
|
|
|
(sshdr.sense_key == NOT_READY ||
|
|
|
|
(sshdr.sense_key == UNIT_ATTENTION && sshdr.asc == 0x29)));
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
if (result)
|
|
|
|
sdev_printk(KERN_ERR, sdev, "SEND DIAGNOSTIC result: %8x\n",
|
|
|
|
result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ses_set_page2_descriptor(struct enclosure_device *edev,
|
|
|
|
struct enclosure_component *ecomp,
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char *desc)
|
2008-02-04 00:48:56 +03:00
|
|
|
{
|
|
|
|
int i, j, count = 0, descriptor = ecomp->number;
|
2008-02-22 02:13:36 +03:00
|
|
|
struct scsi_device *sdev = to_scsi_device(edev->edev.parent);
|
2008-02-04 00:48:56 +03:00
|
|
|
struct ses_device *ses_dev = edev->scratch;
|
2011-03-18 18:24:23 +03:00
|
|
|
unsigned char *type_ptr = ses_dev->page1_types;
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char *desc_ptr = ses_dev->page2 + 8;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
/* Clear everything */
|
|
|
|
memset(desc_ptr, 0, ses_dev->page2_len - 8);
|
2011-03-18 18:24:23 +03:00
|
|
|
for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) {
|
2008-02-04 00:48:56 +03:00
|
|
|
for (j = 0; j < type_ptr[1]; j++) {
|
|
|
|
desc_ptr += 4;
|
|
|
|
if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE &&
|
|
|
|
type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE)
|
|
|
|
continue;
|
|
|
|
if (count++ == descriptor) {
|
|
|
|
memcpy(desc_ptr, desc, 4);
|
|
|
|
/* set select */
|
|
|
|
desc_ptr[0] |= 0x80;
|
|
|
|
/* clear reserved, just in case */
|
|
|
|
desc_ptr[0] &= 0xf0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ses_send_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len);
|
|
|
|
}
|
|
|
|
|
2008-02-14 03:25:16 +03:00
|
|
|
static unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev,
|
2008-02-04 00:48:56 +03:00
|
|
|
struct enclosure_component *ecomp)
|
|
|
|
{
|
|
|
|
int i, j, count = 0, descriptor = ecomp->number;
|
2008-02-22 02:13:36 +03:00
|
|
|
struct scsi_device *sdev = to_scsi_device(edev->edev.parent);
|
2008-02-04 00:48:56 +03:00
|
|
|
struct ses_device *ses_dev = edev->scratch;
|
2011-03-18 18:24:23 +03:00
|
|
|
unsigned char *type_ptr = ses_dev->page1_types;
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char *desc_ptr = ses_dev->page2 + 8;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2017-08-15 11:21:41 +03:00
|
|
|
if (ses_recv_diag(sdev, 2, ses_dev->page2, ses_dev->page2_len) < 0)
|
|
|
|
return NULL;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2011-03-18 18:24:23 +03:00
|
|
|
for (i = 0; i < ses_dev->page1_num_types; i++, type_ptr += 4) {
|
2008-02-04 00:48:56 +03:00
|
|
|
for (j = 0; j < type_ptr[1]; j++) {
|
|
|
|
desc_ptr += 4;
|
|
|
|
if (type_ptr[0] != ENCLOSURE_COMPONENT_DEVICE &&
|
|
|
|
type_ptr[0] != ENCLOSURE_COMPONENT_ARRAY_DEVICE)
|
|
|
|
continue;
|
|
|
|
if (count++ == descriptor)
|
|
|
|
return desc_ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-06-09 08:27:07 +04:00
|
|
|
/* For device slot and array device slot elements, byte 3 bit 6
|
|
|
|
* is "fault sensed" while byte 3 bit 5 is "fault reqstd". As this
|
|
|
|
* code stands these bits are shifted 4 positions right so in
|
|
|
|
* sysfs they will appear as bits 2 and 1 respectively. Strange. */
|
2008-02-04 00:48:56 +03:00
|
|
|
static void ses_get_fault(struct enclosure_device *edev,
|
|
|
|
struct enclosure_component *ecomp)
|
|
|
|
{
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char *desc;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2017-08-15 11:21:43 +03:00
|
|
|
if (!ses_page2_supported(edev)) {
|
|
|
|
ecomp->fault = 0;
|
|
|
|
return;
|
|
|
|
}
|
2008-02-04 00:48:56 +03:00
|
|
|
desc = ses_get_page2_descriptor(edev, ecomp);
|
2008-02-14 03:25:16 +03:00
|
|
|
if (desc)
|
|
|
|
ecomp->fault = (desc[3] & 0x60) >> 4;
|
2008-02-04 00:48:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ses_set_fault(struct enclosure_device *edev,
|
|
|
|
struct enclosure_component *ecomp,
|
|
|
|
enum enclosure_component_setting val)
|
|
|
|
{
|
2014-12-31 01:46:18 +03:00
|
|
|
unsigned char desc[4];
|
|
|
|
unsigned char *desc_ptr;
|
|
|
|
|
2017-08-15 11:21:43 +03:00
|
|
|
if (!ses_page2_supported(edev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-12-31 01:46:18 +03:00
|
|
|
desc_ptr = ses_get_page2_descriptor(edev, ecomp);
|
|
|
|
|
|
|
|
if (!desc_ptr)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
init_device_slot_control(desc, ecomp, desc_ptr);
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
switch (val) {
|
|
|
|
case ENCLOSURE_SETTING_DISABLED:
|
2014-12-31 01:46:18 +03:00
|
|
|
desc[3] &= 0xdf;
|
2008-02-04 00:48:56 +03:00
|
|
|
break;
|
|
|
|
case ENCLOSURE_SETTING_ENABLED:
|
2014-12-31 01:46:18 +03:00
|
|
|
desc[3] |= 0x20;
|
2008-02-04 00:48:56 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* SES doesn't do the SGPIO blink settings */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ses_set_page2_descriptor(edev, ecomp, desc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ses_get_status(struct enclosure_device *edev,
|
|
|
|
struct enclosure_component *ecomp)
|
|
|
|
{
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char *desc;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2017-08-15 11:21:43 +03:00
|
|
|
if (!ses_page2_supported(edev)) {
|
|
|
|
ecomp->status = 0;
|
|
|
|
return;
|
|
|
|
}
|
2008-02-04 00:48:56 +03:00
|
|
|
desc = ses_get_page2_descriptor(edev, ecomp);
|
2008-02-14 03:25:16 +03:00
|
|
|
if (desc)
|
|
|
|
ecomp->status = (desc[0] & 0x0f);
|
2008-02-04 00:48:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ses_get_locate(struct enclosure_device *edev,
|
|
|
|
struct enclosure_component *ecomp)
|
|
|
|
{
|
2008-02-14 03:25:16 +03:00
|
|
|
unsigned char *desc;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2017-08-15 11:21:43 +03:00
|
|
|
if (!ses_page2_supported(edev)) {
|
|
|
|
ecomp->locate = 0;
|
|
|
|
return;
|
|
|
|
}
|
2008-02-04 00:48:56 +03:00
|
|
|
desc = ses_get_page2_descriptor(edev, ecomp);
|
2008-02-14 03:25:16 +03:00
|
|
|
if (desc)
|
|
|
|
ecomp->locate = (desc[2] & 0x02) ? 1 : 0;
|
2008-02-04 00:48:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ses_set_locate(struct enclosure_device *edev,
|
|
|
|
struct enclosure_component *ecomp,
|
|
|
|
enum enclosure_component_setting val)
|
|
|
|
{
|
2014-12-31 01:46:18 +03:00
|
|
|
unsigned char desc[4];
|
|
|
|
unsigned char *desc_ptr;
|
|
|
|
|
2017-08-15 11:21:43 +03:00
|
|
|
if (!ses_page2_supported(edev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-12-31 01:46:18 +03:00
|
|
|
desc_ptr = ses_get_page2_descriptor(edev, ecomp);
|
|
|
|
|
|
|
|
if (!desc_ptr)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
init_device_slot_control(desc, ecomp, desc_ptr);
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
switch (val) {
|
|
|
|
case ENCLOSURE_SETTING_DISABLED:
|
2014-12-31 01:46:18 +03:00
|
|
|
desc[2] &= 0xfd;
|
2008-02-04 00:48:56 +03:00
|
|
|
break;
|
|
|
|
case ENCLOSURE_SETTING_ENABLED:
|
2014-12-31 01:46:18 +03:00
|
|
|
desc[2] |= 0x02;
|
2008-02-04 00:48:56 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* SES doesn't do the SGPIO blink settings */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return ses_set_page2_descriptor(edev, ecomp, desc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ses_set_active(struct enclosure_device *edev,
|
|
|
|
struct enclosure_component *ecomp,
|
|
|
|
enum enclosure_component_setting val)
|
|
|
|
{
|
2014-12-31 01:46:18 +03:00
|
|
|
unsigned char desc[4];
|
|
|
|
unsigned char *desc_ptr;
|
|
|
|
|
2017-08-15 11:21:43 +03:00
|
|
|
if (!ses_page2_supported(edev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-12-31 01:46:18 +03:00
|
|
|
desc_ptr = ses_get_page2_descriptor(edev, ecomp);
|
|
|
|
|
|
|
|
if (!desc_ptr)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
init_device_slot_control(desc, ecomp, desc_ptr);
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
switch (val) {
|
|
|
|
case ENCLOSURE_SETTING_DISABLED:
|
2014-12-31 01:46:18 +03:00
|
|
|
desc[2] &= 0x7f;
|
2008-02-04 00:48:56 +03:00
|
|
|
ecomp->active = 0;
|
|
|
|
break;
|
|
|
|
case ENCLOSURE_SETTING_ENABLED:
|
2014-12-31 01:46:18 +03:00
|
|
|
desc[2] |= 0x80;
|
2008-02-04 00:48:56 +03:00
|
|
|
ecomp->active = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* SES doesn't do the SGPIO blink settings */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return ses_set_page2_descriptor(edev, ecomp, desc);
|
|
|
|
}
|
|
|
|
|
2014-12-31 01:46:16 +03:00
|
|
|
static int ses_show_id(struct enclosure_device *edev, char *buf)
|
|
|
|
{
|
|
|
|
struct ses_device *ses_dev = edev->scratch;
|
|
|
|
unsigned long long id = get_unaligned_be64(ses_dev->page1+8+4);
|
|
|
|
|
|
|
|
return sprintf(buf, "%#llx\n", id);
|
|
|
|
}
|
|
|
|
|
2014-12-31 01:46:18 +03:00
|
|
|
static void ses_get_power_status(struct enclosure_device *edev,
|
|
|
|
struct enclosure_component *ecomp)
|
|
|
|
{
|
|
|
|
unsigned char *desc;
|
|
|
|
|
2017-08-15 11:21:43 +03:00
|
|
|
if (!ses_page2_supported(edev)) {
|
|
|
|
ecomp->power_status = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-31 01:46:18 +03:00
|
|
|
desc = ses_get_page2_descriptor(edev, ecomp);
|
|
|
|
if (desc)
|
|
|
|
ecomp->power_status = (desc[3] & 0x10) ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ses_set_power_status(struct enclosure_device *edev,
|
|
|
|
struct enclosure_component *ecomp,
|
|
|
|
int val)
|
|
|
|
{
|
|
|
|
unsigned char desc[4];
|
|
|
|
unsigned char *desc_ptr;
|
|
|
|
|
2017-08-15 11:21:43 +03:00
|
|
|
if (!ses_page2_supported(edev))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-12-31 01:46:18 +03:00
|
|
|
desc_ptr = ses_get_page2_descriptor(edev, ecomp);
|
|
|
|
|
|
|
|
if (!desc_ptr)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
init_device_slot_control(desc, ecomp, desc_ptr);
|
|
|
|
|
|
|
|
switch (val) {
|
|
|
|
/* power = 1 is device_off = 0 and vice versa */
|
|
|
|
case 0:
|
|
|
|
desc[3] |= 0x10;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
desc[3] &= 0xef;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
ecomp->power_status = val;
|
|
|
|
return ses_set_page2_descriptor(edev, ecomp, desc);
|
|
|
|
}
|
|
|
|
|
2008-02-04 00:48:56 +03:00
|
|
|
static struct enclosure_component_callbacks ses_enclosure_callbacks = {
|
|
|
|
.get_fault = ses_get_fault,
|
|
|
|
.set_fault = ses_set_fault,
|
|
|
|
.get_status = ses_get_status,
|
|
|
|
.get_locate = ses_get_locate,
|
|
|
|
.set_locate = ses_set_locate,
|
2014-12-31 01:46:18 +03:00
|
|
|
.get_power_status = ses_get_power_status,
|
|
|
|
.set_power_status = ses_set_power_status,
|
2008-02-04 00:48:56 +03:00
|
|
|
.set_active = ses_set_active,
|
2014-12-31 01:46:16 +03:00
|
|
|
.show_id = ses_show_id,
|
2008-02-04 00:48:56 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
struct ses_host_edev {
|
|
|
|
struct Scsi_Host *shost;
|
|
|
|
struct enclosure_device *edev;
|
|
|
|
};
|
|
|
|
|
2009-03-04 23:06:05 +03:00
|
|
|
#if 0
|
2008-02-04 00:48:56 +03:00
|
|
|
int ses_match_host(struct enclosure_device *edev, void *data)
|
|
|
|
{
|
|
|
|
struct ses_host_edev *sed = data;
|
|
|
|
struct scsi_device *sdev;
|
|
|
|
|
2008-02-22 02:13:36 +03:00
|
|
|
if (!scsi_is_sdev_device(edev->edev.parent))
|
2008-02-04 00:48:56 +03:00
|
|
|
return 0;
|
|
|
|
|
2008-02-22 02:13:36 +03:00
|
|
|
sdev = to_scsi_device(edev->edev.parent);
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
if (sdev->host != sed->shost)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
sed->edev = edev;
|
|
|
|
return 1;
|
|
|
|
}
|
2009-03-04 23:06:05 +03:00
|
|
|
#endif /* 0 */
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2023-02-02 19:24:49 +03:00
|
|
|
static int ses_process_descriptor(struct enclosure_component *ecomp,
|
|
|
|
unsigned char *desc, int max_desc_len)
|
2008-02-04 00:48:56 +03:00
|
|
|
{
|
|
|
|
int eip = desc[0] & 0x10;
|
|
|
|
int invalid = desc[0] & 0x80;
|
|
|
|
enum scsi_protocol proto = desc[0] & 0x0f;
|
|
|
|
u64 addr = 0;
|
2014-12-31 01:46:17 +03:00
|
|
|
int slot = -1;
|
2008-02-04 00:48:56 +03:00
|
|
|
struct ses_component *scomp = ecomp->scratch;
|
|
|
|
unsigned char *d;
|
|
|
|
|
|
|
|
if (invalid)
|
2023-02-02 19:24:49 +03:00
|
|
|
return 0;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
switch (proto) {
|
2014-12-31 01:46:17 +03:00
|
|
|
case SCSI_PROTOCOL_FCP:
|
|
|
|
if (eip) {
|
2023-02-02 19:24:49 +03:00
|
|
|
if (max_desc_len <= 7)
|
|
|
|
return 1;
|
2014-12-31 01:46:17 +03:00
|
|
|
d = desc + 4;
|
|
|
|
slot = d[3];
|
|
|
|
}
|
|
|
|
break;
|
2008-02-04 00:48:56 +03:00
|
|
|
case SCSI_PROTOCOL_SAS:
|
2023-02-02 19:24:49 +03:00
|
|
|
|
2014-12-31 01:46:17 +03:00
|
|
|
if (eip) {
|
2023-02-02 19:24:49 +03:00
|
|
|
if (max_desc_len <= 27)
|
|
|
|
return 1;
|
2014-12-31 01:46:17 +03:00
|
|
|
d = desc + 4;
|
|
|
|
slot = d[3];
|
2008-02-04 00:48:56 +03:00
|
|
|
d = desc + 8;
|
2023-02-02 19:24:49 +03:00
|
|
|
} else {
|
|
|
|
if (max_desc_len <= 23)
|
|
|
|
return 1;
|
2008-02-04 00:48:56 +03:00
|
|
|
d = desc + 4;
|
2023-02-02 19:24:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-04 00:48:56 +03:00
|
|
|
/* only take the phy0 addr */
|
|
|
|
addr = (u64)d[12] << 56 |
|
|
|
|
(u64)d[13] << 48 |
|
|
|
|
(u64)d[14] << 40 |
|
|
|
|
(u64)d[15] << 32 |
|
|
|
|
(u64)d[16] << 24 |
|
|
|
|
(u64)d[17] << 16 |
|
|
|
|
(u64)d[18] << 8 |
|
|
|
|
(u64)d[19];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* FIXME: Need to add more protocols than just SAS */
|
|
|
|
break;
|
|
|
|
}
|
2014-12-31 01:46:17 +03:00
|
|
|
ecomp->slot = slot;
|
2008-02-04 00:48:56 +03:00
|
|
|
scomp->addr = addr;
|
2023-02-02 19:24:49 +03:00
|
|
|
|
|
|
|
return 0;
|
2008-02-04 00:48:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct efd {
|
|
|
|
u64 addr;
|
|
|
|
struct device *dev;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int ses_enclosure_find_by_addr(struct enclosure_device *edev,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct efd *efd = data;
|
|
|
|
int i;
|
|
|
|
struct ses_component *scomp;
|
|
|
|
|
|
|
|
for (i = 0; i < edev->components; i++) {
|
|
|
|
scomp = edev->component[i].scratch;
|
|
|
|
if (scomp->addr != efd->addr)
|
|
|
|
continue;
|
|
|
|
|
2014-12-31 01:46:15 +03:00
|
|
|
if (enclosure_add_device(edev, i, efd->dev) == 0)
|
|
|
|
kobject_uevent(&efd->dev->kobj, KOBJ_CHANGE);
|
2008-02-04 00:48:56 +03:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-01 04:43:59 +04:00
|
|
|
#define INIT_ALLOC_SIZE 32
|
|
|
|
|
|
|
|
static void ses_enclosure_data_process(struct enclosure_device *edev,
|
|
|
|
struct scsi_device *sdev,
|
|
|
|
int create)
|
|
|
|
{
|
|
|
|
u32 result;
|
|
|
|
unsigned char *buf = NULL, *type_ptr, *desc_ptr, *addl_desc_ptr = NULL;
|
|
|
|
int i, j, page7_len, len, components;
|
|
|
|
struct ses_device *ses_dev = edev->scratch;
|
2011-03-18 18:24:23 +03:00
|
|
|
int types = ses_dev->page1_num_types;
|
2009-08-01 04:43:59 +04:00
|
|
|
unsigned char *hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!hdr_buf)
|
|
|
|
goto simple_populate;
|
|
|
|
|
|
|
|
/* re-read page 10 */
|
|
|
|
if (ses_dev->page10)
|
|
|
|
ses_recv_diag(sdev, 10, ses_dev->page10, ses_dev->page10_len);
|
|
|
|
/* Page 7 for the descriptors is optional */
|
|
|
|
result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE);
|
|
|
|
if (result)
|
|
|
|
goto simple_populate;
|
|
|
|
|
|
|
|
page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
|
|
|
|
/* add 1 for trailing '\0' we'll use */
|
|
|
|
buf = kzalloc(len + 1, GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
|
|
goto simple_populate;
|
|
|
|
result = ses_recv_diag(sdev, 7, buf, len);
|
|
|
|
if (result) {
|
|
|
|
simple_populate:
|
|
|
|
kfree(buf);
|
|
|
|
buf = NULL;
|
|
|
|
desc_ptr = NULL;
|
|
|
|
len = 0;
|
|
|
|
page7_len = 0;
|
|
|
|
} else {
|
|
|
|
desc_ptr = buf + 8;
|
|
|
|
len = (desc_ptr[2] << 8) + desc_ptr[3];
|
|
|
|
/* skip past overall descriptor */
|
|
|
|
desc_ptr += len + 4;
|
|
|
|
}
|
2023-02-02 19:24:49 +03:00
|
|
|
if (ses_dev->page10 && ses_dev->page10_len > 9)
|
2009-11-04 21:01:22 +03:00
|
|
|
addl_desc_ptr = ses_dev->page10 + 8;
|
2011-03-18 18:24:23 +03:00
|
|
|
type_ptr = ses_dev->page1_types;
|
2009-08-01 04:43:59 +04:00
|
|
|
components = 0;
|
|
|
|
for (i = 0; i < types; i++, type_ptr += 4) {
|
|
|
|
for (j = 0; j < type_ptr[1]; j++) {
|
|
|
|
char *name = NULL;
|
|
|
|
struct enclosure_component *ecomp;
|
2023-02-02 19:24:49 +03:00
|
|
|
int max_desc_len;
|
2009-08-01 04:43:59 +04:00
|
|
|
|
|
|
|
if (desc_ptr) {
|
2023-02-02 19:24:50 +03:00
|
|
|
if (desc_ptr + 3 >= buf + page7_len) {
|
2009-08-01 04:43:59 +04:00
|
|
|
desc_ptr = NULL;
|
|
|
|
} else {
|
|
|
|
len = (desc_ptr[2] << 8) + desc_ptr[3];
|
|
|
|
desc_ptr += 4;
|
2023-02-02 19:24:50 +03:00
|
|
|
if (desc_ptr + len > buf + page7_len)
|
|
|
|
desc_ptr = NULL;
|
|
|
|
else {
|
|
|
|
/* Add trailing zero - pushes into
|
|
|
|
* reserved space */
|
|
|
|
desc_ptr[len] = '\0';
|
|
|
|
name = desc_ptr;
|
|
|
|
}
|
2009-08-01 04:43:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
|
|
|
|
type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) {
|
|
|
|
|
|
|
|
if (create)
|
2014-12-31 01:46:14 +03:00
|
|
|
ecomp = enclosure_component_alloc(
|
|
|
|
edev,
|
|
|
|
components++,
|
|
|
|
type_ptr[0],
|
|
|
|
name);
|
scsi: ses: Handle enclosure with just a primary component gracefully
commit c8e22b7a1694bb8d025ea636816472739d859145 upstream.
This reverts commit 3fe97ff3d949 ("scsi: ses: Don't attach if enclosure
has no components") and introduces proper handling of case where there are
no detected secondary components, but primary component (enumerated in
num_enclosures) does exist. That fix was originally proposed by Ding Hui
<dinghui@sangfor.com.cn>.
Completely ignoring devices that have one primary enclosure and no
secondary one results in ses_intf_add() bailing completely
scsi 2:0:0:254: enclosure has no enumerated components
scsi 2:0:0:254: Failed to bind enclosure -12ven in valid configurations such
even on valid configurations with 1 primary and 0 secondary enclosures as
below:
# sg_ses /dev/sg0
3PARdata SES 3321
Supported diagnostic pages:
Supported Diagnostic Pages [sdp] [0x0]
Configuration (SES) [cf] [0x1]
Short Enclosure Status (SES) [ses] [0x8]
# sg_ses -p cf /dev/sg0
3PARdata SES 3321
Configuration diagnostic page:
number of secondary subenclosures: 0
generation code: 0x0
enclosure descriptor list
Subenclosure identifier: 0 [primary]
relative ES process id: 0, number of ES processes: 1
number of type descriptor headers: 1
enclosure logical identifier (hex): 20000002ac02068d
enclosure vendor: 3PARdata product: VV rev: 3321
type descriptor header and text list
Element type: Unspecified, subenclosure id: 0
number of possible elements: 1
The changelog for the original fix follows
=====
We can get a crash when disconnecting the iSCSI session,
the call trace like this:
[ffff00002a00fb70] kfree at ffff00000830e224
[ffff00002a00fba0] ses_intf_remove at ffff000001f200e4
[ffff00002a00fbd0] device_del at ffff0000086b6a98
[ffff00002a00fc50] device_unregister at ffff0000086b6d58
[ffff00002a00fc70] __scsi_remove_device at ffff00000870608c
[ffff00002a00fca0] scsi_remove_device at ffff000008706134
[ffff00002a00fcc0] __scsi_remove_target at ffff0000087062e4
[ffff00002a00fd10] scsi_remove_target at ffff0000087064c0
[ffff00002a00fd70] __iscsi_unbind_session at ffff000001c872c4
[ffff00002a00fdb0] process_one_work at ffff00000810f35c
[ffff00002a00fe00] worker_thread at ffff00000810f648
[ffff00002a00fe70] kthread at ffff000008116e98
In ses_intf_add, components count could be 0, and kcalloc 0 size scomp,
but not saved in edev->component[i].scratch
In this situation, edev->component[0].scratch is an invalid pointer,
when kfree it in ses_intf_remove_enclosure, a crash like above would happen
The call trace also could be other random cases when kfree cannot catch
the invalid pointer
We should not use edev->component[] array when the components count is 0
We also need check index when use edev->component[] array in
ses_enclosure_data_process
=====
Reported-by: Michal Kolar <mich.k@seznam.cz>
Originally-by: Ding Hui <dinghui@sangfor.com.cn>
Cc: stable@vger.kernel.org
Fixes: 3fe97ff3d949 ("scsi: ses: Don't attach if enclosure has no components")
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Link: https://lore.kernel.org/r/nycvar.YFH.7.76.2304042122270.29760@cbobk.fhfr.pm
Tested-by: Michal Kolar <mich.k@seznam.cz>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-04-04 22:23:42 +03:00
|
|
|
else if (components < edev->components)
|
2009-08-01 04:43:59 +04:00
|
|
|
ecomp = &edev->component[components++];
|
scsi: ses: Handle enclosure with just a primary component gracefully
commit c8e22b7a1694bb8d025ea636816472739d859145 upstream.
This reverts commit 3fe97ff3d949 ("scsi: ses: Don't attach if enclosure
has no components") and introduces proper handling of case where there are
no detected secondary components, but primary component (enumerated in
num_enclosures) does exist. That fix was originally proposed by Ding Hui
<dinghui@sangfor.com.cn>.
Completely ignoring devices that have one primary enclosure and no
secondary one results in ses_intf_add() bailing completely
scsi 2:0:0:254: enclosure has no enumerated components
scsi 2:0:0:254: Failed to bind enclosure -12ven in valid configurations such
even on valid configurations with 1 primary and 0 secondary enclosures as
below:
# sg_ses /dev/sg0
3PARdata SES 3321
Supported diagnostic pages:
Supported Diagnostic Pages [sdp] [0x0]
Configuration (SES) [cf] [0x1]
Short Enclosure Status (SES) [ses] [0x8]
# sg_ses -p cf /dev/sg0
3PARdata SES 3321
Configuration diagnostic page:
number of secondary subenclosures: 0
generation code: 0x0
enclosure descriptor list
Subenclosure identifier: 0 [primary]
relative ES process id: 0, number of ES processes: 1
number of type descriptor headers: 1
enclosure logical identifier (hex): 20000002ac02068d
enclosure vendor: 3PARdata product: VV rev: 3321
type descriptor header and text list
Element type: Unspecified, subenclosure id: 0
number of possible elements: 1
The changelog for the original fix follows
=====
We can get a crash when disconnecting the iSCSI session,
the call trace like this:
[ffff00002a00fb70] kfree at ffff00000830e224
[ffff00002a00fba0] ses_intf_remove at ffff000001f200e4
[ffff00002a00fbd0] device_del at ffff0000086b6a98
[ffff00002a00fc50] device_unregister at ffff0000086b6d58
[ffff00002a00fc70] __scsi_remove_device at ffff00000870608c
[ffff00002a00fca0] scsi_remove_device at ffff000008706134
[ffff00002a00fcc0] __scsi_remove_target at ffff0000087062e4
[ffff00002a00fd10] scsi_remove_target at ffff0000087064c0
[ffff00002a00fd70] __iscsi_unbind_session at ffff000001c872c4
[ffff00002a00fdb0] process_one_work at ffff00000810f35c
[ffff00002a00fe00] worker_thread at ffff00000810f648
[ffff00002a00fe70] kthread at ffff000008116e98
In ses_intf_add, components count could be 0, and kcalloc 0 size scomp,
but not saved in edev->component[i].scratch
In this situation, edev->component[0].scratch is an invalid pointer,
when kfree it in ses_intf_remove_enclosure, a crash like above would happen
The call trace also could be other random cases when kfree cannot catch
the invalid pointer
We should not use edev->component[] array when the components count is 0
We also need check index when use edev->component[] array in
ses_enclosure_data_process
=====
Reported-by: Michal Kolar <mich.k@seznam.cz>
Originally-by: Ding Hui <dinghui@sangfor.com.cn>
Cc: stable@vger.kernel.org
Fixes: 3fe97ff3d949 ("scsi: ses: Don't attach if enclosure has no components")
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Link: https://lore.kernel.org/r/nycvar.YFH.7.76.2304042122270.29760@cbobk.fhfr.pm
Tested-by: Michal Kolar <mich.k@seznam.cz>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-04-04 22:23:42 +03:00
|
|
|
else
|
|
|
|
ecomp = ERR_PTR(-EINVAL);
|
2009-08-01 04:43:59 +04:00
|
|
|
|
2014-12-31 01:46:14 +03:00
|
|
|
if (!IS_ERR(ecomp)) {
|
2023-02-02 19:24:49 +03:00
|
|
|
if (addl_desc_ptr) {
|
|
|
|
max_desc_len = ses_dev->page10_len -
|
|
|
|
(addl_desc_ptr - ses_dev->page10);
|
|
|
|
if (ses_process_descriptor(ecomp,
|
|
|
|
addl_desc_ptr,
|
|
|
|
max_desc_len))
|
|
|
|
addl_desc_ptr = NULL;
|
|
|
|
}
|
2014-12-31 01:46:14 +03:00
|
|
|
if (create)
|
|
|
|
enclosure_component_register(
|
|
|
|
ecomp);
|
|
|
|
}
|
2009-08-01 04:43:59 +04:00
|
|
|
}
|
|
|
|
if (desc_ptr)
|
|
|
|
desc_ptr += len;
|
|
|
|
|
2015-12-11 20:16:38 +03:00
|
|
|
if (addl_desc_ptr &&
|
|
|
|
/* only find additional descriptions for specific devices */
|
|
|
|
(type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
|
|
|
|
type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE ||
|
|
|
|
type_ptr[0] == ENCLOSURE_COMPONENT_SAS_EXPANDER ||
|
|
|
|
/* these elements are optional */
|
|
|
|
type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_TARGET_PORT ||
|
|
|
|
type_ptr[0] == ENCLOSURE_COMPONENT_SCSI_INITIATOR_PORT ||
|
2023-02-02 19:24:48 +03:00
|
|
|
type_ptr[0] == ENCLOSURE_COMPONENT_CONTROLLER_ELECTRONICS)) {
|
2009-08-01 04:43:59 +04:00
|
|
|
addl_desc_ptr += addl_desc_ptr[1] + 2;
|
2023-02-02 19:24:48 +03:00
|
|
|
if (addl_desc_ptr + 1 >= ses_dev->page10 + ses_dev->page10_len)
|
|
|
|
addl_desc_ptr = NULL;
|
|
|
|
}
|
2009-08-01 04:43:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
kfree(buf);
|
|
|
|
kfree(hdr_buf);
|
|
|
|
}
|
|
|
|
|
2008-02-04 00:48:56 +03:00
|
|
|
static void ses_match_to_enclosure(struct enclosure_device *edev,
|
2017-11-14 02:48:04 +03:00
|
|
|
struct scsi_device *sdev,
|
|
|
|
int refresh)
|
2008-02-04 00:48:56 +03:00
|
|
|
{
|
2017-11-14 02:48:04 +03:00
|
|
|
struct scsi_device *edev_sdev = to_scsi_device(edev->edev.parent);
|
2008-02-04 00:48:56 +03:00
|
|
|
struct efd efd = {
|
|
|
|
.addr = 0,
|
|
|
|
};
|
|
|
|
|
2017-11-14 02:48:04 +03:00
|
|
|
if (refresh)
|
|
|
|
ses_enclosure_data_process(edev, edev_sdev, 0);
|
2009-08-01 04:43:59 +04:00
|
|
|
|
2017-01-10 00:33:36 +03:00
|
|
|
if (scsi_is_sas_rphy(sdev->sdev_target->dev.parent))
|
2015-12-09 23:56:07 +03:00
|
|
|
efd.addr = sas_get_address(sdev);
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2014-03-15 12:51:51 +04:00
|
|
|
if (efd.addr) {
|
|
|
|
efd.dev = &sdev->sdev_gendev;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2014-03-15 12:51:51 +04:00
|
|
|
enclosure_for_each_device(ses_enclosure_find_by_addr, &efd);
|
|
|
|
}
|
2008-02-04 00:48:56 +03:00
|
|
|
}
|
|
|
|
|
2008-02-22 02:13:36 +03:00
|
|
|
static int ses_intf_add(struct device *cdev,
|
2008-02-04 00:48:56 +03:00
|
|
|
struct class_interface *intf)
|
|
|
|
{
|
2008-02-22 02:13:36 +03:00
|
|
|
struct scsi_device *sdev = to_scsi_device(cdev->parent);
|
2008-02-04 00:48:56 +03:00
|
|
|
struct scsi_device *tmp_sdev;
|
2017-08-15 11:21:42 +03:00
|
|
|
unsigned char *buf = NULL, *hdr_buf, *type_ptr, page;
|
2008-02-04 00:48:56 +03:00
|
|
|
struct ses_device *ses_dev;
|
|
|
|
u32 result;
|
2009-08-01 04:43:59 +04:00
|
|
|
int i, types, len, components = 0;
|
2008-02-04 00:48:56 +03:00
|
|
|
int err = -ENOMEM;
|
2011-03-18 18:24:23 +03:00
|
|
|
int num_enclosures;
|
2008-02-04 00:48:56 +03:00
|
|
|
struct enclosure_device *edev;
|
2008-02-11 10:25:25 +03:00
|
|
|
struct ses_component *scomp = NULL;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
if (!scsi_device_enclosure(sdev)) {
|
|
|
|
/* not an enclosure, but might be in one */
|
2009-08-01 04:39:36 +04:00
|
|
|
struct enclosure_device *prev = NULL;
|
|
|
|
|
|
|
|
while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) {
|
2017-11-14 02:48:04 +03:00
|
|
|
ses_match_to_enclosure(edev, sdev, 1);
|
2009-08-01 04:39:36 +04:00
|
|
|
prev = edev;
|
2008-02-04 00:48:56 +03:00
|
|
|
}
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TYPE_ENCLOSURE prints a message in probe */
|
|
|
|
if (sdev->type != TYPE_ENCLOSURE)
|
|
|
|
sdev_printk(KERN_NOTICE, sdev, "Embedded Enclosure Device\n");
|
|
|
|
|
|
|
|
ses_dev = kzalloc(sizeof(*ses_dev), GFP_KERNEL);
|
|
|
|
hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL);
|
|
|
|
if (!hdr_buf || !ses_dev)
|
|
|
|
goto err_init_free;
|
|
|
|
|
2017-08-15 11:21:42 +03:00
|
|
|
page = 1;
|
|
|
|
result = ses_recv_diag(sdev, page, hdr_buf, INIT_ALLOC_SIZE);
|
2008-02-04 00:48:56 +03:00
|
|
|
if (result)
|
|
|
|
goto recv_failed;
|
|
|
|
|
|
|
|
len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
|
|
|
|
buf = kzalloc(len, GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
|
|
goto err_free;
|
|
|
|
|
2017-08-15 11:21:42 +03:00
|
|
|
result = ses_recv_diag(sdev, page, buf, len);
|
2008-02-04 00:48:56 +03:00
|
|
|
if (result)
|
|
|
|
goto recv_failed;
|
|
|
|
|
2011-03-18 18:24:23 +03:00
|
|
|
types = 0;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2011-03-18 18:24:23 +03:00
|
|
|
/* we always have one main enclosure and the rest are referred
|
|
|
|
* to as secondary subenclosures */
|
|
|
|
num_enclosures = buf[1] + 1;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2011-03-18 18:24:23 +03:00
|
|
|
/* begin at the enclosure descriptor */
|
|
|
|
type_ptr = buf + 8;
|
|
|
|
/* skip all the enclosure descriptors */
|
|
|
|
for (i = 0; i < num_enclosures && type_ptr < buf + len; i++) {
|
|
|
|
types += type_ptr[2];
|
|
|
|
type_ptr += type_ptr[3] + 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
ses_dev->page1_types = type_ptr;
|
|
|
|
ses_dev->page1_num_types = types;
|
|
|
|
|
|
|
|
for (i = 0; i < types && type_ptr < buf + len; i++, type_ptr += 4) {
|
2008-02-04 00:48:56 +03:00
|
|
|
if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
|
|
|
|
type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE)
|
|
|
|
components += type_ptr[1];
|
|
|
|
}
|
2020-11-29 02:27:21 +03:00
|
|
|
|
2008-02-11 10:25:25 +03:00
|
|
|
ses_dev->page1 = buf;
|
|
|
|
ses_dev->page1_len = len;
|
|
|
|
buf = NULL;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2017-08-15 11:21:42 +03:00
|
|
|
page = 2;
|
|
|
|
result = ses_recv_diag(sdev, page, hdr_buf, INIT_ALLOC_SIZE);
|
2008-02-04 00:48:56 +03:00
|
|
|
if (result)
|
2017-08-15 11:21:43 +03:00
|
|
|
goto page2_not_supported;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
|
|
|
|
buf = kzalloc(len, GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
|
|
goto err_free;
|
|
|
|
|
|
|
|
/* make sure getting page 2 actually works */
|
|
|
|
result = ses_recv_diag(sdev, 2, buf, len);
|
|
|
|
if (result)
|
|
|
|
goto recv_failed;
|
|
|
|
ses_dev->page2 = buf;
|
|
|
|
ses_dev->page2_len = len;
|
2008-02-11 10:25:25 +03:00
|
|
|
buf = NULL;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
/* The additional information page --- allows us
|
|
|
|
* to match up the devices */
|
2017-08-15 11:21:42 +03:00
|
|
|
page = 10;
|
|
|
|
result = ses_recv_diag(sdev, page, hdr_buf, INIT_ALLOC_SIZE);
|
2008-02-14 03:25:16 +03:00
|
|
|
if (!result) {
|
|
|
|
|
|
|
|
len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
|
|
|
|
buf = kzalloc(len, GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
|
|
goto err_free;
|
|
|
|
|
2017-08-15 11:21:42 +03:00
|
|
|
result = ses_recv_diag(sdev, page, buf, len);
|
2008-02-14 03:25:16 +03:00
|
|
|
if (result)
|
|
|
|
goto recv_failed;
|
|
|
|
ses_dev->page10 = buf;
|
|
|
|
ses_dev->page10_len = len;
|
|
|
|
buf = NULL;
|
|
|
|
}
|
2017-08-15 11:21:43 +03:00
|
|
|
page2_not_supported:
|
scsi: ses: Handle enclosure with just a primary component gracefully
commit c8e22b7a1694bb8d025ea636816472739d859145 upstream.
This reverts commit 3fe97ff3d949 ("scsi: ses: Don't attach if enclosure
has no components") and introduces proper handling of case where there are
no detected secondary components, but primary component (enumerated in
num_enclosures) does exist. That fix was originally proposed by Ding Hui
<dinghui@sangfor.com.cn>.
Completely ignoring devices that have one primary enclosure and no
secondary one results in ses_intf_add() bailing completely
scsi 2:0:0:254: enclosure has no enumerated components
scsi 2:0:0:254: Failed to bind enclosure -12ven in valid configurations such
even on valid configurations with 1 primary and 0 secondary enclosures as
below:
# sg_ses /dev/sg0
3PARdata SES 3321
Supported diagnostic pages:
Supported Diagnostic Pages [sdp] [0x0]
Configuration (SES) [cf] [0x1]
Short Enclosure Status (SES) [ses] [0x8]
# sg_ses -p cf /dev/sg0
3PARdata SES 3321
Configuration diagnostic page:
number of secondary subenclosures: 0
generation code: 0x0
enclosure descriptor list
Subenclosure identifier: 0 [primary]
relative ES process id: 0, number of ES processes: 1
number of type descriptor headers: 1
enclosure logical identifier (hex): 20000002ac02068d
enclosure vendor: 3PARdata product: VV rev: 3321
type descriptor header and text list
Element type: Unspecified, subenclosure id: 0
number of possible elements: 1
The changelog for the original fix follows
=====
We can get a crash when disconnecting the iSCSI session,
the call trace like this:
[ffff00002a00fb70] kfree at ffff00000830e224
[ffff00002a00fba0] ses_intf_remove at ffff000001f200e4
[ffff00002a00fbd0] device_del at ffff0000086b6a98
[ffff00002a00fc50] device_unregister at ffff0000086b6d58
[ffff00002a00fc70] __scsi_remove_device at ffff00000870608c
[ffff00002a00fca0] scsi_remove_device at ffff000008706134
[ffff00002a00fcc0] __scsi_remove_target at ffff0000087062e4
[ffff00002a00fd10] scsi_remove_target at ffff0000087064c0
[ffff00002a00fd70] __iscsi_unbind_session at ffff000001c872c4
[ffff00002a00fdb0] process_one_work at ffff00000810f35c
[ffff00002a00fe00] worker_thread at ffff00000810f648
[ffff00002a00fe70] kthread at ffff000008116e98
In ses_intf_add, components count could be 0, and kcalloc 0 size scomp,
but not saved in edev->component[i].scratch
In this situation, edev->component[0].scratch is an invalid pointer,
when kfree it in ses_intf_remove_enclosure, a crash like above would happen
The call trace also could be other random cases when kfree cannot catch
the invalid pointer
We should not use edev->component[] array when the components count is 0
We also need check index when use edev->component[] array in
ses_enclosure_data_process
=====
Reported-by: Michal Kolar <mich.k@seznam.cz>
Originally-by: Ding Hui <dinghui@sangfor.com.cn>
Cc: stable@vger.kernel.org
Fixes: 3fe97ff3d949 ("scsi: ses: Don't attach if enclosure has no components")
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Link: https://lore.kernel.org/r/nycvar.YFH.7.76.2304042122270.29760@cbobk.fhfr.pm
Tested-by: Michal Kolar <mich.k@seznam.cz>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-04-04 22:23:42 +03:00
|
|
|
if (components > 0) {
|
|
|
|
scomp = kcalloc(components, sizeof(struct ses_component), GFP_KERNEL);
|
|
|
|
if (!scomp)
|
|
|
|
goto err_free;
|
|
|
|
}
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2008-12-04 00:41:36 +03:00
|
|
|
edev = enclosure_register(cdev->parent, dev_name(&sdev->sdev_gendev),
|
2008-02-04 00:48:56 +03:00
|
|
|
components, &ses_enclosure_callbacks);
|
|
|
|
if (IS_ERR(edev)) {
|
|
|
|
err = PTR_ERR(edev);
|
|
|
|
goto err_free;
|
|
|
|
}
|
|
|
|
|
2010-03-11 02:20:42 +03:00
|
|
|
kfree(hdr_buf);
|
|
|
|
|
2008-02-04 00:48:56 +03:00
|
|
|
edev->scratch = ses_dev;
|
|
|
|
for (i = 0; i < components; i++)
|
2008-02-11 10:25:25 +03:00
|
|
|
edev->component[i].scratch = scomp + i;
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2009-08-01 04:43:59 +04:00
|
|
|
ses_enclosure_data_process(edev, sdev, 1);
|
2008-02-04 00:48:56 +03:00
|
|
|
|
|
|
|
/* see if there are any devices matching before
|
|
|
|
* we found the enclosure */
|
|
|
|
shost_for_each_device(tmp_sdev, sdev->host) {
|
|
|
|
if (tmp_sdev->lun != 0 || scsi_device_enclosure(tmp_sdev))
|
|
|
|
continue;
|
2017-11-14 02:48:04 +03:00
|
|
|
ses_match_to_enclosure(edev, tmp_sdev, 0);
|
2008-02-04 00:48:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
recv_failed:
|
|
|
|
sdev_printk(KERN_ERR, sdev, "Failed to get diagnostic page 0x%x\n",
|
2017-08-15 11:21:42 +03:00
|
|
|
page);
|
2008-02-04 00:48:56 +03:00
|
|
|
err = -ENODEV;
|
|
|
|
err_free:
|
|
|
|
kfree(buf);
|
2008-02-11 10:25:25 +03:00
|
|
|
kfree(scomp);
|
2008-02-04 00:48:56 +03:00
|
|
|
kfree(ses_dev->page10);
|
|
|
|
kfree(ses_dev->page2);
|
|
|
|
kfree(ses_dev->page1);
|
|
|
|
err_init_free:
|
|
|
|
kfree(ses_dev);
|
|
|
|
kfree(hdr_buf);
|
|
|
|
sdev_printk(KERN_ERR, sdev, "Failed to bind enclosure %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ses_remove(struct device *dev)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-08-01 04:41:22 +04:00
|
|
|
static void ses_intf_remove_component(struct scsi_device *sdev)
|
|
|
|
{
|
|
|
|
struct enclosure_device *edev, *prev = NULL;
|
|
|
|
|
|
|
|
while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) {
|
|
|
|
prev = edev;
|
|
|
|
if (!enclosure_remove_device(edev, &sdev->sdev_gendev))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (edev)
|
|
|
|
put_device(&edev->edev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ses_intf_remove_enclosure(struct scsi_device *sdev)
|
2008-02-04 00:48:56 +03:00
|
|
|
{
|
|
|
|
struct enclosure_device *edev;
|
|
|
|
struct ses_device *ses_dev;
|
|
|
|
|
2009-08-01 04:39:36 +04:00
|
|
|
/* exact match to this enclosure */
|
2009-08-01 04:41:22 +04:00
|
|
|
edev = enclosure_find(&sdev->sdev_gendev, NULL);
|
2008-02-04 00:48:56 +03:00
|
|
|
if (!edev)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ses_dev = edev->scratch;
|
|
|
|
edev->scratch = NULL;
|
|
|
|
|
2008-02-11 10:25:25 +03:00
|
|
|
kfree(ses_dev->page10);
|
2008-02-04 00:48:56 +03:00
|
|
|
kfree(ses_dev->page1);
|
|
|
|
kfree(ses_dev->page2);
|
|
|
|
kfree(ses_dev);
|
|
|
|
|
2023-02-02 19:24:51 +03:00
|
|
|
if (edev->components)
|
|
|
|
kfree(edev->component[0].scratch);
|
2008-02-04 00:48:56 +03:00
|
|
|
|
2008-02-22 02:13:36 +03:00
|
|
|
put_device(&edev->edev);
|
2017-08-24 16:13:52 +03:00
|
|
|
enclosure_unregister(edev);
|
2008-02-04 00:48:56 +03:00
|
|
|
}
|
|
|
|
|
2009-08-01 04:41:22 +04:00
|
|
|
static void ses_intf_remove(struct device *cdev,
|
|
|
|
struct class_interface *intf)
|
|
|
|
{
|
|
|
|
struct scsi_device *sdev = to_scsi_device(cdev->parent);
|
|
|
|
|
|
|
|
if (!scsi_device_enclosure(sdev))
|
|
|
|
ses_intf_remove_component(sdev);
|
|
|
|
else
|
|
|
|
ses_intf_remove_enclosure(sdev);
|
|
|
|
}
|
|
|
|
|
2008-02-04 00:48:56 +03:00
|
|
|
static struct class_interface ses_interface = {
|
2008-02-22 02:13:36 +03:00
|
|
|
.add_dev = ses_intf_add,
|
|
|
|
.remove_dev = ses_intf_remove,
|
2008-02-04 00:48:56 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct scsi_driver ses_template = {
|
|
|
|
.gendrv = {
|
|
|
|
.name = "ses",
|
2014-11-12 20:34:51 +03:00
|
|
|
.owner = THIS_MODULE,
|
2008-02-04 00:48:56 +03:00
|
|
|
.probe = ses_probe,
|
|
|
|
.remove = ses_remove,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init ses_init(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = scsi_register_interface(&ses_interface);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = scsi_register_driver(&ses_template.gendrv);
|
|
|
|
if (err)
|
|
|
|
goto out_unreg;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_unreg:
|
|
|
|
scsi_unregister_interface(&ses_interface);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit ses_exit(void)
|
|
|
|
{
|
|
|
|
scsi_unregister_driver(&ses_template.gendrv);
|
|
|
|
scsi_unregister_interface(&ses_interface);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(ses_init);
|
|
|
|
module_exit(ses_exit);
|
|
|
|
|
|
|
|
MODULE_ALIAS_SCSI_DEVICE(TYPE_ENCLOSURE);
|
|
|
|
|
|
|
|
MODULE_AUTHOR("James Bottomley");
|
|
|
|
MODULE_DESCRIPTION("SCSI Enclosure Services (ses) driver");
|
|
|
|
MODULE_LICENSE("GPL v2");
|