Commit f30664e2 authored by Sebastian Ott's avatar Sebastian Ott Committed by Martin Schwidefsky

s390: add scm block driver

Block device driver for Storage Class Memory (SCM). This driver
provides a block device interface for each available SCM increment.
Signed-off-by: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 2e73c2cf
...@@ -70,3 +70,14 @@ config DASD_EER ...@@ -70,3 +70,14 @@ config DASD_EER
This driver provides a character device interface to the This driver provides a character device interface to the
DASD extended error reporting. This is only needed if you want to DASD extended error reporting. This is only needed if you want to
use applications written for the EER facility. use applications written for the EER facility.
config SCM_BLOCK
def_tristate m
prompt "Support for Storage Class Memory"
depends on S390 && BLOCK && EADM_SCH && SCM_BUS
help
Block device driver for Storage Class Memory (SCM). This driver
provides a block device interface for each available SCM increment.
To compile this driver as a module, choose M here: the
module will be called scm_block.
...@@ -17,3 +17,6 @@ obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o ...@@ -17,3 +17,6 @@ obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o
obj-$(CONFIG_DASD_FBA) += dasd_fba_mod.o obj-$(CONFIG_DASD_FBA) += dasd_fba_mod.o
obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
obj-$(CONFIG_DCSSBLK) += dcssblk.o obj-$(CONFIG_DCSSBLK) += dcssblk.o
scm_block-objs := scm_drv.o scm_blk.o
obj-$(CONFIG_SCM_BLOCK) += scm_block.o
This diff is collapsed.
#ifndef SCM_BLK_H
#define SCM_BLK_H
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/blkdev.h>
#include <linux/genhd.h>
#include <linux/list.h>
#include <asm/debug.h>
#include <asm/eadm.h>
#define SCM_NR_PARTS 8
#define SCM_QUEUE_DELAY 5
struct scm_blk_dev {
struct tasklet_struct tasklet;
struct request_queue *rq;
struct gendisk *gendisk;
struct scm_device *scmdev;
spinlock_t rq_lock; /* guard the request queue */
spinlock_t lock; /* guard the rest of the blockdev */
atomic_t queued_reqs;
struct list_head finished_requests;
};
struct scm_request {
struct scm_blk_dev *bdev;
struct request *request;
struct aidaw *aidaw;
struct aob *aob;
struct list_head list;
u8 retries;
int error;
};
#define to_aobrq(rq) container_of((void *) rq, struct aob_rq_header, data)
int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);
void scm_blk_dev_cleanup(struct scm_blk_dev *);
void scm_blk_irq(struct scm_device *, void *, int);
int scm_drv_init(void);
void scm_drv_cleanup(void);
extern debug_info_t *scm_debug;
#define SCM_LOG(imp, txt) do { \
debug_text_event(scm_debug, imp, txt); \
} while (0)
static inline void SCM_LOG_HEX(int level, void *data, int length)
{
if (level > scm_debug->level)
return;
while (length > 0) {
debug_event(scm_debug, level, data, length);
length -= scm_debug->buf_size;
data += scm_debug->buf_size;
}
}
static inline void SCM_LOG_STATE(int level, struct scm_device *scmdev)
{
struct {
u64 address;
u8 oper_state;
u8 rank;
} __packed data = {
.address = scmdev->address,
.oper_state = scmdev->attrs.oper_state,
.rank = scmdev->attrs.rank,
};
SCM_LOG_HEX(level, &data, sizeof(data));
}
#endif /* SCM_BLK_H */
/*
* Device driver for s390 storage class memory.
*
* Copyright IBM Corp. 2012
* Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
*/
#define KMSG_COMPONENT "scm_block"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <asm/eadm.h>
#include "scm_blk.h"
static void notify(struct scm_device *scmdev)
{
pr_info("%lu: The capabilities of the SCM increment changed\n",
(unsigned long) scmdev->address);
SCM_LOG(2, "State changed");
SCM_LOG_STATE(2, scmdev);
}
static int scm_probe(struct scm_device *scmdev)
{
struct scm_blk_dev *bdev;
int ret;
SCM_LOG(2, "probe");
SCM_LOG_STATE(2, scmdev);
if (scmdev->attrs.oper_state != OP_STATE_GOOD)
return -EINVAL;
bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
if (!bdev)
return -ENOMEM;
spin_lock_irq(&scmdev->lock);
dev_set_drvdata(&scmdev->dev, bdev);
spin_unlock_irq(&scmdev->lock);
ret = scm_blk_dev_setup(bdev, scmdev);
if (ret) {
spin_lock_irq(&scmdev->lock);
dev_set_drvdata(&scmdev->dev, NULL);
spin_unlock_irq(&scmdev->lock);
kfree(bdev);
goto out;
}
out:
return ret;
}
static int scm_remove(struct scm_device *scmdev)
{
struct scm_blk_dev *bdev;
spin_lock_irq(&scmdev->lock);
bdev = dev_get_drvdata(&scmdev->dev);
dev_set_drvdata(&scmdev->dev, NULL);
spin_unlock_irq(&scmdev->lock);
scm_blk_dev_cleanup(bdev);
kfree(bdev);
return 0;
}
static struct scm_driver scm_drv = {
.drv = {
.name = "scm_block",
.owner = THIS_MODULE,
},
.notify = notify,
.probe = scm_probe,
.remove = scm_remove,
.handler = scm_blk_irq,
};
int __init scm_drv_init(void)
{
return scm_driver_register(&scm_drv);
}
void scm_drv_cleanup(void)
{
scm_driver_unregister(&scm_drv);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment