summaryrefslogtreecommitdiff
path: root/drivers/s390/block/dasd_eckd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/block/dasd_eckd.c')
-rw-r--r--drivers/s390/block/dasd_eckd.c175
1 files changed, 117 insertions, 58 deletions
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 758ee4153ac1..3caa1ee5f4b0 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -103,7 +103,7 @@ struct ext_pool_exhaust_work_data {
};
/* definitions for the path verification worker */
-struct path_verification_work_data {
+struct pe_handler_work_data {
struct work_struct worker;
struct dasd_device *device;
struct dasd_ccw_req cqr;
@@ -111,9 +111,10 @@ struct path_verification_work_data {
__u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE];
int isglobal;
__u8 tbvpm;
+ __u8 fcsecpm;
};
-static struct path_verification_work_data *path_verification_worker;
-static DEFINE_MUTEX(dasd_path_verification_mutex);
+static struct pe_handler_work_data *pe_handler_worker;
+static DEFINE_MUTEX(dasd_pe_handler_mutex);
struct check_attention_work_data {
struct work_struct worker;
@@ -143,7 +144,7 @@ dasd_eckd_probe (struct ccw_device *cdev)
"ccw-device options");
return ret;
}
- ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
+ ret = dasd_generic_probe(cdev);
return ret;
}
@@ -1000,6 +1001,27 @@ static unsigned char dasd_eckd_path_access(void *conf_data, int conf_len)
return 0;
}
+static void dasd_eckd_store_conf_data(struct dasd_device *device,
+ struct dasd_conf_data *conf_data, int chp)
+{
+ struct channel_path_desc_fmt0 *chp_desc;
+ struct subchannel_id sch_id;
+
+ ccw_device_get_schid(device->cdev, &sch_id);
+ /*
+ * path handling and read_conf allocate data
+ * free it before replacing the pointer
+ */
+ kfree(device->path[chp].conf_data);
+ device->path[chp].conf_data = conf_data;
+ device->path[chp].cssid = sch_id.cssid;
+ device->path[chp].ssid = sch_id.ssid;
+ chp_desc = ccw_device_get_chp_desc(device->cdev, chp);
+ if (chp_desc)
+ device->path[chp].chpid = chp_desc->chpid;
+ kfree(chp_desc);
+}
+
static void dasd_eckd_clear_conf_data(struct dasd_device *device)
{
struct dasd_eckd_private *private = device->private;
@@ -1013,9 +1035,33 @@ static void dasd_eckd_clear_conf_data(struct dasd_device *device)
device->path[i].cssid = 0;
device->path[i].ssid = 0;
device->path[i].chpid = 0;
+ dasd_path_notoper(device, i);
+ dasd_path_remove_kobj(device, i);
}
}
+static void dasd_eckd_read_fc_security(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private = device->private;
+ u8 esm_valid;
+ u8 esm[8];
+ int chp;
+ int rc;
+
+ rc = chsc_scud(private->uid.ssid, (u64 *)esm, &esm_valid);
+ if (rc) {
+ for (chp = 0; chp < 8; chp++)
+ device->path[chp].fc_security = 0;
+ return;
+ }
+
+ for (chp = 0; chp < 8; chp++) {
+ if (esm_valid & (0x80 >> chp))
+ device->path[chp].fc_security = esm[chp];
+ else
+ device->path[chp].fc_security = 0;
+ }
+}
static int dasd_eckd_read_conf(struct dasd_device *device)
{
@@ -1026,12 +1072,9 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
struct dasd_eckd_private *private, path_private;
struct dasd_uid *uid;
char print_path_uid[60], print_device_uid[60];
- struct channel_path_desc_fmt0 *chp_desc;
- struct subchannel_id sch_id;
private = device->private;
opm = ccw_device_get_path_mask(device->cdev);
- ccw_device_get_schid(device->cdev, &sch_id);
conf_data_saved = 0;
path_err = 0;
/* get configuration data per operational path */
@@ -1066,15 +1109,6 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
kfree(conf_data);
continue;
}
- pos = pathmask_to_pos(lpm);
- /* store per path conf_data */
- device->path[pos].conf_data = conf_data;
- device->path[pos].cssid = sch_id.cssid;
- device->path[pos].ssid = sch_id.ssid;
- chp_desc = ccw_device_get_chp_desc(device->cdev, pos);
- if (chp_desc)
- device->path[pos].chpid = chp_desc->chpid;
- kfree(chp_desc);
/*
* build device UID that other path data
* can be compared to it
@@ -1132,18 +1166,13 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
dasd_path_add_cablepm(device, lpm);
continue;
}
- pos = pathmask_to_pos(lpm);
- /* store per path conf_data */
- device->path[pos].conf_data = conf_data;
- device->path[pos].cssid = sch_id.cssid;
- device->path[pos].ssid = sch_id.ssid;
- chp_desc = ccw_device_get_chp_desc(device->cdev, pos);
- if (chp_desc)
- device->path[pos].chpid = chp_desc->chpid;
- kfree(chp_desc);
path_private.conf_data = NULL;
path_private.conf_len = 0;
}
+
+ pos = pathmask_to_pos(lpm);
+ dasd_eckd_store_conf_data(device, conf_data, pos);
+
switch (dasd_eckd_path_access(conf_data, conf_len)) {
case 0x02:
dasd_path_add_nppm(device, lpm);
@@ -1160,6 +1189,8 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
}
}
+ dasd_eckd_read_fc_security(device);
+
return path_err;
}
@@ -1219,7 +1250,7 @@ static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
}
static int rebuild_device_uid(struct dasd_device *device,
- struct path_verification_work_data *data)
+ struct pe_handler_work_data *data)
{
struct dasd_eckd_private *private = device->private;
__u8 lpm, opm = dasd_path_get_opm(device);
@@ -1257,31 +1288,18 @@ static int rebuild_device_uid(struct dasd_device *device,
return rc;
}
-static void do_path_verification_work(struct work_struct *work)
+static void dasd_eckd_path_available_action(struct dasd_device *device,
+ struct pe_handler_work_data *data)
{
- struct path_verification_work_data *data;
- struct dasd_device *device;
struct dasd_eckd_private path_private;
struct dasd_uid *uid;
__u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE];
__u8 lpm, opm, npm, ppm, epm, hpfpm, cablepm;
+ struct dasd_conf_data *conf_data;
unsigned long flags;
char print_uid[60];
- int rc;
-
- data = container_of(work, struct path_verification_work_data, worker);
- device = data->device;
+ int rc, pos;
- /* delay path verification until device was resumed */
- if (test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
- schedule_work(work);
- return;
- }
- /* check if path verification already running and delay if so */
- if (test_and_set_bit(DASD_FLAG_PATH_VERIFY, &device->flags)) {
- schedule_work(work);
- return;
- }
opm = 0;
npm = 0;
ppm = 0;
@@ -1397,6 +1415,14 @@ static void do_path_verification_work(struct work_struct *work)
}
}
+ conf_data = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL);
+ if (conf_data) {
+ memcpy(conf_data, data->rcd_buffer,
+ DASD_ECKD_RCD_DATA_SIZE);
+ }
+ pos = pathmask_to_pos(lpm);
+ dasd_eckd_store_conf_data(device, conf_data, pos);
+
/*
* There is a small chance that a path is lost again between
* above path verification and the following modification of
@@ -1417,34 +1443,65 @@ static void do_path_verification_work(struct work_struct *work)
dasd_path_add_cablepm(device, cablepm);
dasd_path_add_nohpfpm(device, hpfpm);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+
+ dasd_path_create_kobj(device, pos);
+ }
+}
+
+static void do_pe_handler_work(struct work_struct *work)
+{
+ struct pe_handler_work_data *data;
+ struct dasd_device *device;
+
+ data = container_of(work, struct pe_handler_work_data, worker);
+ device = data->device;
+
+ /* delay path verification until device was resumed */
+ if (test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
+ schedule_work(work);
+ return;
+ }
+ /* check if path verification already running and delay if so */
+ if (test_and_set_bit(DASD_FLAG_PATH_VERIFY, &device->flags)) {
+ schedule_work(work);
+ return;
}
+
+ if (data->tbvpm)
+ dasd_eckd_path_available_action(device, data);
+ if (data->fcsecpm)
+ dasd_eckd_read_fc_security(device);
+
clear_bit(DASD_FLAG_PATH_VERIFY, &device->flags);
dasd_put_device(device);
if (data->isglobal)
- mutex_unlock(&dasd_path_verification_mutex);
+ mutex_unlock(&dasd_pe_handler_mutex);
else
kfree(data);
}
-static int dasd_eckd_verify_path(struct dasd_device *device, __u8 lpm)
+static int dasd_eckd_pe_handler(struct dasd_device *device,
+ __u8 tbvpm, __u8 fcsecpm)
{
- struct path_verification_work_data *data;
+ struct pe_handler_work_data *data;
data = kmalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA);
if (!data) {
- if (mutex_trylock(&dasd_path_verification_mutex)) {
- data = path_verification_worker;
+ if (mutex_trylock(&dasd_pe_handler_mutex)) {
+ data = pe_handler_worker;
data->isglobal = 1;
- } else
+ } else {
return -ENOMEM;
+ }
} else {
memset(data, 0, sizeof(*data));
data->isglobal = 0;
}
- INIT_WORK(&data->worker, do_path_verification_work);
+ INIT_WORK(&data->worker, do_pe_handler_work);
dasd_get_device(device);
data->device = device;
- data->tbvpm = lpm;
+ data->tbvpm = tbvpm;
+ data->fcsecpm = fcsecpm;
schedule_work(&data->worker);
return 0;
}
@@ -2056,6 +2113,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
if (rc)
goto out_err3;
+ dasd_path_create_kobjects(device);
+
/* Read Feature Codes */
dasd_eckd_read_features(device);
@@ -6590,7 +6649,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
.check_device = dasd_eckd_check_characteristics,
.uncheck_device = dasd_eckd_uncheck_device,
.do_analysis = dasd_eckd_do_analysis,
- .verify_path = dasd_eckd_verify_path,
+ .pe_handler = dasd_eckd_pe_handler,
.basic_to_ready = dasd_eckd_basic_to_ready,
.online_to_ready = dasd_eckd_online_to_ready,
.basic_to_known = dasd_eckd_basic_to_known,
@@ -6649,16 +6708,16 @@ dasd_eckd_init(void)
GFP_KERNEL | GFP_DMA);
if (!dasd_vol_info_req)
return -ENOMEM;
- path_verification_worker = kmalloc(sizeof(*path_verification_worker),
- GFP_KERNEL | GFP_DMA);
- if (!path_verification_worker) {
+ pe_handler_worker = kmalloc(sizeof(*pe_handler_worker),
+ GFP_KERNEL | GFP_DMA);
+ if (!pe_handler_worker) {
kfree(dasd_reserve_req);
kfree(dasd_vol_info_req);
return -ENOMEM;
}
rawpadpage = (void *)__get_free_page(GFP_KERNEL);
if (!rawpadpage) {
- kfree(path_verification_worker);
+ kfree(pe_handler_worker);
kfree(dasd_reserve_req);
kfree(dasd_vol_info_req);
return -ENOMEM;
@@ -6667,7 +6726,7 @@ dasd_eckd_init(void)
if (!ret)
wait_for_device_probe();
else {
- kfree(path_verification_worker);
+ kfree(pe_handler_worker);
kfree(dasd_reserve_req);
kfree(dasd_vol_info_req);
free_page((unsigned long)rawpadpage);
@@ -6679,7 +6738,7 @@ static void __exit
dasd_eckd_cleanup(void)
{
ccw_driver_unregister(&dasd_eckd_driver);
- kfree(path_verification_worker);
+ kfree(pe_handler_worker);
kfree(dasd_reserve_req);
free_page((unsigned long)rawpadpage);
}