Base compatibility support for Intel(R) Smart Response Technology
Subsystems (2)
cover Base compatibility support for Intel(R) Smart Response Technology
Initial request for comment on kernel support for these platform-firmware defined SSD + HDD cache configurations shipping on recent platforms. Note, mdadm support to enumerate/assemble these arrays is included in a follow on patchset.
The largest architectural difference over the existing "imsm" external metadata support is a new md personality "isrt" that handles the cache-specific metadata. The base metadata required to assemble the volume is handled in userspace. The next major difference is that these configurations span "containers". In the standard cache configuration two single-drive-raid0 volumes (from separate containers) are associated into a cached volume. The diagram below attempts to make this clearer.
+-----------------------+ +----------------------+
| sda | SSD | sdb | HDD
| +-------------------+ | | +------------------+ |
| | /dev/md/imsm0 | | Container0 | | /dev/md/imsm1 | | Container1
| | +---------------+ | | | | +--------------+ | |
| | | /dev/md/vol0 | | | RAID Volume0 | | | /dev/md/vol1 | | | RAID Volume1
| | | +---------+ | | | | | | +----------+ | | |
| | | |SRT Cache| | | | | | | |SRT Target| | | |
+-+-+--+----+----+--+-+-+ +-+-+-+----+-----+-+-+-+
| |
| |
| HDD Cached by SSD |
| +--------------+ |
+-----------+ /dev/md/isrt +----------+
+--------------+ "Base compatibility" refers to the fact that this implementation does not target performance. Instead, this simply provides the ability to perform cache coherent reads and writes. For example, cache-inserts are never performed. The only case where we update the cache metadata is dirtying an existing frame in the cache, otherwise the write is directed to the HDD. Reads are decomposed into portions that can be routed and wholly consumed by the SSD or the HDD (directed by the cache metadata).
The primary motivation for this release is dual-boot compatibility. Whether this support is paired with existing multi-device cache policy engines in the kernel for a performance oriented use case is TBD.
Existing bug reports and feature requests:
https://bugzilla.redhat.com/show_bug.cgi?id=890881
https://bugzilla.redhat.com/show_bug.cgi?id=1040362
To simplify testing these patches are also available via github, but be warned this tree will rebase in response to review feedback:
git://github.com/djbw/linux isrt
---
Dan Williams (3):
md/isrt: base infrastructure and metadata loading md/isrt: read support
md/isrt: write support
drivers/md/Kconfig | 18 + drivers/md/Makefile | 1 drivers/md/isrt.c | 745 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/md/isrt.h | 300 +++++++++++++++++++++ 4 files changed, 1064 insertions(+), 0 deletions(-) create mode 100644 drivers/md/isrt.c create mode 100644 drivers/md/isrt.h
RFC PATCH 1/3 md/isrt: base infrastructure and metadata loading
Initial md / block boilerplate for the Intel (R) Smart Response Technology compatibility driver. Supports reading the packed metadata and parsing it into a cache lookup tree.
--- drivers/md/Kconfig | 18 ++ drivers/md/Makefile | 1 drivers/md/isrt.c | 524 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/md/isrt.h | 290 ++++++++++++++++++++++++++++ 4 files changed, 833 insertions(+), 0 deletions(-) create mode 100644 drivers/md/isrt.c create mode 100644 drivers/md/isrt.h
drivers/md/Kconfig +18 -0
@@ -174,6 +174,24 @@ config MD_FAULTY In unsure, say N. +config MD_INTEL_SRT + tristate "Intel (R) Smart Response Technology support" + depends on BLK_DEV_MD + help + Basic compatibility support for Intel(R) Smart Response + Technology arrays. These arrays include a HDD fronted by an SSD. + This driver enables basic compatibility for parsing the metadata and + directing reads to the most up-to-date version of the data (if it is + cached on the SSD). For writes the driver simply writes the data back + to the HDD (if it is dirty in the SSD) and then invalidates the blocks + in the metadata. It never inserts new dirty data into the cache. + + Note, component members of an isrt volume are imsm raid volumes, you + should enable at least MD_RAID0 before mdadm will be able to assemble + an isrt volume. + + If unsure, say N. + source "drivers/md/bcache/Kconfig" config BLK_DEV_DM_BUILTIN
drivers/md/Makefile +1 -0
@@ -30,6 +30,7 @@ obj-$(CONFIG_MD_RAID10) += raid10.o obj-$(CONFIG_MD_RAID456) += raid456.o obj-$(CONFIG_MD_MULTIPATH) += multipath.o obj-$(CONFIG_MD_FAULTY) += faulty.o +obj-$(CONFIG_MD_INTEL_SRT) += isrt.o obj-$(CONFIG_BCACHE) += bcache/ obj-$(CONFIG_BLK_DEV_MD) += md-mod.o obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
drivers/md/isrt.c new file +524 -0
524 lines not on this page · raw message
drivers/md/isrt.h new file +290 -0
290 lines not on this page · raw message
Re: [RFC PATCH 1/3] md/isrt: base infrastructure and metadata loading
Quoted 7 lines · On Wed, 23 Apr 2014 23:18:49 -0700 Dan Williams <[email protected]>…
Initial md / block boilerplate for the Intel (R) Smart Response Technology compatibility driver. Supports reading the packed metadata and parsing it into a cache lookup tree.
Cc Dave Jiang <[email protected]>Cc Artur Paszkiewicz <[email protected]>Signed-off-by Dan Williams <[email protected]>
It would really help in reviewing this to have a glossary.
There are frames and segments and sectors and pages.
I hope sectors are 512 bytes and pages are PAGE_SIZE, which may or may not be 4096.
And there are 16 sectors per frame, so I guess space is allocated in the cache in 8K aligned frames ??
There are 64 segments per page so if pages did happen to be 4096 bytes, that makes 64 bytes per segment. What are they?
There is a list somewhere of 32byte frame descriptors which is read into a single vmalloced region (why? you keep page pointers, so why not read it into separate pages?)
How is this organised? I might be able to work that out from the code, but I'd rather not.
Please don't make me guess, I'm not good at it.
I guess it didn't help that diff out the header after the code. I got bored before I got there and didn't read all to words, so maybe some answers are in there. They don't really stand out though.
You've chosen '8' for the 'level' number.
As this is an array which doesn't have redundancy, I'd rather a number <= 0. I think there are places where I assume >=1 has redundancy and understands spares etc.
Should conf->count be a kref??? Just a thought, not a requirement.
NeilBrown
Re: [RFC PATCH 1/3] md/isrt: base infrastructure and metadata loading
Quoted 30 lines · NeilBrown
Quoted 7 lines · On Wed, 23 Apr 2014 23:18:49 -0700 Dan Williams <[email protected]>…
Initial md / block boilerplate for the Intel (R) Smart Response Technology compatibility driver. Supports reading the packed metadata and parsing it into a cache lookup tree.
Cc Dave Jiang <[email protected]>Cc Artur Paszkiewicz <[email protected]>Signed-off-by Dan Williams <[email protected]>It would really help in reviewing this to have a glossary.
There are frames and segments and sectors and pages.
I hope sectors are 512 bytes and pages are
PAGE_SIZE, which may or may not be 4096.And there are 16 sectors per frame, so I guess space is allocated in the cache in 8K aligned frames ??
There are 64 segments per page so if pages did happen to be 4096 bytes, that makes 64 bytes per segment. What are they?
There is a list somewhere of 32byte frame descriptors which is read into a single vmalloced region (why? you keep page pointers, so why not read it into separate pages?)
How is this organised? I might be able to work that out from the code, but I'd rather not.
Quoted 5 lines
Please don't make me guess, I'm not good at it.
I guess it didn't help that diff out the header after the code. I got bored before I got there and didn't read all to words, so maybe some answers are in there. They don't really stand out though.
No, they don't. Let me throw together a proper cheat sheet.
Quoted 1 line
You've chosen '8' for the 'level' number.
Hmm, ok. I use -12 in the mdadm bits, I neglected to go back and fix up the kernel.
Quoted 5 lines
As this is an array which doesn't have redundancy, I'd rather a number <= 0. I think there are places where I assume >=1 has redundancy and understands spares etc.
Should
conf->countbe a kref??? Just a thought, not a requirement.
Doesn't kref == 0 imply object destroyed? It's a count of pending metadata events.
Re: [RFC PATCH 1/3] md/isrt: base infrastructure and metadata loading
Quoted 31 lines · On Thu, 24 Apr 2014 00:38:01 -0700 Dan Williams <[email protected]>…
Quoted 30 lines · NeilBrown
Quoted 7 lines · On Wed, 23 Apr 2014 23:18:49 -0700 Dan Williams <[email protected]>…
Initial md / block boilerplate for the Intel (R) Smart Response Technology compatibility driver. Supports reading the packed metadata and parsing it into a cache lookup tree.
Cc Dave Jiang <[email protected]>Cc Artur Paszkiewicz <[email protected]>Signed-off-by Dan Williams <[email protected]>It would really help in reviewing this to have a glossary.
There are frames and segments and sectors and pages.
I hope sectors are 512 bytes and pages are
PAGE_SIZE, which may or may not be 4096.And there are 16 sectors per frame, so I guess space is allocated in the cache in 8K aligned frames ??
There are 64 segments per page so if pages did happen to be 4096 bytes, that makes 64 bytes per segment. What are they?
There is a list somewhere of 32byte frame descriptors which is read into a single vmalloced region (why? you keep page pointers, so why not read it into separate pages?)
How is this organised? I might be able to work that out from the code, but I'd rather not.
Quoted 7 lines
Quoted 5 lines
Please don't make me guess, I'm not good at it.
I guess it didn't help that diff out the header after the code. I got bored before I got there and didn't read all to words, so maybe some answers are in there. They don't really stand out though.
No, they don't. Let me throw together a proper cheat sheet.
Thanks.
Quoted 6 lines
Quoted 2 lines
You've chosen '8' for the 'level' number.
Hmm, ok. I use -12 in the mdadm bits, I neglected to go back and fix up the kernel.
Quoted 8 lines
Quoted 5 lines
As this is an array which doesn't have redundancy, I'd rather a number <= 0. I think there are places where I assume >=1 has redundancy and understands spares etc.
Should
conf->countbe a kref??? Just a thought, not a requirement.Doesn't kref == 0 imply object destroyed? It's a count of pending metadata events.
kref means that in a kobject. Elsewhere it means whatever you want.
mpb_read_endio would kref_put(&conf->ref, release)
where release would get the conf and wake_up(&conf->eventq);
It probably isn't a big win..
NeilBrown
Re: [RFC PATCH 1/3] md/isrt: base infrastructure and metadata loading
Quoted 60 lines · NeilBrown
Quoted 31 lines · On Thu, 24 Apr 2014 00:38:01 -0700 Dan Williams <[email protected]>…
Quoted 30 lines · NeilBrown
Quoted 7 lines · On Wed, 23 Apr 2014 23:18:49 -0700 Dan Williams <[email protected]>…
Initial md / block boilerplate for the Intel (R) Smart Response Technology compatibility driver. Supports reading the packed metadata and parsing it into a cache lookup tree.
Cc Dave Jiang <[email protected]>Cc Artur Paszkiewicz <[email protected]>Signed-off-by Dan Williams <[email protected]>It would really help in reviewing this to have a glossary.
There are frames and segments and sectors and pages.
I hope sectors are 512 bytes and pages are
PAGE_SIZE, which may or may not be 4096.And there are 16 sectors per frame, so I guess space is allocated in the cache in 8K aligned frames ??
There are 64 segments per page so if pages did happen to be 4096 bytes, that makes 64 bytes per segment. What are they?
There is a list somewhere of 32byte frame descriptors which is read into a single vmalloced region (why? you keep page pointers, so why not read it into separate pages?)
How is this organised? I might be able to work that out from the code, but I'd rather not.Quoted 7 lines
Quoted 5 lines
Please don't make me guess, I'm not good at it.
I guess it didn't help that diff out the header after the code. I got bored before I got there and didn't read all to words, so maybe some answers are in there. They don't really stand out though.
No, they don't. Let me throw together a proper cheat sheet.
Thanks.
Quoted 6 lines
Quoted 2 lines
You've chosen '8' for the 'level' number.
Hmm, ok. I use -12 in the mdadm bits, I neglected to go back and fix up the kernel.
Quoted 8 lines
Quoted 5 lines
As this is an array which doesn't have redundancy, I'd rather a number <= 0. I think there are places where I assume >=1 has redundancy and understands spares etc.
Should
conf->countbe a kref??? Just a thought, not a requirement.Doesn't kref == 0 imply object destroyed? It's a count of pending metadata events.
Quoted 7 lines
kref means that in a kobject. Elsewhere it means whatever you want.
mpb_read_endiowouldkref_put(&conf->ref, release)where release would get the conf and
wake_up(&conf->eventq);It probably isn't a big win..
Yes, but I should train my brain that kref != object lifetime, it's just a ref...
...or is it?
static inline void kref_get(struct kref *kref)
{
/* If refcount was 0 before incrementing then we have a race
* condition when this kref is freeing by some other thread right now.
* In this case one should use kref_get_unless_zero()
*/
WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
}Seems we would need kref_get_zero_ok(), right?
Re: [RFC PATCH 1/3] md/isrt: base infrastructure and metadata loading
Quoted 82 lines · On Thu, 24 Apr 2014 10:33:31 -0700 Dan Williams <[email protected]>…
Quoted 60 lines · NeilBrown
Quoted 31 lines · On Thu, 24 Apr 2014 00:38:01 -0700 Dan Williams <[email protected]>…
Quoted 30 lines · NeilBrown
Quoted 7 lines · On Wed, 23 Apr 2014 23:18:49 -0700 Dan Williams <[email protected]>…
Initial md / block boilerplate for the Intel (R) Smart Response Technology compatibility driver. Supports reading the packed metadata and parsing it into a cache lookup tree.
Cc Dave Jiang <[email protected]>Cc Artur Paszkiewicz <[email protected]>Signed-off-by Dan Williams <[email protected]>It would really help in reviewing this to have a glossary.
There are frames and segments and sectors and pages.
I hope sectors are 512 bytes and pages are
PAGE_SIZE, which may or may not be 4096.And there are 16 sectors per frame, so I guess space is allocated in the cache in 8K aligned frames ??
There are 64 segments per page so if pages did happen to be 4096 bytes, that makes 64 bytes per segment. What are they?
There is a list somewhere of 32byte frame descriptors which is read into a single vmalloced region (why? you keep page pointers, so why not read it into separate pages?)
How is this organised? I might be able to work that out from the code, but I'd rather not.Quoted 7 lines
Quoted 5 lines
Please don't make me guess, I'm not good at it.
I guess it didn't help that diff out the header after the code. I got bored before I got there and didn't read all to words, so maybe some answers are in there. They don't really stand out though.
No, they don't. Let me throw together a proper cheat sheet.
Thanks.
Quoted 6 lines
Quoted 2 lines
You've chosen '8' for the 'level' number.
Hmm, ok. I use -12 in the mdadm bits, I neglected to go back and fix up the kernel.
Quoted 8 lines
Quoted 5 lines
As this is an array which doesn't have redundancy, I'd rather a number <= 0. I think there are places where I assume >=1 has redundancy and understands spares etc.
Should
conf->countbe a kref??? Just a thought, not a requirement.Doesn't kref == 0 imply object destroyed? It's a count of pending metadata events.
Quoted 7 lines
kref means that in a kobject. Elsewhere it means whatever you want.
mpb_read_endiowouldkref_put(&conf->ref, release)where release would get the conf and
wake_up(&conf->eventq);It probably isn't a big win..
Yes, but I should train my brain that kref != object lifetime, it's just a ref...
...or is it?
static inline voidkref_get(struct kref *kref){ /* If refcount was 0 before incrementing then we have a race * condition when this kref is freeing by some other thread right now. * In this case one should use kref_get_unless_zero() */ WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2); }
Quoted 1 line
Seems we would need
kref_get_zero_ok(), right?
That doesn't sound like a good idea, the only value in using kref would be if it made the code more obvious. That wouldn't :-(
So I looked more closely at the code, and now wonder why 'count' is in 'conf' at all.
You don't really need a global count of requests at all.
There two places where it is used.
One is in isrt_mpb_read() where a single bio is submitted and waited for. That could use submit_bio_wait() (not 'should', just 'could').
The other is in isrt_read_packed_md() where multiple bios are submitted and then waited for.
In both cases the counter (and wait queue) could be on the stack (like the completion is in submit_bio_wait().
struct multi_complete { atomic_t count; wait_queue_head_t wait; unsigned long state; };
static void multi_read_endio(struct bio *bio, int error) {
struct multi_complete *mc = bio->private;
if (error || !test_bit(BIO_UPTODATE(&bio->bi_flags)))
set_bit(ISRT_ERROR, &mc->state);
if (atomic_dec_and_test(&mc->count))
wake_up(&mc->wait); bio_put(bio);
}
isrt_read_packed_md(struct mddev *mdev)
{
...
struct multi_complete mc = { ATOMIC_INIT(0),
__WAIT_QUEUE_HEAD_INITIALIZER(mc.wait), 0};
....
atomic_inc(&mc,count);
bio->bi_private = &mc;
submit_bio(READ, bio);.....
wait_event(&mc.wait, atomic_read(&mc.count)==0);
....
}
It isn't really a big improvement, so maybe it isn't worth it. But it does make it obvious that we are only waiting for the reads that we just submitted. With the current code I see conf->count and wonder what else we could be waiting for.
Up to you - maybe just leave it as it is.
(But please use 'READ', not '0' as the first arg to submit_bio).
NeilBrown
Re: [RFC PATCH 1/3] md/isrt: base infrastructure and metadata loading
Quoted 1 line · NeilBrown
On Thu, 24 Apr 2014 10:33:31 -0700 Dan Williams <[email protected]>
[..]
Quoted 8 lines
It isn't really a big improvement, so maybe it isn't worth it. But it does make it obvious that we are only waiting for the reads that we just submitted. With the current code I see
conf->countand wonder what else we could be waiting for.Up to you - maybe just leave it as it is.
(But please use 'READ', not '0' as the first arg tosubmit_bio).
Will do, and for the other point I do like the removal of unnecessarily global state.
RFC PATCH 2/3 md/isrt: read support
In reply to Dan WilliamsLookup incoming read requests in the cache. Four cases to handle:
1/ If the request cross a frame boundary split it at the boundary and
recursively resubmit. (unlikely due to the fact that we tell the upper layers to send us one request at a time)
2/ If the request finds fails a frame lookup or finds all sectors
invalid in the per sector validity, route the i/o to the backing device.
3/ If the request succeeds at frame lookup and finds all sectos valid in
the per sector validity, remap the i/o to the cache frame sector and route the i/o to the cache device.
4/ If the request is a partial hit split off a sector and recurisvely
submit until case 2 or case 3 is hit.
--- drivers/md/isrt.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 136 insertions(+), 8 deletions(-)
drivers/md/isrt.c +136 -8
@@ -465,26 +465,154 @@ static int isrt_stop(struct mddev *mddev)return 0 ; } +static bool is_io_in_frame_boundary (struct bio *bio) +{ +return SECTORS_PER_FRAME >= (bio->bi_iter.bi_sector &FRAME_MASK ) + +bio_sectors (bio); +} + +static bool __is_io_cached (struct nv_cache_packed_md *frame,struct bio *bio,bool negate) +{ +u16 invalid =le16_to_cpu (frame->per_sector_validity); +int sector_idx = bio->bi_iter.bi_sector &FRAME_MASK ; +int end = sector_idx +bio_sectors (bio); +int i; + +if (WARN_ONCE (end >SECTORS_PER_FRAME ,"bio crosses frame boundary by %d sectors\n" , + end -SECTORS_PER_FRAME )) +return false; + +if (negate) + invalid = ~invalid; + +for (i = sector_idx; i < end; i++) +if (invalid & (1 << i)) +break ; + +return i >= end; +} + +static bool is_io_cached (struct nv_cache_packed_md *frame,struct bio *bio) +{ +if (!frame) +return false; +return __is_io_cached (frame, bio, false); +} + +static bool is_io_uncached (struct nv_cache_packed_md *frame,struct bio *bio) +{ +if (!frame) +return true; +return __is_io_cached (frame, bio, true); +} + +static struct bio *isrt_split (struct mddev *mddev,struct bio *bio, +sector_t sectors) +{ +struct bio *split; + +/* Sanity check -- queue functions should prevent this happening */ +if (bio->bi_vcnt !=1 || bio->bi_iter.bi_idx !=0 + ||bio_sectors (bio) ==1 ) { +pr_err ("%s: make_request bug: can't convert block across frame" +" or bigger than %dk %llu %d\n" , +mdname (mddev),SECTORS_PER_FRAME /2 , + (unsigned long long )bio->bi_iter.bi_sector,bio_sectors (bio)); +bio_io_error (bio); +return bio; + } + +pr_debug ("%s: sector: %llu split: %llu/%llu\n" , __func__, + (unsigned long long ) bio->bi_iter.bi_sector, + (unsigned long long ) sectors, + (unsigned long long )bio_sectors (bio) - sectors); + + split =bio_split (bio, sectors,GFP_NOIO , fs_bio_set); +if (!split) { +pr_err ("%s: bio_split() allocation failure\n" ,mdname (mddev)); +bio_io_error (bio); +return bio; + } +bio_chain (split, bio); + +generic_make_request (split); + +return split; +} + +static sector_t next_frame (sector_t sector) +{ +return SECTORS_PER_FRAME - (sector &FRAME_MASK ); +} +static void isrt_make_request (struct mddev *mddev,struct bio *bio) {struct isrt_conf *conf = mddev->private;struct nv_cache_packed_md *frame;struct isrt_page *p; +struct md_rdev *rdev; +struct bio *split;if (unlikely (bio->bi_rw &REQ_FLUSH )) {md_flush_request (mddev, bio);return ; } -spin_lock (&conf->lock); - p =isrt_lookup_page (conf, bio->bi_iter.bi_sector); - frame =isrt_lookup_frame (conf, p, bio->bi_iter.bi_sector); -spin_unlock (&conf->lock); +if (bio_data_dir (bio) ==WRITE ) { +bio_endio (bio, -EOPNOTSUPP ); +return ; + } + +if (WARN_ONCE (bio->bi_vcnt >1 , +pr_fmt ("%s: block bug: 1 segment supported, got: %d\n" ), +mdname (mddev), bio->bi_vcnt)) { +bio_endio (bio, -EOPNOTSUPP ); +return ; + } + +do { +sector_t sector = bio->bi_iter.bi_sector; + +if (!is_io_in_frame_boundary (bio)) { + split =isrt_split (mddev, bio,next_frame (sector)); + }else { +spin_lock (&conf->lock); + p =isrt_lookup_page (conf, sector); + frame =isrt_lookup_frame (conf, p, sector); +spin_unlock (&conf->lock); + +pr_debug ("%s: %s sector: %llu+%d cache: %s\n" , +mdname (mddev), +bio_data_dir (bio) ==READ ?"READ" :"WRITE" , + (unsigned long long ) sector,bio_sectors (bio), +is_io_cached (frame, bio) ?"hit" : +is_io_uncached (frame, bio) ?"miss" :"partial" ); + +/* we now have a single frame i/o that may need +* to be split according to per-sector validity, +* otherwise re-route the bio to the proper +* device +*/ + split = bio; +if (is_io_uncached (frame, bio)) { + rdev = conf->dev[ISRT_TARGET_DEV_IDX ]; + bio->bi_bdev = rdev->bdev; + }else if (is_io_cached (frame, bio)) { +int frame_idx =to_frame_idx (conf, frame); + +sector_t offset = sector &FRAME_MASK ; +sector_t frame_offset = frame_idx *SECTORS_PER_FRAME ; + + rdev = conf->dev[ISRT_DEV_IDX ]; + bio->bi_bdev = rdev->bdev; + bio->bi_iter.bi_sector = conf->cache_frame0_lba + + frame_offset + offset; + }else + split =isrt_split (mddev, bio,1 ); + } -pr_debug ("%s: sector: %llu cache: %s\n" , - __func__, (unsigned long long ) bio->bi_iter.bi_sector, - frame ?"hit" :"miss" ); -bio_endio (bio, -EOPNOTSUPP ); +generic_make_request (split); + }while (split != bio); }static void isrt_status (struct seq_file *seq,struct mddev *mddev)
RFC PATCH 3/3 md/isrt: write support
In reply to Dan WilliamsThe only case that requires special handling is a write to a clean/cached sector. Writes to an un-cached sector can be passed directly to the target device. Writes to a dirty sector can be passed directly to the cache device. For writes to a clean sector we mark the frame dirty, flush the metadata write, and then write to the cache device, which is power-fail safe. The other cases are already handled naturally by the recursive splitting implementation.
Use one global write_mutex for simplicity. This implementation is meant for read-mostly / sporadic write workloads, i.e. basic dual-boot compatibility.
--- drivers/md/isrt.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++---- drivers/md/isrt.h | 10 +++++ 2 files changed, 111 insertions(+), 8 deletions(-)
drivers/md/isrt.c +101 -8
@@ -21,7 +21,9 @@#include "md.h" #include "isrt.h" -static void mpb_read_endio (struct bio *bio,int error) +struct workqueue_struct *isrt_dirty_workqueue; + +static void metadata_endio (struct bio *bio,int error) {struct mddev *mddev = bio->bi_private;struct isrt_conf *conf = mddev->private;
@@ -49,7 +51,7 @@ static int isrt_mpb_read(struct mddev *mddev, struct page *page) bio->bi_iter.bi_sector =0 ; bio->bi_private = mddev; bio->bi_bdev = rdev->bdev; - bio->bi_end_io = mpb_read_endio; + bio->bi_end_io = metadata_endio;bio_add_page (bio, page, size,0 );atomic_inc (&conf->count);
@@ -79,7 +81,7 @@ static int isrt_read_packed_md(struct mddev *mddev) bio->bi_iter.bi_sector = conf->packed_md_lba + (i >>9 ); bio->bi_private = mddev; bio->bi_bdev = rdev->bdev; - bio->bi_end_io = mpb_read_endio; + bio->bi_end_io = metadata_endio;bio_add_page (bio, page,PAGE_SIZE ,0 );atomic_inc (&conf->count);
@@ -199,6 +201,7 @@ static int isrt_init_conf(struct mddev *mddev, struct isrt_conf *conf) conf->root =RB_ROOT ;init_waitqueue_head (&conf->eventq);atomic_set (&conf->count,0 ); +mutex_init (&conf->write_mutex);if (!page)return -ENOMEM ;
@@ -304,6 +307,11 @@ static void isrt_free_conf(struct isrt_conf *conf)if (!conf)return ; +mutex_lock (&conf->write_mutex); +clear_bit (ISRT_RUN , &conf->state); +flush_workqueue (isrt_dirty_workqueue); +mutex_unlock (&conf->write_mutex); +spin_lock (&conf->lock);for (r =rb_first (&conf->root); r; ) {struct isrt_page *p =to_cache_page (r);
@@ -425,6 +433,7 @@ static struct isrt_conf *isrt_setup_conf(struct mddev *mddev) mddev->queue->backing_dev_info.congested_fn = isrt_congested; mddev->queue->backing_dev_info.congested_data = mddev; +set_bit (ISRT_RUN , &conf->state);return conf; abort:
@@ -545,6 +554,85 @@ static sector_t next_frame(sector_t sector)return SECTORS_PER_FRAME - (sector &FRAME_MASK ); } + +struct isrt_dirty_work *to_dirty_work (struct work_struct *work) +{ + return container_of(work, struct isrt_dirty_work, work); +} + +static void do_mark_dirty (struct work_struct *work) +{ +struct isrt_dirty_work *dirty_work =to_dirty_work (work); +struct nv_cache_packed_md *frame = dirty_work->frame; +struct isrt_conf *conf = dirty_work->conf; +struct mddev *mddev = conf->mddev; +int frame_idx_align; +struct page *page; +sector_t sect_offset; +struct bio *bio; + +if (frame->flags &NVC_PACKED_DIRTY ) +return ; + +/* we do this once per write hit on a clean frame (most frames are +* expected to be dirty or invalid) +*/ + frame->flags |=NVC_PACKED_DIRTY ; + frame_idx_align =to_frame_idx (conf, frame) & ~(((PAGE_SIZE /sizeof (*frame))-1 )); + bio =bio_alloc_mddev (GFP_NOIO ,1 , mddev); + page =vmalloc_to_page (&conf->vmeta[frame_idx_align]); + sect_offset = (frame_idx_align *sizeof (*frame)) >>9 ; + +if (!bio) { + dirty_work->result = false; +return ; + } +if (!page) { +bio_put (bio); + dirty_work->result = false; +return ; + } + + bio->bi_iter.bi_sector = conf->packed_md_lba + sect_offset; + bio->bi_private = mddev; + bio->bi_bdev = conf->dev[ISRT_DEV_IDX ]->bdev; + bio->bi_end_io = metadata_endio; +bio_add_page (bio, page,PAGE_SIZE ,0 ); + +pr_debug ("%s: frame: %d align: %d sect_offset: %llu\n" , + __func__,to_frame_idx (conf, frame), frame_idx_align, + (unsigned long long )sect_offset); + +atomic_inc (&conf->count); +submit_bio (WRITE_FLUSH_FUA , bio); +wait_event (conf->eventq,atomic_read (&conf->count) ==0 ); + +if (test_bit (ISRT_ERROR , &conf->state)) { + frame->flags &= ~NVC_PACKED_DIRTY ; + dirty_work->result = false; + } +} + +static bool mark_dirty (struct isrt_conf *conf,struct nv_cache_packed_md *frame) +{ +struct isrt_dirty_work dirty_work = { + .conf = conf, + .frame = frame, + .result = true, + }; + +INIT_WORK_ONSTACK (&dirty_work.work, do_mark_dirty); + +mutex_lock (&conf->write_mutex); +if (test_bit (ISRT_RUN , &conf->state)) { +queue_work (isrt_dirty_workqueue, &dirty_work.work); +flush_work (&dirty_work.work); + } +mutex_unlock (&conf->write_mutex); + +return dirty_work.result; +} +static void isrt_make_request (struct mddev *mddev,struct bio *bio) {struct isrt_conf *conf = mddev->private;
@@ -558,11 +646,6 @@ static void isrt_make_request(struct mddev *mddev, struct bio *bio)return ; } -if (bio_data_dir (bio) ==WRITE ) { -bio_endio (bio, -EOPNOTSUPP ); -return ; - } -if (WARN_ONCE (bio->bi_vcnt >1 ,pr_fmt ("%s: block bug: 1 segment supported, got: %d\n" ),mdname (mddev), bio->bi_vcnt)) {
@@ -603,6 +686,12 @@ static void isrt_make_request(struct mddev *mddev, struct bio *bio)sector_t offset = sector &FRAME_MASK ;sector_t frame_offset = frame_idx *SECTORS_PER_FRAME ; +if (bio_data_dir (bio) ==WRITE + && !mark_dirty (conf, frame)) { +bio_io_error (bio); +return ; + } + rdev = conf->dev[ISRT_DEV_IDX ]; bio->bi_bdev = rdev->bdev; bio->bi_iter.bi_sector = conf->cache_frame0_lba
@@ -637,12 +726,16 @@ static struct md_personality isrt_personality = {static int __init isrt_init (void ) { + isrt_dirty_workqueue =create_workqueue ("isrt" ); +if (!isrt_dirty_workqueue) +return -ENOMEM ;return register_md_personality (&isrt_personality); }static void isrt_exit (void ) {unregister_md_personality (&isrt_personality); +destroy_workqueue (isrt_dirty_workqueue); }module_init (isrt_init);
drivers/md/isrt.h +10 -0
@@ -264,9 +264,19 @@ struct isrt_conf {spinlock_t lock;#define ISRT_META_IO 0#define ISRT_ERROR 1 +#define ISRT_RUN 2unsigned long state;atomic_t count;wait_queue_head_t eventq; +struct mutex write_mutex; +}; + +/* we can't wait for metadata updates inline */ +struct isrt_dirty_work { +bool result; +struct work_struct work; +struct isrt_conf *conf; +struct nv_cache_packed_md *frame; };static inline u32 to_seg_num (sector_t lba)