* Re: [RFC 1/3] block: Introduce blk_bio_map_sg() to map one bio
From: Ming Lei @ 2016-05-25 8:52 UTC (permalink / raw)
To: Baolin Wang
Cc: Jens Axboe, Alasdair Kergon, Mike Snitzer,
open list:DEVICE-MAPPER (LVM), Herbert Xu, David Miller,
ebiggers3, js1304, tadeusz.struk, smueller, standby24x7,
Shaohua Li, Dan Williams, Martin K. Petersen, Sagi Grimberg,
Kent Overstreet, Keith Busch, Tejun Heo, Mark Brown,
Arnd Bergmann, linux-crypto, linux-block,
open list:SOFTWARE RAID (Multiple Disks) SUPPORT,
Linux Kernel Mailing List
In-Reply-To: <abe693fad3abb0adfc0cf9ca5b3d287fdb0f2d5b.1464144791.git.baolin.wang@linaro.org>
On Wed, May 25, 2016 at 2:12 PM, Baolin Wang <baolin.wang@linaro.org> wrote:
> In dm-crypt, it need to map one bio to scatterlist for improving the
> hardware engine encryption efficiency. Thus this patch introduces the
> blk_bio_map_sg() function to map one bio with scatterlists.
>
> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
> ---
> block/blk-merge.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/blkdev.h | 3 +++
> 2 files changed, 48 insertions(+)
>
> diff --git a/block/blk-merge.c b/block/blk-merge.c
> index 2613531..9b92af4 100644
> --- a/block/blk-merge.c
> +++ b/block/blk-merge.c
> @@ -417,6 +417,51 @@ single_segment:
> }
>
> /*
> + * map a bio to scatterlist, return number of sg entries setup.
> + */
> +int blk_bio_map_sg(struct request_queue *q, struct bio *bio,
> + struct scatterlist *sglist,
> + struct scatterlist **sg)
> +{
> + struct bio_vec bvec, bvprv = { NULL };
> + struct bvec_iter iter;
> + int nsegs, cluster;
> +
> + nsegs = 0;
> + cluster = blk_queue_cluster(q);
> +
> + if (bio->bi_rw & REQ_DISCARD) {
> + /*
> + * This is a hack - drivers should be neither modifying the
> + * biovec, nor relying on bi_vcnt - but because of
> + * blk_add_request_payload(), a discard bio may or may not have
> + * a payload we need to set up here (thank you Christoph) and
> + * bi_vcnt is really the only way of telling if we need to.
> + */
> +
> + if (bio->bi_vcnt)
> + goto single_segment;
> +
> + return 0;
> + }
> +
> + if (bio->bi_rw & REQ_WRITE_SAME) {
> +single_segment:
> + *sg = sglist;
> + bvec = bio_iovec(bio);
> + sg_set_page(*sg, bvec.bv_page, bvec.bv_len, bvec.bv_offset);
> + return 1;
> + }
> +
> + bio_for_each_segment(bvec, bio, iter)
> + __blk_segment_map_sg(q, &bvec, sglist, &bvprv, sg,
> + &nsegs, &cluster);
> +
> + return nsegs;
> +}
> +EXPORT_SYMBOL(blk_bio_map_sg);
You can use __blk_bios_map_sg() to implement blk_bio_map_sg(),
then code duplication may be avoided.
> +
> +/*
> * map a request to scatterlist, return number of sg entries setup. Caller
> * must make sure sg can hold rq->nr_phys_segments entries
> */
> diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> index 1fd8fdf..e5de4f8 100644
> --- a/include/linux/blkdev.h
> +++ b/include/linux/blkdev.h
> @@ -1013,6 +1013,9 @@ extern void blk_queue_write_cache(struct request_queue *q, bool enabled, bool fu
> extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev);
>
> extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *);
> +extern int blk_bio_map_sg(struct request_queue *q, struct bio *bio,
> + struct scatterlist *sglist,
> + struct scatterlist **sg);
> extern void blk_dump_rq_flags(struct request *, char *);
> extern long nr_blockdev_pages(void);
>
> --
> 1.7.9.5
>
^ permalink raw reply
* Re: [RFC 1/3] block: Introduce blk_bio_map_sg() to map one bio
From: Baolin Wang @ 2016-05-25 9:02 UTC (permalink / raw)
To: Ming Lei
Cc: Jens Axboe, Alasdair Kergon, Mike Snitzer,
open list:DEVICE-MAPPER (LVM), Herbert Xu, David Miller,
ebiggers3, js1304, tadeusz.struk, smueller, Masanari Iida,
Shaohua Li, Dan Williams, Martin K. Petersen, Sagi Grimberg,
Kent Overstreet, Keith Busch, Tejun Heo, Mark Brown,
Arnd Bergmann, linux-crypto, linux-block,
open list:SOFTWARE RAID (Multiple Disks) SUPPORT,
Linux Kernel Mailing List
In-Reply-To: <CACVXFVMYm_3rHe5wxs9YA15h+N0EfjMcGQEgnzLZTVbh7HUaRw@mail.gmail.com>
On 25 May 2016 at 16:52, Ming Lei <ming.lei@canonical.com> wrote:
>> /*
>> + * map a bio to scatterlist, return number of sg entries setup.
>> + */
>> +int blk_bio_map_sg(struct request_queue *q, struct bio *bio,
>> + struct scatterlist *sglist,
>> + struct scatterlist **sg)
>> +{
>> + struct bio_vec bvec, bvprv = { NULL };
>> + struct bvec_iter iter;
>> + int nsegs, cluster;
>> +
>> + nsegs = 0;
>> + cluster = blk_queue_cluster(q);
>> +
>> + if (bio->bi_rw & REQ_DISCARD) {
>> + /*
>> + * This is a hack - drivers should be neither modifying the
>> + * biovec, nor relying on bi_vcnt - but because of
>> + * blk_add_request_payload(), a discard bio may or may not have
>> + * a payload we need to set up here (thank you Christoph) and
>> + * bi_vcnt is really the only way of telling if we need to.
>> + */
>> +
>> + if (bio->bi_vcnt)
>> + goto single_segment;
>> +
>> + return 0;
>> + }
>> +
>> + if (bio->bi_rw & REQ_WRITE_SAME) {
>> +single_segment:
>> + *sg = sglist;
>> + bvec = bio_iovec(bio);
>> + sg_set_page(*sg, bvec.bv_page, bvec.bv_len, bvec.bv_offset);
>> + return 1;
>> + }
>> +
>> + bio_for_each_segment(bvec, bio, iter)
>> + __blk_segment_map_sg(q, &bvec, sglist, &bvprv, sg,
>> + &nsegs, &cluster);
>> +
>> + return nsegs;
>> +}
>> +EXPORT_SYMBOL(blk_bio_map_sg);
>
> You can use __blk_bios_map_sg() to implement blk_bio_map_sg(),
> then code duplication may be avoided.
OK. I'll re-factor the code to map one bio.
>
>> +
>> +/*
>> * map a request to scatterlist, return number of sg entries setup. Caller
>> * must make sure sg can hold rq->nr_phys_segments entries
>> */
>> diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
>> index 1fd8fdf..e5de4f8 100644
>> --- a/include/linux/blkdev.h
>> +++ b/include/linux/blkdev.h
>> @@ -1013,6 +1013,9 @@ extern void blk_queue_write_cache(struct request_queue *q, bool enabled, bool fu
>> extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev);
>>
>> extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *);
>> +extern int blk_bio_map_sg(struct request_queue *q, struct bio *bio,
>> + struct scatterlist *sglist,
>> + struct scatterlist **sg);
>> extern void blk_dump_rq_flags(struct request *, char *);
>> extern long nr_blockdev_pages(void);
>>
>> --
>> 1.7.9.5
>>
--
Baolin.wang
Best Regards
^ permalink raw reply
* Re: [PATCH] right meaning of PARITY_ENABLE_RMW and PARITY_PREFER_RMW
From: Shaohua Li @ 2016-05-25 16:02 UTC (permalink / raw)
To: Song Liu; +Cc: linux-raid, stockhausen, neilb
In-Reply-To: <1464049506-1589075-1-git-send-email-songliubraving@fb.com>
On Mon, May 23, 2016 at 05:25:06PM -0700, Song Liu wrote:
> In current handle_stripe_dirtying, the code prefers rmw with
> PARITY_ENABLE_RMW; while prefers rcw with PARITY_PREFER_RMW.
>
> This patch reverses this behavior.
>
> Signed-off-by: Song Liu <songliubraving@fb.com>
> Signed-off-by: Shaohua Li <shli@fb.com>
> ---
> drivers/md/raid5.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index 8959e6d..ad9e15a 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -3600,7 +3600,7 @@ static void handle_stripe_dirtying(struct r5conf *conf,
> pr_debug("for sector %llu, rmw=%d rcw=%d\n",
> (unsigned long long)sh->sector, rmw, rcw);
> set_bit(STRIPE_HANDLE, &sh->state);
> - if ((rmw < rcw || (rmw == rcw && conf->rmw_level == PARITY_ENABLE_RMW)) && rmw > 0) {
> + if ((rmw < rcw || (rmw == rcw && conf->rmw_level == PARITY_PREFER_RMW)) && rmw > 0) {
> /* prefer read-modify-write, but need to get some data */
> if (conf->mddev->queue)
> blk_add_trace_msg(conf->mddev->queue,
> @@ -3627,7 +3627,7 @@ static void handle_stripe_dirtying(struct r5conf *conf,
> }
> }
> }
> - if ((rcw < rmw || (rcw == rmw && conf->rmw_level != PARITY_ENABLE_RMW)) && rcw > 0) {
> + if ((rcw < rmw || (rcw == rmw && conf->rmw_level != PARITY_PREFER_RMW)) && rcw > 0) {
> /* want reconstruct write, but need to get some data */
> int qread =0;
> rcw = 0;
The patch looks correct, I'll apply.
I'm wondering why original code is in current way. It dosn't work like what the
patch log (d06f191f8ecae) describes. Neil/Markus, is any patch missed?
Thanks,
Shaohua
^ permalink raw reply
* AW: [PATCH] right meaning of PARITY_ENABLE_RMW and PARITY_PREFER_RMW
From: Markus Stockhausen @ 2016-05-25 17:07 UTC (permalink / raw)
To: Shaohua Li, Song Liu; +Cc: linux-raid@vger.kernel.org, neilb@suse.de
In-Reply-To: <20160525160216.GA15101@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 2466 bytes --]
> Von: linux-raid-owner@vger.kernel.org [linux-raid-owner@vger.kernel.org]" im Auftrag von "Shaohua Li [shli@kernel.org]
> Gesendet: Mittwoch, 25. Mai 2016 18:02
> An: Song Liu
> Cc: linux-raid@vger.kernel.org; Markus Stockhausen; neilb@suse.de
> Betreff: Re: [PATCH] right meaning of PARITY_ENABLE_RMW and PARITY_PREFER_RMW
>
> On Mon, May 23, 2016 at 05:25:06PM -0700, Song Liu wrote:
> > In current handle_stripe_dirtying, the code prefers rmw with
> > PARITY_ENABLE_RMW; while prefers rcw with PARITY_PREFER_RMW.
> >
> > This patch reverses this behavior.
> >
> > Signed-off-by: Song Liu <songliubraving@fb.com>
> > Signed-off-by: Shaohua Li <shli@fb.com>
> > ---
> > drivers/md/raid5.c | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> > index 8959e6d..ad9e15a 100644
> > --- a/drivers/md/raid5.c
> > +++ b/drivers/md/raid5.c
> > @@ -3600,7 +3600,7 @@ static void handle_stripe_dirtying(struct r5conf *conf,
> > pr_debug("for sector %llu, rmw=%d rcw=%d\n",
> > (unsigned long long)sh->sector, rmw, rcw);
> > set_bit(STRIPE_HANDLE, &sh->state);
> > - if ((rmw < rcw || (rmw == rcw && conf->rmw_level == PARITY_ENABLE_RMW)) && rmw > 0) {
> > + if ((rmw < rcw || (rmw == rcw && conf->rmw_level == PARITY_PREFER_RMW)) && rmw > 0) {
> > /* prefer read-modify-write, but need to get some data */
> > if (conf->mddev->queue)
> > blk_add_trace_msg(conf->mddev->queue,
> > @@ -3627,7 +3627,7 @@ static void handle_stripe_dirtying(struct r5conf *conf,
> > }
> > }
> > }
> > - if ((rcw < rmw || (rcw == rmw && conf->rmw_level != PARITY_ENABLE_RMW)) && rcw > 0) {
> > + if ((rcw < rmw || (rcw == rmw && conf->rmw_level != PARITY_PREFER_RMW)) && rcw > 0) {
> > /* want reconstruct write, but need to get some data */
> > int qread =0;
> > rcw = 0;
> >
> > The patch looks correct, I'll apply.
>
> I'm wondering why original code is in current way. It dosn't work like what the
> patch log (d06f191f8ecae) describes. Neil/Markus, is any patch missed?
Hi,
simple reason for usage of PARITY_ENABLE_RMW was the patch order. Option
PARITY_PREFER_RMW was implemented after all the rest. I guess I missed
changing that condition.
Thanks for the fix.
Markus=
[-- Attachment #2: InterScan_Disclaimer.txt --]
[-- Type: text/plain, Size: 1650 bytes --]
****************************************************************************
Diese E-Mail enthält vertrauliche und/oder rechtlich geschützte
Informationen. Wenn Sie nicht der richtige Adressat sind oder diese E-Mail
irrtümlich erhalten haben, informieren Sie bitte sofort den Absender und
vernichten Sie diese Mail. Das unerlaubte Kopieren sowie die unbefugte
Weitergabe dieser Mail ist nicht gestattet.
Ãber das Internet versandte E-Mails können unter fremden Namen erstellt oder
manipuliert werden. Deshalb ist diese als E-Mail verschickte Nachricht keine
rechtsverbindliche Willenserklärung.
Collogia
Unternehmensberatung AG
Ubierring 11
D-50678 Köln
Vorstand:
Kadir Akin
Dr. Michael Höhnerbach
Vorsitzender des Aufsichtsrates:
Hans Kristian Langva
Registergericht: Amtsgericht Köln
Registernummer: HRB 52 497
This e-mail may contain confidential and/or privileged information. If you
are not the intended recipient (or have received this e-mail in error)
please notify the sender immediately and destroy this e-mail. Any
unauthorized copying, disclosure or distribution of the material in this
e-mail is strictly forbidden.
e-mails sent over the internet may have been written under a wrong name or
been manipulated. That is why this message sent as an e-mail is not a
legally binding declaration of intention.
Collogia
Unternehmensberatung AG
Ubierring 11
D-50678 Köln
executive board:
Kadir Akin
Dr. Michael Höhnerbach
President of the supervisory board:
Hans Kristian Langva
Registry office: district court Cologne
Register number: HRB 52 497
****************************************************************************
^ permalink raw reply
* Re: raid 5 crashed
From: bobzer @ 2016-05-26 3:06 UTC (permalink / raw)
To: linux-raid, Robin Hill, Mikael Abrahamsson
In-Reply-To: <20160511131524.GA11811@cthulhu.home.robinhill.me.uk>
thanks for your help.
i took time to answer because unlucky me, the power supply of my
laptop fried so no laptop and no way to work on my raid :-(
any way i got a new one :-)
for the dmesg i paste it here : http://pastebin.com/whUHs256
root@serveur:~# uname -a
Linux serveur 3.2.0-4-amd64 #1 SMP Debian 3.2.63-2+deb7u1 x86_64 GNU/Linux
root@serveur:~# mdadm -V
mdadm - v3.3-78-gf43f5b3 - 02nd avril 2014
about zero the superblock on the wrong device, i hope i didn't do
that, but also i really don't think i did that, because i took care
and at that time the raid was working
i don't know what to do, if i use dd_rescue and can't get back 100% of
the data could i be able to start the raid anyway ?
what are my risk if i try something like :
mdadm --assemble --force /dev/md0 /dev/sd[bcde]1
thank you very much for your time
Mathieu
On Wed, May 11, 2016 at 3:15 PM, Robin Hill <robin@robinhill.me.uk> wrote:
> On Tue May 10, 2016 at 11:28:31PM +0200, bobzer wrote:
>
>> hi everyone,
>>
>> I'm in panic mode :-( because i got a raid 5 with 4 disk but 2 removed
>> yesterday i got a power outage which removed one disk. the disks
>> sd[bcd]1 was ok and saying that sde1 is removed but sde1 said that
>> everything is fine.
>> so i stop the raid, zero the superblock of sde1, start the raid and
>> add sde1 to the raid. then it start to reconstruct, i think it had
>> time to finish before this problem (i'm not 100% sure that it finish
>> but i think so)
>> the data was accessible so i went to sleep
>> today i discovered the raid in this state :
>>
>> root@serveur:/home/math# mdadm -D /dev/md0
>> /dev/md0:
>> Version : 1.2
>> Creation Time : Sun Mar 4 22:49:14 2012
>> Raid Level : raid5
>> Array Size : 5860532352 (5589.04 GiB 6001.19 GB)
>> Used Dev Size : 1953510784 (1863.01 GiB 2000.40 GB)
>> Raid Devices : 4
>> Total Devices : 4
>> Persistence : Superblock is persistent
>>
>> Update Time : Fri May 6 17:44:02 2016
>> State : clean, FAILED
>> Active Devices : 2
>> Working Devices : 3
>> Failed Devices : 1
>> Spare Devices : 1
>>
>> Layout : left-symmetric
>> Chunk Size : 128K
>>
>> Name : debian:0
>> UUID : bf3c605b:9699aa55:d45119a2:7ba58d56
>> Events : 892482
>>
>> Number Major Minor RaidDevice State
>> 3 8 33 0 active sync /dev/sdc1
>> 1 8 49 1 active sync /dev/sdd1
>> 4 0 0 4 removed
>> 6 0 0 6 removed
>>
>> 4 8 17 - faulty /dev/sdb1
>> 5 8 65 - spare /dev/sde1
>>
> So this reports /dev/sdb1 faulty and /dev/sde1 spare. That would
> indicate that the rebuild hadn't finished.
>
>> root@serveur:/home/math# mdadm --examine /dev/sdb1
>> /dev/sdb1:
>> Magic : a92b4efc
>> Version : 1.2
>> Feature Map : 0x0
>> Array UUID : bf3c605b:9699aa55:d45119a2:7ba58d56
>> Name : debian:0
>> Creation Time : Sun Mar 4 22:49:14 2012
>> Raid Level : raid5
>> Raid Devices : 4
>>
>> Avail Dev Size : 3907021954 (1863.01 GiB 2000.40 GB)
>> Array Size : 5860532352 (5589.04 GiB 6001.19 GB)
>> Used Dev Size : 3907021568 (1863.01 GiB 2000.40 GB)
>> Data Offset : 2048 sectors
>> Super Offset : 8 sectors
>> Unused Space : before=1960 sectors, after=386 sectors
>> State : clean
>> Device UUID : 9bececcb:d520ca38:fd88d956:5718e361
>>
>> Update Time : Fri May 6 02:07:00 2016
>> Bad Block Log : 512 entries available at offset 72 sectors
>> Checksum : dc2a133a - correct
>> Events : 892215
>>
>> Layout : left-symmetric
>> Chunk Size : 128K
>>
>> Device Role : Active device 2
>> Array State : AAAA ('A' == active, '.' == missing, 'R' == replacing)
>>
> We can see /dev/sdb1 has a lower event count than the others and also
> that it indicates all the drives in the array were active when it was
> last running. That would strongly suggest that it was not in the array
> when /dev/sde1 was added to rebuild. The update time is also nearly 16
> hours earlier than that of the other drives.
>
>> root@serveur:/home/math# mdadm --examine /dev/sdc1
>> /dev/sdc1:
>> Magic : a92b4efc
>> Version : 1.2
>> Feature Map : 0x0
>> Array UUID : bf3c605b:9699aa55:d45119a2:7ba58d56
>> Name : debian:0
>> Creation Time : Sun Mar 4 22:49:14 2012
>> Raid Level : raid5
>> Raid Devices : 4
>>
>> Avail Dev Size : 3907021954 (1863.01 GiB 2000.40 GB)
>> Array Size : 5860532352 (5589.04 GiB 6001.19 GB)
>> Used Dev Size : 3907021568 (1863.01 GiB 2000.40 GB)
>> Data Offset : 2048 sectors
>> Super Offset : 8 sectors
>> Unused Space : before=1960 sectors, after=386 sectors
>> State : clean
>> Device UUID : 1ecaf51c:3289a902:7bb71a93:237c68e8
>>
>> Update Time : Fri May 6 17:58:27 2016
>> Bad Block Log : 512 entries available at offset 72 sectors
>> Checksum : b9d6aa84 - correct
>> Events : 892484
>>
>> Layout : left-symmetric
>> Chunk Size : 128K
>>
>> Device Role : Active device 0
>> Array State : AA.. ('A' == active, '.' == missing, 'R' == replacing)
>>
>> root@serveur:/home/math# mdadm --examine /dev/sdd1
>> /dev/sdd1:
>> Magic : a92b4efc
>> Version : 1.2
>> Feature Map : 0x0
>> Array UUID : bf3c605b:9699aa55:d45119a2:7ba58d56
>> Name : debian:0
>> Creation Time : Sun Mar 4 22:49:14 2012
>> Raid Level : raid5
>> Raid Devices : 4
>>
>> Avail Dev Size : 3907021954 (1863.01 GiB 2000.40 GB)
>> Array Size : 5860532352 (5589.04 GiB 6001.19 GB)
>> Used Dev Size : 3907021568 (1863.01 GiB 2000.40 GB)
>> Data Offset : 2048 sectors
>> Super Offset : 8 sectors
>> Unused Space : before=0 sectors, after=386 sectors
>> State : clean
>> Device UUID : 406c4cb5:c188e4a9:7ed8be9f:14a49b16
>>
>> Update Time : Fri May 6 17:58:27 2016
>> Bad Block Log : 512 entries available at offset 2032 sectors
>> Checksum : 343f9cd0 - correct
>> Events : 892484
>>
>> Layout : left-symmetric
>> Chunk Size : 128K
>>
>> Device Role : Active device 1
>> Array State : AA.. ('A' == active, '.' == missing, 'R' == replacing)
>>
> These two drives contain the same information. They indicate that they
> were the only 2 running members in the array when they were last updated.
>
>> root@serveur:/home/math# mdadm --examine /dev/sde1
>> /dev/sde1:
>> Magic : a92b4efc
>> Version : 1.2
>> Feature Map : 0x8
>> Array UUID : bf3c605b:9699aa55:d45119a2:7ba58d56
>> Name : debian:0
>> Creation Time : Sun Mar 4 22:49:14 2012
>> Raid Level : raid5
>> Raid Devices : 4
>>
>> Avail Dev Size : 3907025072 (1863.01 GiB 2000.40 GB)
>> Array Size : 5860532352 (5589.04 GiB 6001.19 GB)
>> Used Dev Size : 3907021568 (1863.01 GiB 2000.40 GB)
>> Data Offset : 2048 sectors
>> Super Offset : 8 sectors
>> Unused Space : before=1960 sectors, after=3504 sectors
>> State : clean
>> Device UUID : f2e9c1ec:2852cf21:1a588581:b9f49a8b
>>
>> Update Time : Fri May 6 17:58:27 2016
>> Bad Block Log : 512 entries available at offset 72 sectors - bad
>> blocks present.
>> Checksum : 3a65b8bc - correct
>> Events : 892484
>>
>> Layout : left-symmetric
>> Chunk Size : 128K
>>
>> Device Role : spare
>> Array State : AA.. ('A' == active, '.' == missing, 'R' == replacing)
>>
> And finally /dev/sde1 shows as a spare, with the rest of the data
> matching /dev/sdc1 and /dev/sde1.
>
>> PLEASE help me :-) i don't know what to do so i did nothing to not do
>> any stupid things
>> 1000 thank you
>>
>> ps i just saw this, i hope it not mak y case worst
>> root@serveur:/home/math# cat /etc/mdadm/mdadm.conf
>> DEVICE /dev/sd[bcd]1
>> ARRAY /dev/md0 metadata=1.2 name=debian:0
>> UUID=bf3c605b:9699aa55:d45119a2:7ba58d56
>>
>
> From the data here, if looks to me as though /dev/sdb1 failed originally
> (hence it thinks the array was complete). Either then /dev/sde1 also
> failed, or you've proceeded to zero the superblock on the wrong drive.
> You really need to look through the system logs and verify what happened
> when and to what disk (if you rebooted at any point, the drive ordering
> may have changed, so don't take for granted that the drive names are
> consistent throughout).
>
> Cheers,
> Robin
> --
> ___
> ( ' } | Robin Hill <robin@robinhill.me.uk> |
> / / ) | Little Jim says .... |
> // !! | "He fallen in de water !!" |
^ permalink raw reply
* mdadm remove old md details reassemble working drives
From: Bryan Hepworth @ 2016-05-26 9:56 UTC (permalink / raw)
To: linux-raid@vger.kernel.org
Hi
Hopefully this will be fairly straight forward. I have a machine that has had hardware added over the course of its existence before I took over. There is an issue with two of the shelves that means that they aren't starting automatically and after digging around I think I know the reason why.
When the raid array was created it would have listed the drives in order /dev/sdb /dev/sdc /dev/sdd etc however at some point it has jumped and the device names have changed. The raid arrays are both currently offline, but running the mdadm --examine command shows that they are all present and correct and all in step with each other just with different device names to /etc/mdadm.conf to what they are in reality. I'd like another pair of eyes to have a look before I commit to making changes. The arrays are formatted as xfs and I've had problems with that in the past so want to be sure.
My question is how do I go about getting the arrays back up? After reading the notes it would appear I would have to re-assemble the drives as follows, is this correct?
mdadm --stop /dev/md130
mdadm --stop /dev/md130
mdadm --assemble /dev/md130 /dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf /dev/sdg /dev/sdh /dev/sdi /dev/sdp /dev/sdq /dev/sdr /dev/sds
mdadm --assemble /dev/md136 /dev/sdt /dev/sdu /dev/sdv /dev/sdw /dev/sdx /dev/sdy /dev/sdz /dev/sdaa /dev/sdab /dev/sdac /dev/sdad /dev/sdae
mdadm --detail --scan >> /etc/mdadm.conf
Here's the output from various commands that I believe are the important ones.
Thanks
Bryan
/etc/mdadm.conf
[root@inscribe5 etc]# cat mdadm.conf
ARRAY /dev/md/inscribe5.ncl.ac.uk:130 level=raid5 num-devices=12 metadata=1.2 name=inscribe5.ncl.ac.uk:130 UUID=cb850648:d5b35007:dc6000f0:b48ab578
devices=/dev/sdb,/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf,/dev/sdg,/dev/sdh,/dev/sdi,/dev/sdj,/dev/sdk,/dev/sdl,/dev/sdm
ARRAY /dev/md/igmimager.ncl.ac.uk:136 level=raid5 num-devices=12 metadata=1.2 name=igmimager.ncl.ac.uk:136 UUID=b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
devices=/dev/sdn,/dev/sdo,/dev/sdp,/dev/sdq,/dev/sdr,/dev/sds,/dev/sdt,/dev/sdu,/dev/sdv,/dev/sdw,/dev/sdx,/dev/sdy
[root@inscribe5 etc]#
/proc/mdstat
[root@inscribe5 ~]# cat /proc/mdstat
Personalities : [raid6] [raid5] [raid4]
md126 : inactive sdo[2](S) sdn[4](S)
3906767024 blocks super 1.2
md127 : active (auto-read-only) raid5 sdm1[4](F) sdl1[2](F) sdk1[1](F) sdj1[0](F)
5860142592 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/0] [____]
bitmap: 0/15 pages [0KB], 65536KB chunk
md136 : active (auto-read-only) raid5 super 1.2 level 5, 512k chunk, algorithm 2 [12/0] [____________]
bitmap: 0/30 pages [0KB], 65536KB chunk
md130 : active (auto-read-only) raid5 super 1.2 level 5, 512k chunk, algorithm 2 [12/0] [____________]
bitmap: 0/30 pages [0KB], 65536KB chunk
mdadm --examine Output
[root@inscribe5 etc]# mdadm --examine /dev/sdb
/dev/sdb:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : a7ece5a2:cd52d443:15fca242:f9849df7
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 9c7c0559 - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 0
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdc
/dev/sdc:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 135b7c93:88cd0e38:0e86b868:a12f2416
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : c5e92321 - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 1
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdd
/dev/sdd:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : b78cd05e:c441f2ee:e7893ce2:a1a4755e
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 9f641dd - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 2
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sde
/dev/sde:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : cd686c04:311bb2ed:cdc54888:07883b40
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 362416ac - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 3
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdf
/dev/sdf:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : f183d0da:207286e1:08399567:916810e1
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 807ddc86 - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 4
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdg
/dev/sdg:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 2872da1b:1ed23064:c7a72b7d:f35e7a44
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : bd328fdb - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 5
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdh
/dev/sdh:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : cdba7e4f:07758d88:2911bba3:ff668906
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : fdd1ecd8 - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 6
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdi
/dev/sdi:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 41dd6007:5dd339f2:b8eff43e:d82d9365
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 19a4130c - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 7
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdj
mdadm: cannot open /dev/sdj: No such file or directory
[root@inscribe5 etc]# mdadm --examine /dev/sdk
mdadm: cannot open /dev/sdk: No such file or directory
[root@inscribe5 etc]# mdadm --examine /dev/sdl
mdadm: cannot open /dev/sdl: No such file or directory
[root@inscribe5 etc]# mdadm --examine /dev/sdm
mdadm: cannot open /dev/sdm: No such file or directory
[root@inscribe5 etc]# mdadm --examine /dev/sdn
mdadm: cannot open /dev/sdn: No such file or directory
[root@inscribe5 etc]# mdadm --examine /dev/sdo
mdadm: cannot open /dev/sdo: No such file or directory
[root@inscribe5 etc]# mdadm --examine /dev/sdp
/dev/sdp:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : e8b00d6f:157a23c0:638f2508:f75ad939
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : ecb15a35 - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 8
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdq
/dev/sdq:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : b9da41fa:adefe7b5:360a8993:57fabddb
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 9af213d4 - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 9
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdr
/dev/sdr:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : c5d26859:108844a1:0dd6db16:8e2616b8
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 45209c51 - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 10
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sds
/dev/sds:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : cb850648:d5b35007:dc6000f0:b48ab578
Name : inscribe5.ncl.ac.uk:130 (local to host inscribe5.ncl.ac.uk)
Creation Time : Thu May 28 12:17:06 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 4213b169:062dfa7c:c62ef36c:c6110735
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:27 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 426c5b7 - correct
Events : 31406
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 11
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdt
/dev/sdt:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : f72233d4:2a14c7e3:95f4ca36:61db22e6
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 3a5364dd - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 0
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdu
/dev/sdu:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 89efc0ab:8639052b:e5c44ef0:a3d3c085
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : b2411f5d - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 1
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdv
/dev/sdv:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : d6b55438:42171969:8450174e:e8a6dd4f
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : a4ce224a - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 2
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdw
/dev/sdw:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : f9499252:50d79458:f2662aa9:f25f1c3a
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : f3d945f4 - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 3
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdx
/dev/sdx:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : b6ce9001:c33349f9:4f1cab26:2a778d5d
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : e47df3ba - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 4
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdy
/dev/sdy:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : b7a71581:7b81c93c:df58b3a5:6773c94d
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 16c75342 - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 5
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdz
/dev/sdz:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 0065d0f6:97a47a2b:1733e172:74b59f40
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 3b374fed - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 6
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdaa
/dev/sdaa:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 809b7d03:0a06eaa1:6704d4a0:14621573
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 1ebc65d1 - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 7
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdab
/dev/sdab:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 4c899786:22b4da32:fd268ffe:f4117c6e
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 8be8d42c - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 8
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdac
/dev/sdac:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 8a21bc71:8db320f0:fff1e06b:c006555f
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 927e2ba4 - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 9
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdad
/dev/sdad:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 23ccc25a:e36a303a:d7a533c6:a330d5d2
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 93676b4f - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 10
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
[root@inscribe5 etc]# mdadm --examine /dev/sdae
/dev/sdae:
Magic : a92b4efc
Version : 1.2
Feature Map : 0x1
Array UUID : b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
Name : igmimager.ncl.ac.uk:136
Creation Time : Thu May 28 12:34:30 2015
Raid Level : raid5
Raid Devices : 12
Avail Dev Size : 7813775024 (3725.90 GiB 4000.65 GB)
Array Size : 42975758848 (40984.88 GiB 44007.18 GB)
Used Dev Size : 7813774336 (3725.90 GiB 4000.65 GB)
Data Offset : 262144 sectors
Super Offset : 8 sectors
Unused Space : before=262056 sectors, after=688 sectors
State : clean
Device UUID : 0f3c30b6:c6adb547:f9a251ad:927a008f
Internal Bitmap : 8 sectors from superblock
Update Time : Fri May 20 13:17:33 2016
Bad Block Log : 512 entries available at offset 72 sectors
Checksum : 9fa36531 - correct
Events : 16085
Layout : left-symmetric
Chunk Size : 512K
Device Role : Active device 11
Array State : AAAAAAAAAAAA ('A' == active, '.' == missing, 'R' == replacing)
^ permalink raw reply
* Re: mdadm remove old md details reassemble working drives
From: Andreas Klauer @ 2016-05-26 11:41 UTC (permalink / raw)
To: Bryan Hepworth; +Cc: linux-raid@vger.kernel.org
In-Reply-To: <DB5PR07MB1287BD3F72D4FA302118C075D5410@DB5PR07MB1287.eurprd07.prod.outlook.com>
On Thu, May 26, 2016 at 09:56:24AM +0000, Bryan Hepworth wrote:
> /etc/mdadm.conf
>
> [root@inscribe5 etc]# cat mdadm.conf
> ARRAY /dev/md/inscribe5.ncl.ac.uk:130 level=raid5 num-devices=12 metadata=1.2 name=inscribe5.ncl.ac.uk:130 UUID=cb850648:d5b35007:dc6000f0:b48ab578
> devices=/dev/sdb,/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf,/dev/sdg,/dev/sdh,/dev/sdi,/dev/sdj,/dev/sdk,/dev/sdl,/dev/sdm
> ARRAY /dev/md/igmimager.ncl.ac.uk:136 level=raid5 num-devices=12 metadata=1.2 name=igmimager.ncl.ac.uk:136 UUID=b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
> devices=/dev/sdn,/dev/sdo,/dev/sdp,/dev/sdq,/dev/sdr,/dev/sds,/dev/sdt,/dev/sdu,/dev/sdv,/dev/sdw,/dev/sdx,/dev/sdy
That looks a bit overengineered...
You can reduce the mdadm.conf to just use UUID and nothing else
ARRAY /dev/md0 UUID=cb850648:d5b35007:dc6000f0:b48ab578
Keep the /dev/md/name if you need it but everything else
(num-devices, metadata, devices, level, ...) is part
of the metadata, no need to duplicate it in the conf.
There doesn't seem to be a problem with the metadata itself
(all clean, same update time, same event count) so unless
the devices get detected late or something it should just work
when assembling by UUID...
Regards
Andreas Klauer
^ permalink raw reply
* Re: [RFC 3/3] md: dm-crypt: Introduce the bulk mode method when sending request
From: Mike Snitzer @ 2016-05-26 14:04 UTC (permalink / raw)
To: Baolin Wang
Cc: axboe, agk, dm-devel, herbert, davem, sagig, linux-raid,
martin.petersen, js1304, tadeusz.struk, smueller, ming.lei,
ebiggers3, linux-block, keith.busch, linux-kernel, standby24x7,
broonie, arnd, linux-crypto, tj, dan.j.williams, shli,
kent.overstreet
In-Reply-To: <70f051223cf882b03b373369b6b65b4a7ebf07b6.1464144791.git.baolin.wang@linaro.org>
Comments inlined.
In general the most concerning bit is the need for memory allocation in
the IO path (see comment/question below near call to sg_alloc_table).
In DM targets we make heavy use of .ctr preallocated memory and/or
per-bio-data to avoid memory allocations in the IO path.
On Wed, May 25 2016 at 2:12am -0400,
Baolin Wang <baolin.wang@linaro.org> wrote:
> In now dm-crypt code, it is ineffective to map one segment (always one
> sector) of one bio with just only one scatterlist at one time for hardware
> crypto engine. Especially for some encryption mode (like ecb or xts mode)
> cooperating with the crypto engine, they just need one initial IV or null
> IV instead of different IV for each sector. In this situation We can consider
> to use multiple scatterlists to map the whole bio and send all scatterlists
> of one bio to crypto engine to encrypt or decrypt, which can improve the
> hardware engine's efficiency.
>
> With this optimization, On my test setup (beaglebone black board) using 64KB
> I/Os on an eMMC storage device I saw about 60% improvement in throughput for
> encrypted writes, and about 100% improvement for encrypted reads. But this
> is not fit for other modes which need different IV for each sector.
>
> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
> ---
> drivers/md/dm-crypt.c | 188 +++++++++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 176 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
> index 4f3cb35..1c86ea7 100644
> --- a/drivers/md/dm-crypt.c
> +++ b/drivers/md/dm-crypt.c
> @@ -33,6 +33,7 @@
> #include <linux/device-mapper.h>
>
> #define DM_MSG_PREFIX "crypt"
> +#define DM_MAX_SG_LIST 1024
>
> /*
> * context holding the current state of a multi-part conversion
> @@ -46,6 +47,8 @@ struct convert_context {
> sector_t cc_sector;
> atomic_t cc_pending;
> struct skcipher_request *req;
> + struct sg_table sgt_in;
> + struct sg_table sgt_out;
> };
>
> /*
> @@ -803,6 +806,108 @@ static struct crypt_iv_operations crypt_iv_tcw_ops = {
> .post = crypt_iv_tcw_post
> };
>
> +/*
> + * Check how many sg entry numbers are needed when map one bio
> + * with scatterlists in advance.
> + */
> +static unsigned int crypt_sg_entry(struct bio *bio_t)
> +{
> + struct request_queue *q = bdev_get_queue(bio_t->bi_bdev);
> + int cluster = blk_queue_cluster(q);
> + struct bio_vec bvec, bvprv = { NULL };
> + struct bvec_iter biter;
> + unsigned long nbytes = 0, sg_length = 0;
> + unsigned int sg_cnt = 0, first_bvec = 0;
> +
> + if (bio_t->bi_rw & REQ_DISCARD) {
> + if (bio_t->bi_vcnt)
> + return 1;
> + return 0;
> + }
> +
> + if (bio_t->bi_rw & REQ_WRITE_SAME)
> + return 1;
> +
> + bio_for_each_segment(bvec, bio_t, biter) {
> + nbytes = bvec.bv_len;
> +
> + if (!cluster) {
> + sg_cnt++;
> + continue;
> + }
> +
> + if (!first_bvec) {
> + first_bvec = 1;
> + goto new_segment;
> + }
> +
> + if (sg_length + nbytes > queue_max_segment_size(q))
> + goto new_segment;
> +
> + if (!BIOVEC_PHYS_MERGEABLE(&bvprv, &bvec))
> + goto new_segment;
> +
> + if (!BIOVEC_SEG_BOUNDARY(q, &bvprv, &bvec))
> + goto new_segment;
> +
> + sg_length += nbytes;
> + continue;
> +
> +new_segment:
> + memcpy(&bvprv, &bvec, sizeof(struct bio_vec));
> + sg_length = nbytes;
> + sg_cnt++;
> + }
> +
> + return sg_cnt;
> +}
> +
> +static int crypt_convert_alloc_table(struct crypt_config *cc,
> + struct convert_context *ctx)
> +{
> + struct bio *bio_in = ctx->bio_in;
> + struct bio *bio_out = ctx->bio_out;
> + unsigned int mode = skcipher_is_bulk_mode(any_tfm(cc));
please use: bool bulk_mode = ...
> + unsigned int sg_in_max, sg_out_max;
> + int ret = 0;
> +
> + if (!mode)
> + goto out2;
Please use more descriptive label names than out[1-3]
> +
> + /*
> + * Need to calculate how many sg entry need to be used
> + * for this bio.
> + */
> + sg_in_max = crypt_sg_entry(bio_in) + 1;
The return from crypt_sg_entry() is pretty awkward, given you just go on
to add 1; as is the bounds checking.. the magic value of 2 needs to be
be made clearer.
> + if (sg_in_max > DM_MAX_SG_LIST || sg_in_max <= 2)
> + goto out2;
> +
> + ret = sg_alloc_table(&ctx->sgt_in, sg_in_max, GFP_KERNEL);
Is it safe to be using GFP_KERNEL here? AFAIK this is in the IO mapping
path and we try to avoid memory allocations at all costs -- due to the
risk of deadlock when issuing IO to stacked block devices (dm-crypt
could be part of a much more elaborate IO stack).
> + if (ret)
> + goto out2;
> +
> + if (bio_data_dir(bio_in) == READ)
> + goto out1;
> +
> + sg_out_max = crypt_sg_entry(bio_out) + 1;
> + if (sg_out_max > DM_MAX_SG_LIST || sg_out_max <= 2)
> + goto out3;
> +
> + ret = sg_alloc_table(&ctx->sgt_out, sg_out_max, GFP_KERNEL);
> + if (ret)
> + goto out3;
> +
> + return 0;
> +
> +out3:
out_free_table?
> + sg_free_table(&ctx->sgt_in);
> +out2:
out_skip_alloc?
> + ctx->sgt_in.orig_nents = 0;
> +out1:
out_skip_write?
> + ctx->sgt_out.orig_nents = 0;
> + return ret;
> +}
> +
> static void crypt_convert_init(struct crypt_config *cc,
> struct convert_context *ctx,
> struct bio *bio_out, struct bio *bio_in,
> @@ -843,7 +948,13 @@ static int crypt_convert_block(struct crypt_config *cc,
> {
> struct bio_vec bv_in = bio_iter_iovec(ctx->bio_in, ctx->iter_in);
> struct bio_vec bv_out = bio_iter_iovec(ctx->bio_out, ctx->iter_out);
> + unsigned int mode = skcipher_is_bulk_mode(any_tfm(cc));
again please use: bool bulk_mode = ...
> + struct bio *bio_in = ctx->bio_in;
> + struct bio *bio_out = ctx->bio_out;
> + unsigned int total_bytes = bio_in->bi_iter.bi_size;
> struct dm_crypt_request *dmreq;
> + struct scatterlist *sg_in;
> + struct scatterlist *sg_out;
> u8 *iv;
> int r;
>
> @@ -852,16 +963,6 @@ static int crypt_convert_block(struct crypt_config *cc,
>
> dmreq->iv_sector = ctx->cc_sector;
> dmreq->ctx = ctx;
> - sg_init_table(&dmreq->sg_in, 1);
> - sg_set_page(&dmreq->sg_in, bv_in.bv_page, 1 << SECTOR_SHIFT,
> - bv_in.bv_offset);
> -
> - sg_init_table(&dmreq->sg_out, 1);
> - sg_set_page(&dmreq->sg_out, bv_out.bv_page, 1 << SECTOR_SHIFT,
> - bv_out.bv_offset);
> -
> - bio_advance_iter(ctx->bio_in, &ctx->iter_in, 1 << SECTOR_SHIFT);
> - bio_advance_iter(ctx->bio_out, &ctx->iter_out, 1 << SECTOR_SHIFT);
>
> if (cc->iv_gen_ops) {
> r = cc->iv_gen_ops->generator(cc, iv, dmreq);
> @@ -869,8 +970,63 @@ static int crypt_convert_block(struct crypt_config *cc,
> return r;
> }
>
> - skcipher_request_set_crypt(req, &dmreq->sg_in, &dmreq->sg_out,
> - 1 << SECTOR_SHIFT, iv);
> + if (mode && ctx->sgt_in.orig_nents > 0) {
> + struct scatterlist *sg = NULL;
> + unsigned int total_sg_in, total_sg_out;
> +
> + total_sg_in = blk_bio_map_sg(bdev_get_queue(bio_in->bi_bdev),
> + bio_in, ctx->sgt_in.sgl, &sg);
> + if ((total_sg_in <= 0) ||
> + (total_sg_in > ctx->sgt_in.orig_nents)) {
> + DMERR("%s in sg map error %d, sg table nents[%d]\n",
> + __func__, total_sg_in, ctx->sgt_in.orig_nents);
> + return -EINVAL;
> + }
> +
> + if (sg)
> + sg_mark_end(sg);
> +
> + ctx->iter_in.bi_size -= total_bytes;
> + sg_in = ctx->sgt_in.sgl;
> + sg_out = ctx->sgt_in.sgl;
> +
> + if (bio_data_dir(bio_in) == READ)
> + goto set_crypt;
> +
> + sg = NULL;
> + total_sg_out = blk_bio_map_sg(bdev_get_queue(bio_out->bi_bdev),
> + bio_out, ctx->sgt_out.sgl, &sg);
> + if ((total_sg_out <= 0) ||
> + (total_sg_out > ctx->sgt_out.orig_nents)) {
> + DMERR("%s out sg map error %d, sg table nents[%d]\n",
> + __func__, total_sg_out, ctx->sgt_out.orig_nents);
> + return -EINVAL;
> + }
> +
> + if (sg)
> + sg_mark_end(sg);
> +
> + ctx->iter_out.bi_size -= total_bytes;
> + sg_out = ctx->sgt_out.sgl;
> + } else {
> + sg_init_table(&dmreq->sg_in, 1);
> + sg_set_page(&dmreq->sg_in, bv_in.bv_page, 1 << SECTOR_SHIFT,
> + bv_in.bv_offset);
> +
> + sg_init_table(&dmreq->sg_out, 1);
> + sg_set_page(&dmreq->sg_out, bv_out.bv_page, 1 << SECTOR_SHIFT,
> + bv_out.bv_offset);
> +
> + bio_advance_iter(ctx->bio_in, &ctx->iter_in, 1 << SECTOR_SHIFT);
> + bio_advance_iter(ctx->bio_out, &ctx->iter_out, 1 << SECTOR_SHIFT);
> +
> + sg_in = &dmreq->sg_in;
> + sg_out = &dmreq->sg_out;
> + total_bytes = 1 << SECTOR_SHIFT;
> + }
> +
> +set_crypt:
> + skcipher_request_set_crypt(req, sg_in, sg_out, total_bytes, iv);
Given how long this code has gotten I'd prefer to see this factored out
to a new setup method.
> if (bio_data_dir(ctx->bio_in) == WRITE)
> r = crypto_skcipher_encrypt(req);
> @@ -1081,6 +1237,8 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
> if (io->ctx.req)
> crypt_free_req(cc, io->ctx.req, base_bio);
>
> + sg_free_table(&io->ctx.sgt_in);
> + sg_free_table(&io->ctx.sgt_out);
> base_bio->bi_error = error;
> bio_endio(base_bio);
> }
> @@ -1312,6 +1470,9 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
> io->ctx.iter_out = clone->bi_iter;
>
> sector += bio_sectors(clone);
> + r = crypt_convert_alloc_table(cc, &io->ctx);
> + if (r < 0)
> + io->error = -EIO;
>
> crypt_inc_pending(io);
> r = crypt_convert(cc, &io->ctx);
> @@ -1343,6 +1504,9 @@ static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
>
> crypt_convert_init(cc, &io->ctx, io->base_bio, io->base_bio,
> io->sector);
> + r = crypt_convert_alloc_table(cc, &io->ctx);
> + if (r < 0)
> + io->error = -EIO;
>
> r = crypt_convert(cc, &io->ctx);
> if (r < 0)
> --
> 1.7.9.5
>
> --
> dm-devel mailing list
> dm-devel@redhat.com
> https://www.redhat.com/mailman/listinfo/dm-devel
^ permalink raw reply
* RE: mdadm remove old md details reassemble working drives
From: Bryan Hepworth @ 2016-05-26 14:21 UTC (permalink / raw)
To: Andreas Klauer; +Cc: linux-raid@vger.kernel.org
In-Reply-To: <20160526114124.GA4028@metamorpher.de>
>-----Original Message-----
>From: Andreas Klauer [mailto:Andreas.Klauer@metamorpher.de]
>Sent: 26 May 2016 12:41
>To: Bryan Hepworth
>Cc: linux-raid@vger.kernel.org
>Subject: Re: mdadm remove old md details reassemble working drives
>
>On Thu, May 26, 2016 at 09:56:24AM +0000, Bryan Hepworth wrote:
>> /etc/mdadm.conf
>>
>> [root@inscribe5 etc]# cat mdadm.conf
>> ARRAY /dev/md/inscribe5.ncl.ac.uk:130 level=raid5 num-devices=12
>metadata=1.2 name=inscribe5.ncl.ac.uk:130
>UUID=cb850648:d5b35007:dc6000f0:b48ab578
>>
>devices=/dev/sdb,/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf,/dev/sdg,/dev/sdh,
>/dev/sdi,/dev/sdj,/dev/sdk,/dev/sdl,/dev/sdm
>> ARRAY /dev/md/igmimager.ncl.ac.uk:136 level=raid5 num-devices=12
>metadata=1.2 name=igmimager.ncl.ac.uk:136
>UUID=b4cb3aaf:e09885cb:6c0ea8bf:a4118a61
>>
>devices=/dev/sdn,/dev/sdo,/dev/sdp,/dev/sdq,/dev/sdr,/dev/sds,/dev/sdt,
>/dev/sdu,/dev/sdv,/dev/sdw,/dev/sdx,/dev/sdy
>
>That looks a bit overengineered...
>
>You can reduce the mdadm.conf to just use UUID and nothing else
>
>ARRAY /dev/md0 UUID=cb850648:d5b35007:dc6000f0:b48ab578
>
>Keep the /dev/md/name if you need it but everything else
>(num-devices, metadata, devices, level, ...) is part
>of the metadata, no need to duplicate it in the conf.
>
>There doesn't seem to be a problem with the metadata itself
>(all clean, same update time, same event count) so unless
>the devices get detected late or something it should just work
>when assembling by UUID...
>
>Regards
>Andreas Klauer
Thanks Andreas
Works like a charm, back up and running thanks so much for your input.
Bryan
^ permalink raw reply
* Re: BLKZEROOUT not zeroing md dev on VMDK
From: Darrick J. Wong @ 2016-05-27 4:18 UTC (permalink / raw)
To: Sitsofe Wheeler
Cc: Shaohua Li, Jens Axboe, Arvind Kumar, VMware PV-Drivers,
linux-raid, linux-scsi, linux-block, linux-kernel
In-Reply-To: <20160518223616.GA409@sucs.org>
On Wed, May 18, 2016 at 11:39:30PM +0100, Sitsofe Wheeler wrote:
> Hi,
>
> With Ubuntu's 4.4.0-22-generic kernel and a Fedora 23
> 4.6.0-1.vanilla.knurd.1.fc23.x86_64 kernel I've found that the
> BLKZEROOUT syscall can malfunction and not zero data.
>
> When BLKZEROOUT is issued to an MD device atop a PVSCSI controller
> supplied VMDK from ESXi 6.0 the call returns immediately and with a zero
> return code. Unfortunately, inspecting the data on the MD device shows
> that it has not been zeroed and is in fact untouched. The easiest way to
> see this behaviour is to boot the VM, create an mdadm device atop
> /dev/sd?, scribble some non-zero value on the disk and then use
> blkdiscard --zeroout /dev/md??? . If you then inspect the MD disk (e.g.
> with hexdump) you will still see the old data and using POSIX_FADV_DONTNEED
> on the MD device doesn't change the outcome.
>
> The only clue I've seen is that
> /sys/block/sd?/queue/write_same_max_bytes starts out being 33553920 but
> after a WRITE SAME is issued it becomes 0. If the MD device is created
> after write_same_max_bytes has become 0 on the backing disk then
> BLKZEROOUT seems to work correctly.
It's possible that the pvscsi device advertised WRITE SAME, but if the device
sends back ILLEGAL REQUEST then the SCSI disk driver will set
write_same_max_bytes=0. Subsequent BLKZEROOUT attempts will then issue writes
of zeroes to the drive.
--D
>
> --
> Sitsofe | http://sucs.org/~sits/
^ permalink raw reply
* Re: BLKZEROOUT not zeroing md dev on VMDK
From: Sitsofe Wheeler @ 2016-05-27 4:45 UTC (permalink / raw)
To: Darrick J. Wong
Cc: Shaohua Li, Jens Axboe, Arvind Kumar, VMware PV-Drivers,
linux-raid, linux-scsi@vger.kernel.org, linux-block,
linux-kernel@vger.kernel.org
In-Reply-To: <20160527041824.GC9418@birch.djwong.org>
On 27 May 2016 at 05:18, Darrick J. Wong <darrick.wong@oracle.com> wrote:
>
> It's possible that the pvscsi device advertised WRITE SAME, but if the device
> sends back ILLEGAL REQUEST then the SCSI disk driver will set
> write_same_max_bytes=0. Subsequent BLKZEROOUT attempts will then issue writes
> of zeroes to the drive.
Thanks for following up on this but that's not what happens on the md
device - you can go on to issue as many BLKZEROOUT requests as you
like but the md disk is never zeroed nor is an error returned.
I filed a bug at https://bugzilla.kernel.org/show_bug.cgi?id=118581
(see https://bugzilla.kernel.org/show_bug.cgi?id=118581#c6 for
alternative reproduction steps that use scsi_debug and can be reworked
to impact device mapper) and Shaohua Li noted that
blkdev_issue_write_same could return 0 even when the disk didn't
support write same (see
https://bugzilla.kernel.org/show_bug.cgi?id=118581#c8 ).
Shaohua went on to create a patch for this ("block: correctly fallback
for zeroout" - https://patchwork.kernel.org/patch/9137311/ ) which has
yet to be reviewed.
--
Sitsofe | http://sucs.org/~sits/
^ permalink raw reply
* [RFC 0/5] raid5-cache: the write cache part
From: Song Liu @ 2016-05-27 5:29 UTC (permalink / raw)
To: linux-raid; +Cc: shli, nfbrown, dan.j.williams, hch, kernel-team, Song Liu
Hi,
This is the caching part of raid5-cache. The journal part was released
with kernel 4.4.
The caching part uses same disk format of raid456 journal, and provides
acceleration to writes. Write operations are committed (bio_endio) once
the data is secured in journal. Reconstruct and RMW are postponed to
reclaim path, which is (hopefully) not on the critical path.
The patch are splitted in 3 major changes: read path (chunk_aligned_read),
write part (the main changes), and a naive reclaim. I have tested read
and write patches (0001-0004), including data-verify in degraded modes.
The reclaim patch still needs some work.
I haven't finished the recovery part of the raid5-cache. But as the
patch set grows, I would like feedback about current changes.
Thanks,
Song
Song Liu (5):
add bio_split_mddev
move stripe cache define and functions to raid5.h
r5cache: look up stripe cache for chunk_aligned_read
r5cache: write part of r5cache
r5cache: naive reclaim approach
drivers/md/md.c | 14 +-
drivers/md/md.h | 2 +
drivers/md/raid5-cache.c | 711 +++++++++++++++++++++++++++++++++++++++++++++--
drivers/md/raid5.c | 264 ++++++++++++------
drivers/md/raid5.h | 101 ++++++-
5 files changed, 990 insertions(+), 102 deletions(-)
--
2.8.0.rc2
^ permalink raw reply
* [RFC 1/5] add bio_split_mddev
From: Song Liu @ 2016-05-27 5:29 UTC (permalink / raw)
To: linux-raid; +Cc: shli, nfbrown, dan.j.williams, hch, kernel-team, Song Liu
In-Reply-To: <1464326983-3798454-1-git-send-email-songliubraving@fb.com>
similar to bio_clone_mddev, bio_alloc_mddev, this patch added
bio_split_mddev, which uses a local bio set.
Signed-off-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Shaohua Li <shli@fb.com>
---
drivers/md/md.c | 14 +++++++++++---
drivers/md/md.h | 2 ++
drivers/md/raid5.c | 2 +-
3 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 866825f..f42f8d0 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -158,10 +158,9 @@ static const struct block_device_operations md_fops;
static int start_readonly;
-/* bio_clone_mddev
- * like bio_clone, but with a local bio set
+/* bio_alloc_mddev, bio_clone_mddev, bio_split_mddev
+ * like bio_alloc, bio_clone, bio_split, but with a local bio set
*/
-
struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
struct mddev *mddev)
{
@@ -187,6 +186,15 @@ struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
}
EXPORT_SYMBOL_GPL(bio_clone_mddev);
+struct bio *bio_split_mddev(struct bio *bio, int sectors,
+ gfp_t gfp, struct mddev *mddev)
+{
+ if (!mddev || !mddev->bio_set)
+ return bio_split(bio, sectors, gfp, NULL);
+ return bio_split(bio, sectors, gfp, mddev->bio_set);
+}
+EXPORT_SYMBOL_GPL(bio_split_mddev);
+
/*
* We have a system wide 'event count' that is incremented
* on any 'interesting' event, and readers of /proc/mdstat
diff --git a/drivers/md/md.h b/drivers/md/md.h
index b5c4be7..9e1d4bf 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -642,6 +642,8 @@ extern struct bio *bio_clone_mddev(struct bio *bio, gfp_t gfp_mask,
struct mddev *mddev);
extern struct bio *bio_alloc_mddev(gfp_t gfp_mask, int nr_iovecs,
struct mddev *mddev);
+extern struct bio *bio_split_mddev(struct bio *bio, int sectors,
+ gfp_t gfp, struct mddev *mddev);
extern void md_unplug(struct blk_plug_cb *cb, bool from_schedule);
extern void md_reload_sb(struct mddev *mddev, int raid_disk);
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index ad9e15a..8e25e67 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -4871,7 +4871,7 @@ static struct bio *chunk_aligned_read(struct mddev *mddev, struct bio *raid_bio)
unsigned sectors = chunk_sects - (sector & (chunk_sects-1));
if (sectors < bio_sectors(raid_bio)) {
- split = bio_split(raid_bio, sectors, GFP_NOIO, fs_bio_set);
+ split = bio_split_mddev(raid_bio, sectors, GFP_NOIO, mddev);
bio_chain(split, raid_bio);
} else
split = raid_bio;
--
2.8.0.rc2
^ permalink raw reply related
* [RFC 2/5] move stripe cache define and functions to raid5.h
From: Song Liu @ 2016-05-27 5:29 UTC (permalink / raw)
To: linux-raid; +Cc: shli, nfbrown, dan.j.williams, hch, kernel-team, Song Liu
In-Reply-To: <1464326983-3798454-1-git-send-email-songliubraving@fb.com>
These defines and functions will be used in r5cache, so move them
to raid5.h.
Signed-off-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Shaohua Li <shli@fb.com>
---
drivers/md/raid5.c | 55 ------------------------------------------------------
drivers/md/raid5.h | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 55 deletions(-)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 8e25e67..dc24b664 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -70,61 +70,6 @@ module_param(devices_handle_discard_safely, bool, 0644);
MODULE_PARM_DESC(devices_handle_discard_safely,
"Set to Y if all devices in each array reliably return zeroes on reads from discarded regions");
static struct workqueue_struct *raid5_wq;
-/*
- * Stripe cache
- */
-
-#define NR_STRIPES 256
-#define STRIPE_SIZE PAGE_SIZE
-#define STRIPE_SHIFT (PAGE_SHIFT - 9)
-#define STRIPE_SECTORS (STRIPE_SIZE>>9)
-#define IO_THRESHOLD 1
-#define BYPASS_THRESHOLD 1
-#define NR_HASH (PAGE_SIZE / sizeof(struct hlist_head))
-#define HASH_MASK (NR_HASH - 1)
-#define MAX_STRIPE_BATCH 8
-
-static inline struct hlist_head *stripe_hash(struct r5conf *conf, sector_t sect)
-{
- int hash = (sect >> STRIPE_SHIFT) & HASH_MASK;
- return &conf->stripe_hashtbl[hash];
-}
-
-static inline int stripe_hash_locks_hash(sector_t sect)
-{
- return (sect >> STRIPE_SHIFT) & STRIPE_HASH_LOCKS_MASK;
-}
-
-static inline void lock_device_hash_lock(struct r5conf *conf, int hash)
-{
- spin_lock_irq(conf->hash_locks + hash);
- spin_lock(&conf->device_lock);
-}
-
-static inline void unlock_device_hash_lock(struct r5conf *conf, int hash)
-{
- spin_unlock(&conf->device_lock);
- spin_unlock_irq(conf->hash_locks + hash);
-}
-
-static inline void lock_all_device_hash_locks_irq(struct r5conf *conf)
-{
- int i;
- local_irq_disable();
- spin_lock(conf->hash_locks);
- for (i = 1; i < NR_STRIPE_HASH_LOCKS; i++)
- spin_lock_nest_lock(conf->hash_locks + i, conf->hash_locks);
- spin_lock(&conf->device_lock);
-}
-
-static inline void unlock_all_device_hash_locks_irq(struct r5conf *conf)
-{
- int i;
- spin_unlock(&conf->device_lock);
- for (i = NR_STRIPE_HASH_LOCKS; i; i--)
- spin_unlock(conf->hash_locks + i - 1);
- local_irq_enable();
-}
/* bio's attached to a stripe+device for I/O are linked together in bi_sector
* order without overlap. There may be several bio's per stripe+device, and
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 517d4b6..3b68d4f 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -616,6 +616,61 @@ static inline int algorithm_is_DDF(int layout)
return layout >= 8 && layout <= 10;
}
+/*
+ * Stripe cache
+ */
+#define NR_STRIPES 256
+#define STRIPE_SIZE PAGE_SIZE
+#define STRIPE_SHIFT (PAGE_SHIFT - 9)
+#define STRIPE_SECTORS (STRIPE_SIZE>>9)
+#define IO_THRESHOLD 1
+#define BYPASS_THRESHOLD 1
+#define NR_HASH (PAGE_SIZE / sizeof(struct hlist_head))
+#define HASH_MASK (NR_HASH - 1)
+#define MAX_STRIPE_BATCH 8
+
+static inline struct hlist_head *stripe_hash(struct r5conf *conf, sector_t sect)
+{
+ int hash = (sect >> STRIPE_SHIFT) & HASH_MASK;
+ return &conf->stripe_hashtbl[hash];
+}
+
+static inline int stripe_hash_locks_hash(sector_t sect)
+{
+ return (sect >> STRIPE_SHIFT) & STRIPE_HASH_LOCKS_MASK;
+}
+
+static inline void lock_device_hash_lock(struct r5conf *conf, int hash)
+{
+ spin_lock_irq(conf->hash_locks + hash);
+ spin_lock(&conf->device_lock);
+}
+
+static inline void unlock_device_hash_lock(struct r5conf *conf, int hash)
+{
+ spin_unlock(&conf->device_lock);
+ spin_unlock_irq(conf->hash_locks + hash);
+}
+
+static inline void lock_all_device_hash_locks_irq(struct r5conf *conf)
+{
+ int i;
+ local_irq_disable();
+ spin_lock(conf->hash_locks);
+ for (i = 1; i < NR_STRIPE_HASH_LOCKS; i++)
+ spin_lock_nest_lock(conf->hash_locks + i, conf->hash_locks);
+ spin_lock(&conf->device_lock);
+}
+
+static inline void unlock_all_device_hash_locks_irq(struct r5conf *conf)
+{
+ int i;
+ spin_unlock(&conf->device_lock);
+ for (i = NR_STRIPE_HASH_LOCKS; i; i--)
+ spin_unlock(conf->hash_locks + i - 1);
+ local_irq_enable();
+}
+
extern void md_raid5_kick_device(struct r5conf *conf);
extern int raid5_set_cache_size(struct mddev *mddev, int size);
extern sector_t raid5_compute_blocknr(struct stripe_head *sh, int i, int previous);
--
2.8.0.rc2
^ permalink raw reply related
* [RFC 3/5] r5cache: look up stripe cache for chunk_aligned_read
From: Song Liu @ 2016-05-27 5:29 UTC (permalink / raw)
To: linux-raid; +Cc: shli, nfbrown, dan.j.williams, hch, kernel-team, Song Liu
In-Reply-To: <1464326983-3798454-1-git-send-email-songliubraving@fb.com>
This is the read part of raid5 cache (r5cache).
In raid456, when the array is in-sync, the md layer bypasses stripe
cache for chunk_aligned_read(). However, with write back cache,
data in the RAID disks may not be uptodate. Therefore, it is necessary
to search the stripe cache latest data.
With this patch, raid5_read_one_chunk() looks up data in stripe cache.
The outcome of this lookup could be read_full_hit (all data of the
chunk are in stripe cache), read_partial_hit (only part of the chunk
is in stripe cache), or read_miss (no data of the chunk in stripe cache).
For read_full_hit, raid5_read_one_chunk returns data directly from
stripe cache; for read_miss, raid5_read_one_chunk reads all data from
the disk; for read_partial_hit, raid5_read_one_chunk reads data from
disk, and amends the data with data in stripe cache in endio
(r5c_chunk_aligned_read_endio).
Sysfs entry is added to show statistics of read_full_hits,
read_partial_hits, and read_misses.
Signed-off-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Shaohua Li <shli@fb.com>
---
drivers/md/raid5-cache.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/md/raid5.c | 23 ++++-
drivers/md/raid5.h | 6 ++
3 files changed, 265 insertions(+), 2 deletions(-)
diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
index e889e2d..5f0d96f 100644
--- a/drivers/md/raid5-cache.c
+++ b/drivers/md/raid5-cache.c
@@ -40,8 +40,15 @@
*/
#define R5L_POOL_SIZE 4
+struct r5c_cache {
+ atomic64_t read_full_hits; /* the whole chunk in cache */
+ atomic64_t read_partial_hits; /* some pages of the chunk in cache */
+ atomic64_t read_misses; /* the whold chunk is not in cache */
+};
+
struct r5l_log {
struct md_rdev *rdev;
+ struct r5c_cache cache;
u32 uuid_checksum;
@@ -134,6 +141,21 @@ enum r5l_io_unit_state {
IO_UNIT_STRIPE_END = 3, /* stripes data finished writing to raid */
};
+struct r5c_chunk_map {
+ int sh_count;
+ struct r5conf *conf;
+ struct bio *parent_bi;
+ int dd_idx;
+ struct stripe_head *sh_array[0];
+};
+
+static void init_r5c_cache(struct r5conf *conf, struct r5c_cache *cache)
+{
+ atomic64_set(&cache->read_full_hits, 0);
+ atomic64_set(&cache->read_partial_hits, 0);
+ atomic64_set(&cache->read_misses, 0);
+}
+
static sector_t r5l_ring_add(struct r5l_log *log, sector_t start, sector_t inc)
{
start += inc;
@@ -1120,6 +1142,220 @@ static void r5l_write_super(struct r5l_log *log, sector_t cp)
set_bit(MD_CHANGE_DEVS, &mddev->flags);
}
+/* TODO: use async copy */
+static void r5c_copy_data_to_bvec(struct r5dev *rdev, int sh_offset,
+ struct bio_vec *bvec, int bvec_offset, int len)
+{
+ /* We always copy data from orig_page. This is because in R-M-W, we use
+ * page to do prexor of parity */
+ void *src_p = kmap_atomic(rdev->orig_page);
+ void *dst_p = kmap_atomic(bvec->bv_page);
+ memcpy(dst_p + bvec_offset, src_p + sh_offset, len);
+ kunmap_atomic(dst_p);
+ kunmap_atomic(src_p);
+}
+
+/*
+ * copy data from a chunk_map to a bio
+ */
+static void r5c_copy_chunk_map_to_bio(struct r5c_chunk_map *chunk_map,
+ struct bio *bio)
+{
+ struct bvec_iter iter;
+ struct bio_vec bvec;
+ int sh_idx;
+ unsigned sh_offset;
+
+ sh_idx = 0;
+ sh_offset = (bio->bi_iter.bi_sector & ((sector_t)STRIPE_SECTORS-1)) << 9;
+
+ /*
+ * If bio is not page aligned, the chunk_map will have 1 more sh than bvecs
+ * in the bio. Chunk_map may also have NULL-sh. To copy the right data, we
+ * need to walk through the chunk_map carefully. In this implementation,
+ * bvec/bvec_offset always matches with sh_array[sh_idx]/sh_offset.
+ *
+ * In the following example, the nested loop will run 4 times; and
+ * r5c_copy_data_to_bvec will be called for the first and last iteration.
+ *
+ * --------------------------------
+ * chunk_map | valid sh | NULL | valid sh |
+ * --------------------------------
+ * ---------------------
+ * bio | | |
+ * ---------------------
+ *
+ * | | | | |
+ * copy_data | Y | N | N | Y |
+ */
+ bio_for_each_segment(bvec, bio, iter) {
+ int len;
+ unsigned bvec_offset = bvec.bv_offset;
+ while (bvec_offset < PAGE_SIZE) {
+ len = min_t(unsigned, PAGE_SIZE - bvec_offset, PAGE_SIZE - sh_offset);
+ if (chunk_map->sh_array[sh_idx])
+ r5c_copy_data_to_bvec(&chunk_map->sh_array[sh_idx]->dev[chunk_map->dd_idx], sh_offset,
+ &bvec, bvec_offset, len);
+ bvec_offset += len;
+ sh_offset += len;
+ if (sh_offset == PAGE_SIZE) {
+ sh_idx += 1;
+ sh_offset = 0;
+ }
+ }
+ }
+ return;
+}
+
+/*
+ * release stripes in chunk_map and free the chunk_map
+ */
+static void free_r5c_chunk_map(struct r5c_chunk_map *chunk_map)
+{
+ unsigned sh_idx;
+ struct stripe_head *sh;
+
+ for (sh_idx = 0; sh_idx < chunk_map->sh_count; ++sh_idx) {
+ sh = chunk_map->sh_array[sh_idx];
+ if (sh) {
+ set_bit(STRIPE_HANDLE, &sh->state);
+ raid5_release_stripe(sh);
+ }
+ }
+ kfree(chunk_map);
+}
+
+static void r5c_chunk_aligned_read_endio(struct bio *bio)
+{
+ struct r5c_chunk_map *chunk_map = (struct r5c_chunk_map *) bio->bi_private;
+ struct bio *parent_bi = chunk_map->parent_bi;
+
+ r5c_copy_chunk_map_to_bio(chunk_map, bio);
+ free_r5c_chunk_map(chunk_map);
+ bio_put(bio);
+ bio_endio(parent_bi);
+}
+
+/*
+ * look up bio in stripe cache
+ * return raid_bio -> no data in cache, read the chunk from disk
+ * return new r5c_bio -> partial data in cache, read from disk, and amend in r5c_align_endio
+ * return NULL -> all data in cache, no need to read disk
+ */
+struct bio *r5c_lookup_chunk(struct r5l_log *log, struct bio *raid_bio)
+{
+ struct r5conf *conf;
+ sector_t logical_sector;
+ sector_t first_stripe, last_stripe; /* first (inclusive) stripe and last (exclusive) */
+ int dd_idx;
+ struct stripe_head *sh;
+ unsigned sh_count, sh_idx, sh_cached;
+ struct r5c_chunk_map *chunk_map;
+ struct bio *r5c_bio;
+ int hash;
+ unsigned long flags;
+
+ if (!log)
+ return raid_bio;
+
+ conf = log->rdev->mddev->private;
+
+ logical_sector = raid_bio->bi_iter.bi_sector &
+ ~((sector_t)STRIPE_SECTORS-1);
+ sh_count = DIV_ROUND_UP_SECTOR_T(bio_end_sector(raid_bio) - logical_sector, STRIPE_SECTORS);
+
+ first_stripe = raid5_compute_sector(conf, logical_sector, 0, &dd_idx, NULL);
+ last_stripe = first_stripe + STRIPE_SECTORS * sh_count;
+
+ chunk_map = kzalloc(sizeof(struct r5c_chunk_map) + sh_count * sizeof(struct stripe_head*), GFP_NOIO);
+ sh_cached = 0;
+
+ for (sh_idx = 0; sh_idx < sh_count; ++sh_idx) {
+ hash = stripe_hash_locks_hash(first_stripe + sh_idx * STRIPE_SECTORS);
+ spin_lock_irqsave(conf->hash_locks + hash, flags);
+ sh = __find_stripe(conf, first_stripe + sh_idx * STRIPE_SECTORS, conf->generation);
+ if (sh &&
+ test_bit(R5_UPTODATE, &sh->dev[dd_idx].flags)) {
+ if (!atomic_inc_not_zero(&sh->count)) {
+ spin_lock(&conf->device_lock);
+ if (!atomic_read(&sh->count)) {
+ if (!test_bit(STRIPE_HANDLE, &sh->state))
+ atomic_inc(&conf->active_stripes);
+ BUG_ON(list_empty(&sh->lru) &&
+ !test_bit(STRIPE_EXPANDING, &sh->state));
+ list_del_init(&sh->lru);
+ if (sh->group) {
+ sh->group->stripes_cnt--;
+ sh->group = NULL;
+ }
+ }
+ atomic_inc(&sh->count);
+ spin_unlock(&conf->device_lock);
+ }
+ chunk_map->sh_array[sh_idx] = sh;
+ ++sh_cached;
+ }
+ spin_unlock_irqrestore(conf->hash_locks + hash, flags);
+ }
+
+ if (sh_cached == 0) {
+ atomic64_inc(&log->cache.read_misses);
+ kfree(chunk_map);
+ return raid_bio;
+ }
+
+ chunk_map->sh_count = sh_count;
+ chunk_map->dd_idx = dd_idx;
+
+ if (sh_cached == sh_count) {
+ atomic64_inc(&log->cache.read_full_hits);
+ r5c_copy_chunk_map_to_bio(chunk_map, raid_bio);
+ free_r5c_chunk_map(chunk_map);
+ bio_endio(raid_bio);
+ return NULL;
+ }
+
+ chunk_map->parent_bi = raid_bio;
+ chunk_map->conf = conf;
+
+ atomic64_inc(&log->cache.read_partial_hits);
+
+ /* TODO: handle bio_clone failure? */
+ r5c_bio = bio_clone_mddev(raid_bio, GFP_NOIO, log->rdev->mddev);
+
+ r5c_bio->bi_private = chunk_map;
+ r5c_bio->bi_end_io = r5c_chunk_aligned_read_endio;
+
+ return r5c_bio;
+}
+
+ssize_t
+r5c_stat_show(struct mddev *mddev, char* page)
+{
+ struct r5conf *conf = mddev->private;
+ struct r5l_log *log;
+ int ret = 0;
+
+ if (!conf)
+ return 0;
+
+ log = conf->log;
+
+ if (!log)
+ return 0;
+
+ ret += snprintf(page + ret, PAGE_SIZE - ret, "r5c_read_full_hits: %llu\n",
+ (unsigned long long) atomic64_read(&log->cache.read_full_hits));
+
+ ret += snprintf(page + ret, PAGE_SIZE - ret, "r5c_read_partial_hits: %llu\n",
+ (unsigned long long) atomic64_read(&log->cache.read_partial_hits));
+
+ ret += snprintf(page + ret, PAGE_SIZE - ret, "r5c_read_misses: %llu\n",
+ (unsigned long long) atomic64_read(&log->cache.read_misses));
+
+ return ret;
+}
+
static int r5l_load_log(struct r5l_log *log)
{
struct md_rdev *rdev = log->rdev;
@@ -1239,6 +1475,8 @@ int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev)
INIT_LIST_HEAD(&log->no_space_stripes);
spin_lock_init(&log->no_space_stripes_lock);
+ init_r5c_cache(conf, &log->cache);
+
if (r5l_load_log(log))
goto error;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index dc24b664..cdd9c4b 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -503,7 +503,7 @@ retry:
set_bit(STRIPE_BATCH_READY, &sh->state);
}
-static struct stripe_head *__find_stripe(struct r5conf *conf, sector_t sector,
+struct stripe_head *__find_stripe(struct r5conf *conf, sector_t sector,
short generation)
{
struct stripe_head *sh;
@@ -515,6 +515,7 @@ static struct stripe_head *__find_stripe(struct r5conf *conf, sector_t sector,
pr_debug("__stripe %llu not in cache\n", (unsigned long long)sector);
return NULL;
}
+EXPORT_SYMBOL(__find_stripe);
/*
* Need to check if array has failed when deciding whether to:
@@ -4726,7 +4727,8 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio)
{
struct r5conf *conf = mddev->private;
int dd_idx;
- struct bio* align_bi;
+ struct bio *align_bi;
+ struct bio *r5c_bio;
struct md_rdev *rdev;
sector_t end_sector;
@@ -4734,6 +4736,18 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio)
pr_debug("%s: non aligned\n", __func__);
return 0;
}
+
+ r5c_bio = r5c_lookup_chunk(conf->log, raid_bio);
+
+ if (r5c_bio == NULL) {
+ pr_debug("Read all data from stripe cache\n");
+ return 1;
+ } else if (r5c_bio == raid_bio)
+ pr_debug("No data in stripe cache, read all from disk\n");
+ else {
+ pr_debug("Partial data in stripe cache, read and amend\n");
+ raid_bio = r5c_bio;
+ }
/*
* use bio_clone_mddev to make a copy of the bio
*/
@@ -6157,6 +6171,10 @@ raid5_group_thread_cnt = __ATTR(group_thread_cnt, S_IRUGO | S_IWUSR,
raid5_show_group_thread_cnt,
raid5_store_group_thread_cnt);
+static struct md_sysfs_entry
+r5c_cache_stats = __ATTR(r5c_cache_stats, S_IRUGO,
+ r5c_stat_show, NULL);
+
static struct attribute *raid5_attrs[] = {
&raid5_stripecache_size.attr,
&raid5_stripecache_active.attr,
@@ -6164,6 +6182,7 @@ static struct attribute *raid5_attrs[] = {
&raid5_group_thread_cnt.attr,
&raid5_skip_copy.attr,
&raid5_rmw_level.attr,
+ &r5c_cache_stats.attr,
NULL,
};
static struct attribute_group raid5_attrs_group = {
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 3b68d4f..de11514 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -690,4 +690,10 @@ extern void r5l_stripe_write_finished(struct stripe_head *sh);
extern int r5l_handle_flush_request(struct r5l_log *log, struct bio *bio);
extern void r5l_quiesce(struct r5l_log *log, int state);
extern bool r5l_log_disk_error(struct r5conf *conf);
+
+extern struct bio *r5c_lookup_chunk(struct r5l_log *log, struct bio *raid_bio);
+extern struct stripe_head *__find_stripe(struct r5conf *conf, sector_t sector,
+ short generation);
+
+extern ssize_t r5c_stat_show(struct mddev *mddev, char* page);
#endif
--
2.8.0.rc2
^ permalink raw reply related
* [RFC 4/5] r5cache: write part of r5cache
From: Song Liu @ 2016-05-27 5:29 UTC (permalink / raw)
To: linux-raid; +Cc: shli, nfbrown, dan.j.williams, hch, kernel-team, Song Liu
In-Reply-To: <1464326983-3798454-1-git-send-email-songliubraving@fb.com>
This is the write part of r5cache. The cache is integrated with
stripe cache of raid456. It leverages code of r5l_log to write
data to journal device.
r5cache split current write path into 2 parts: the write path
and the reclaim path. The write path is as following:
1. write data to journal
2. call bio_endio
Then the reclaim path is as:
1. Freeze the stripe (no more writes coming in)
2. Calcualte parity (reconstruct or RMW)
3. Write parity to journal device (data is already written to it)
4. Write data and parity to RAID disks
With r5cache, write operation does not wait for parity calculation
and write out, so the write latency is lower (1 write to journal
device vs. read and then write to raid disks). Also, r5cache will
reduce RAID overhead (multipile IO due to read-modify-write of
parity) and provide more opportunities of full stripe writes.
r5cache adds a new state of each stripe: enum r5c_states. The write
path runs in state CLEAN and RUNNING (data in cache). Cache writes
start from r5c_handle_stripe_dirtying(), where bit R5_Wantcache is
set for devices with bio in towrite. Then, the data is written to
the journal through r5l_log implementation. Once the data is in the
journal, we set bit R5_InCache, and presue bio_endio for these
writes.
The reclaim path starts by freezing the stripe (no more writes).
This stripes back to raid5 state machine, where
handle_stripe_dirtying will evaluate the stripe for reconstruct
writes or RMW writes (read data and calculate parity).
For RMW, the code allocates extra page for prexor. Specifically,
a new page is allocated for r5dev->page to do prexor; while
r5dev->orig_page keeps the cached data. The extra page is freed
after prexor.
r5cache naturally excludes SkipCopy. With R5_Wantcache bit set,
async_copy_data will not skip copy.
Before writing data to RAID disks, the r5l_log logic stores
parity (and non-overwrite data) to the journal.
Instead of inactive_list, stripes with cached data are tracked in
r5conf->r5c_cached_list. r5conf->r5c_cached_stripes tracks how
many stripes has dirty data in the cache.
Two sysfs entries are provided for the write cache:
1. r5c_cached_stripes shows how many stripes have cached data.
Writing anything to r5c_cached_stripes will flush all data
to RAID disks.
2. r5c_cache_mode provides knob to switch the system between
write-back or write-through (only write-log).
There are some known limitations of the cache implementation:
1. Write cache only covers full page writes (R5_OVERWRITE). Writes
of smaller granularity are write through.
2. Only one log io (sh->log_io) for each stripe at anytime. Later
writes for the same stripe have to wait. This can be improved by
moving log_io to r5dev.
Signed-off-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Shaohua Li <shli@fb.com>
---
drivers/md/raid5-cache.c | 399 +++++++++++++++++++++++++++++++++++++++++++++--
drivers/md/raid5.c | 172 +++++++++++++++++---
drivers/md/raid5.h | 38 ++++-
3 files changed, 577 insertions(+), 32 deletions(-)
diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
index 5f0d96f..66a3cd5 100644
--- a/drivers/md/raid5-cache.c
+++ b/drivers/md/raid5-cache.c
@@ -40,7 +40,19 @@
*/
#define R5L_POOL_SIZE 4
+enum r5c_cache_mode {
+ R5C_MODE_NO_CACHE = 0,
+ R5C_MODE_WRITE_THROUGH = 1,
+ R5C_MODE_WRITE_BACK = 2,
+};
+
+static char *r5c_cache_mode_str[] = {"no-cache", "write-through", "write-back"};
+
struct r5c_cache {
+ int flush_threshold; /* flush the stripe when flush_threshold buffers are dirty */
+ int mode; /* enum r5c_cache_mode */
+
+ /* read stats */
atomic64_t read_full_hits; /* the whole chunk in cache */
atomic64_t read_partial_hits; /* some pages of the chunk in cache */
atomic64_t read_misses; /* the whold chunk is not in cache */
@@ -151,11 +163,23 @@ struct r5c_chunk_map {
static void init_r5c_cache(struct r5conf *conf, struct r5c_cache *cache)
{
+ cache->flush_threshold = conf->raid_disks - conf->max_degraded; /* full stripe */
+ cache->mode = R5C_MODE_WRITE_BACK;
+
atomic64_set(&cache->read_full_hits, 0);
atomic64_set(&cache->read_partial_hits, 0);
atomic64_set(&cache->read_misses, 0);
}
+void r5c_set_state(struct stripe_head *sh, enum r5c_states new_state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sh->stripe_lock, flags);
+ sh->r5c_state = new_state;
+ spin_unlock_irqrestore(&sh->stripe_lock, flags);
+}
+
static sector_t r5l_ring_add(struct r5l_log *log, sector_t start, sector_t inc)
{
start += inc;
@@ -191,12 +215,74 @@ static void __r5l_set_io_unit_state(struct r5l_io_unit *io,
io->state = state;
}
+void r5c_freeze_stripe_for_reclaim(struct stripe_head *sh)
+{
+ struct r5conf *conf = sh->raid_conf;
+
+ if (!conf->log)
+ return;
+
+ WARN_ON(sh->r5c_state >= R5C_STATE_FROZEN);
+ r5c_set_state(sh, R5C_STATE_FROZEN);
+ if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ atomic_inc(&conf->preread_active_stripes);
+ if (test_and_clear_bit(STRIPE_IN_R5C_CACHE, &sh->state)) {
+ BUG_ON(atomic_read(&conf->r5c_cached_stripes) == 0);
+ atomic_dec(&conf->r5c_cached_stripes);
+ }
+}
+
+static void r5c_handle_data_cached(struct stripe_head *sh)
+{
+ int i;
+
+ for (i = sh->disks; i--; )
+ if (test_and_clear_bit(R5_Wantcache, &sh->dev[i].flags)) {
+ set_bit(R5_InCache, &sh->dev[i].flags);
+ clear_bit(R5_LOCKED, &sh->dev[i].flags);
+ atomic_inc(&sh->dev_in_cache);
+ }
+}
+
+/*
+ * this journal write must contain full parity,
+ * it may also contain data of none-overwrites
+ */
+static void r5c_handle_parity_cached(struct stripe_head *sh)
+{
+ int i;
+
+ for (i = sh->disks; i--; )
+ if (test_bit(R5_InCache, &sh->dev[i].flags))
+ set_bit(R5_Wantwrite, &sh->dev[i].flags);
+ r5c_set_state(sh, R5C_STATE_PARITY_DONE);
+}
+
+static void r5c_finish_cache_stripe(struct stripe_head *sh)
+{
+ switch (sh->r5c_state) {
+ case R5C_STATE_PARITY_RUN:
+ r5c_handle_parity_cached(sh);
+ break;
+ case R5C_STATE_CLEAN:
+ r5c_set_state(sh, R5C_STATE_RUNNING);
+ case R5C_STATE_RUNNING:
+ r5c_handle_data_cached(sh);
+ break;
+ default:
+ BUG();
+ }
+}
+
static void r5l_io_run_stripes(struct r5l_io_unit *io)
{
struct stripe_head *sh, *next;
list_for_each_entry_safe(sh, next, &io->stripe_list, log_list) {
list_del_init(&sh->log_list);
+
+ r5c_finish_cache_stripe(sh);
+
set_bit(STRIPE_HANDLE, &sh->state);
raid5_release_stripe(sh);
}
@@ -425,7 +511,10 @@ static int r5l_log_stripe(struct r5l_log *log, struct stripe_head *sh,
io = log->current_io;
for (i = 0; i < sh->disks; i++) {
- if (!test_bit(R5_Wantwrite, &sh->dev[i].flags))
+ if (!test_bit(R5_Wantwrite, &sh->dev[i].flags) &&
+ !test_bit(R5_Wantcache, &sh->dev[i].flags))
+ continue;
+ if (test_bit(R5_InCache, &sh->dev[i].flags))
continue;
if (i == sh->pd_idx || i == sh->qd_idx)
continue;
@@ -435,18 +524,19 @@ static int r5l_log_stripe(struct r5l_log *log, struct stripe_head *sh,
r5l_append_payload_page(log, sh->dev[i].page);
}
- if (sh->qd_idx >= 0) {
+ if (parity_pages == 2) {
r5l_append_payload_meta(log, R5LOG_PAYLOAD_PARITY,
sh->sector, sh->dev[sh->pd_idx].log_checksum,
sh->dev[sh->qd_idx].log_checksum, true);
r5l_append_payload_page(log, sh->dev[sh->pd_idx].page);
r5l_append_payload_page(log, sh->dev[sh->qd_idx].page);
- } else {
+ } else if (parity_pages == 1) {
r5l_append_payload_meta(log, R5LOG_PAYLOAD_PARITY,
sh->sector, sh->dev[sh->pd_idx].log_checksum,
0, false);
r5l_append_payload_page(log, sh->dev[sh->pd_idx].page);
- }
+ } else
+ BUG_ON(parity_pages != 0);
list_add_tail(&sh->log_list, &io->stripe_list);
atomic_inc(&io->pending_stripe);
@@ -455,7 +545,6 @@ static int r5l_log_stripe(struct r5l_log *log, struct stripe_head *sh,
return 0;
}
-static void r5l_wake_reclaim(struct r5l_log *log, sector_t space);
/*
* running in raid5d, where reclaim could wait for raid5d too (when it flushes
* data from log to raid disks), so we shouldn't wait for reclaim here
@@ -471,6 +560,7 @@ int r5l_write_stripe(struct r5l_log *log, struct stripe_head *sh)
if (!log)
return -EAGAIN;
+
/* Don't support stripe batch */
if (sh->log_io || !test_bit(R5_Wantwrite, &sh->dev[sh->pd_idx].flags) ||
test_bit(STRIPE_SYNCING, &sh->state)) {
@@ -479,11 +569,17 @@ int r5l_write_stripe(struct r5l_log *log, struct stripe_head *sh)
return -EAGAIN;
}
+ WARN_ON(sh->r5c_state < R5C_STATE_FROZEN);
+
for (i = 0; i < sh->disks; i++) {
void *addr;
if (!test_bit(R5_Wantwrite, &sh->dev[i].flags))
continue;
+
+ if (test_bit(R5_InCache, &sh->dev[i].flags))
+ continue;
+
write_disks++;
/* checksum is already calculated in last run */
if (test_bit(STRIPE_LOG_TRAPPED, &sh->state))
@@ -496,6 +592,9 @@ int r5l_write_stripe(struct r5l_log *log, struct stripe_head *sh)
parity_pages = 1 + !!(sh->qd_idx >= 0);
data_pages = write_disks - parity_pages;
+ pr_debug("%s: write %d data_pages and %d parity_pages\n",
+ __func__, data_pages, parity_pages);
+
meta_size =
((sizeof(struct r5l_payload_data_parity) + sizeof(__le32))
* data_pages) +
@@ -766,7 +865,6 @@ static void r5l_write_super_and_discard_space(struct r5l_log *log,
}
}
-
static void r5l_do_reclaim(struct r5l_log *log)
{
sector_t reclaim_target = xchg(&log->reclaim_target, 0);
@@ -826,10 +924,15 @@ static void r5l_reclaim_thread(struct md_thread *thread)
if (!log)
return;
+/*
+ spin_lock_irq(&conf->device_lock);
+ r5c_do_reclaim(conf);
+ spin_unlock_irq(&conf->device_lock);
+*/
r5l_do_reclaim(log);
}
-static void r5l_wake_reclaim(struct r5l_log *log, sector_t space)
+void r5l_wake_reclaim(struct r5l_log *log, sector_t space)
{
unsigned long target;
unsigned long new = (unsigned long)space; /* overflow in theory */
@@ -1274,8 +1377,7 @@ struct bio *r5c_lookup_chunk(struct r5l_log *log, struct bio *raid_bio)
hash = stripe_hash_locks_hash(first_stripe + sh_idx * STRIPE_SECTORS);
spin_lock_irqsave(conf->hash_locks + hash, flags);
sh = __find_stripe(conf, first_stripe + sh_idx * STRIPE_SECTORS, conf->generation);
- if (sh &&
- test_bit(R5_UPTODATE, &sh->dev[dd_idx].flags)) {
+ if (sh && test_bit(R5_UPTODATE, &sh->dev[dd_idx].flags)) {
if (!atomic_inc_not_zero(&sh->count)) {
spin_lock(&conf->device_lock);
if (!atomic_read(&sh->count)) {
@@ -1356,6 +1458,285 @@ r5c_stat_show(struct mddev *mddev, char* page)
return ret;
}
+static void r5c_flush_stripe(struct r5conf *conf, struct stripe_head *sh)
+{
+ list_del_init(&sh->lru);
+ r5c_freeze_stripe_for_reclaim(sh);
+ atomic_inc(&conf->active_stripes);
+ atomic_inc(&sh->count);
+ set_bit(STRIPE_HANDLE, &sh->state);
+ raid5_release_stripe(sh);
+}
+
+int r5c_flush_cache(struct r5conf *conf)
+{
+ int count = 0;
+
+ if (!conf->log)
+ return 0;
+ while(!list_empty(&conf->r5c_cached_list)) {
+ struct list_head *l = conf->r5c_cached_list.next;
+ struct stripe_head *sh;
+ sh = list_entry(l, struct stripe_head, lru);
+ r5c_flush_stripe(conf, sh);
+ ++count;
+ }
+ return count;
+}
+
+ssize_t
+r5c_cached_stripes_show(struct mddev *mddev, char* page)
+{
+ struct r5conf *conf = mddev->private;
+ int ret = 0;
+
+ if (!conf)
+ return 0;
+
+ ret += snprintf(page + ret, PAGE_SIZE - ret, "r5c_cached_stripes: %llu\n",
+ (unsigned long long) atomic_read(&conf->r5c_cached_stripes));
+ return ret;
+}
+
+ssize_t r5l_show_need_cache_flush(struct mddev *mddev, char *page)
+{
+ struct r5conf *conf = mddev->private;
+ struct r5l_log *log = conf->log;
+ int ret = 0;
+ int val;
+
+ if (!log)
+ val = 0;
+ else
+ val = log->need_cache_flush;
+
+ ret += snprintf(page + ret, PAGE_SIZE - ret, "%d\n", val);
+ return ret;
+}
+
+ssize_t r5l_store_need_cache_flush(struct mddev *mddev, const char *page, size_t len)
+{
+ struct r5conf *conf = mddev->private;
+ struct r5l_log *log = conf->log;
+ int val;
+
+ if (!log)
+ return -EINVAL;
+
+ if (kstrtoint(page, 10, &val))
+ return -EINVAL;
+
+ if (val > 1 || val < 0)
+ return -EINVAL;
+
+ log->need_cache_flush = val;
+ return len;
+}
+
+ssize_t
+r5c_cached_stripes_store(struct mddev *mddev, const char *page, size_t len)
+{
+ struct r5conf *conf = mddev->private;
+
+ spin_lock_irq(&conf->device_lock);
+ r5c_flush_cache(conf); /* flush cache regardless of any input, TODO: change this*/
+ spin_unlock_irq(&conf->device_lock);
+
+ md_wakeup_thread(mddev->thread);
+ return len;
+}
+
+ssize_t r5c_show_cache_mode(struct mddev *mddev, char *page)
+{
+ struct r5conf *conf = mddev->private;
+ int val = 0;
+ int ret = 0;
+
+ if (conf->log)
+ val = conf->log->cache.mode;
+ ret += snprintf(page, PAGE_SIZE - ret, "%d: %s\n", val, r5c_cache_mode_str[val]);
+ return ret;
+}
+
+ssize_t r5c_store_cache_mode(struct mddev *mddev, const char *page, size_t len)
+{
+ struct r5conf *conf = mddev->private;
+ int val;
+
+ if (!conf->log)
+ return -EINVAL;
+ if (kstrtoint(page, 10, &val))
+ return -EINVAL;
+ if (val < R5C_MODE_WRITE_THROUGH || val > R5C_MODE_WRITE_BACK)
+ return -EINVAL;
+ spin_lock_irq(&conf->device_lock);
+ conf->log->cache.mode = val;
+ spin_unlock_irq(&conf->device_lock);
+ printk(KERN_INFO "%s: setting r5c cache mode to %d: %s\n", mdname(mddev), val, r5c_cache_mode_str[val]);
+ return len;
+}
+
+int r5c_handle_stripe_dirtying(struct r5conf *conf,
+ struct stripe_head *sh,
+ struct stripe_head_state *s,
+ int disks) {
+ struct r5l_log *log = conf->log;
+ int i;
+ struct r5dev *dev;
+
+ if (!log || sh->r5c_state >= R5C_STATE_FROZEN)
+ return -EAGAIN;
+
+ if (conf->log->cache.mode == R5C_MODE_WRITE_THROUGH || conf->quiesce != 0 || conf->mddev->degraded != 0) {
+ /* write through mode */
+ r5c_freeze_stripe_for_reclaim(sh);
+ return -EAGAIN;
+ }
+
+ s->to_cache = 0;
+
+ for (i = disks; i--; ) {
+ dev = &sh->dev[i];
+ /* if none-overwrite, use the reclaim path (write through) */
+ if (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags) &&
+ !test_bit(R5_InCache, &dev->flags)) {
+ r5c_freeze_stripe_for_reclaim(sh);
+ return -EAGAIN;
+ }
+ }
+
+ for (i = disks; i--; ) {
+ dev = &sh->dev[i];
+ if (dev->towrite) {
+ set_bit(R5_Wantcache, &dev->flags);
+ set_bit(R5_Wantdrain, &dev->flags);
+ set_bit(R5_LOCKED, &dev->flags);
+ s->to_cache++;
+ }
+ }
+
+ if (s->to_cache)
+ set_bit(STRIPE_OP_BIODRAIN, &s->ops_request);
+
+ return 0;
+}
+
+void r5c_handle_stripe_flush(struct r5conf *conf,
+ struct stripe_head *sh,
+ struct stripe_head_state *s,
+ int disks) {
+ int i;
+ int do_wakeup = 0;
+
+ if (sh->r5c_state == R5C_STATE_PARITY_DONE) {
+ r5c_set_state(sh, R5C_STATE_INRAID);
+ for (i = disks; i--; ) {
+ clear_bit(R5_InCache, &sh->dev[i].flags);
+ clear_bit(R5_UPTODATE, &sh->dev[i].flags);
+ if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
+ do_wakeup = 1;
+ }
+ }
+ if (do_wakeup)
+ wake_up(&conf->wait_for_overlap);
+}
+
+int
+r5c_cache_data(struct r5l_log *log, struct stripe_head *sh,
+ struct stripe_head_state *s)
+{
+ int pages;
+ int meta_size;
+ int reserve;
+ int i;
+ int ret = 0;
+ int page_count = 0;
+
+ BUG_ON(!log);
+ BUG_ON(s->to_cache == 0);
+
+ for (i = 0; i < sh->disks; i++) {
+ void *addr;
+ if (!test_bit(R5_Wantcache, &sh->dev[i].flags))
+ continue;
+ addr = kmap_atomic(sh->dev[i].page);
+ sh->dev[i].log_checksum = crc32c_le(log->uuid_checksum,
+ addr, PAGE_SIZE);
+ kunmap_atomic(addr);
+ page_count++;
+ }
+ WARN_ON(page_count != s->to_cache);
+
+ pages = s->to_cache;
+
+ meta_size =
+ ((sizeof(struct r5l_payload_data_parity) + sizeof(__le32))
+ * pages);
+ /* Doesn't work with very big raid array */
+ if (meta_size + sizeof(struct r5l_meta_block) > PAGE_SIZE)
+ return -EINVAL;
+
+ /*
+ * The stripe must enter state machine again to call endio, so
+ * don't delay.
+ */
+ clear_bit(STRIPE_DELAYED, &sh->state);
+ atomic_inc(&sh->count);
+
+ mutex_lock(&log->io_mutex);
+ /* meta + data */
+ reserve = (1 + pages) << (PAGE_SHIFT - 9);
+ if (!r5l_has_free_space(log, reserve)) {
+ spin_lock(&log->no_space_stripes_lock);
+ list_add_tail(&sh->log_list, &log->no_space_stripes);
+ spin_unlock(&log->no_space_stripes_lock);
+
+ r5l_wake_reclaim(log, reserve);
+ } else {
+ ret = r5l_log_stripe(log, sh, pages, 0);
+ if (ret) {
+ spin_lock_irq(&log->io_list_lock);
+ list_add_tail(&sh->log_list, &log->no_mem_stripes);
+ spin_unlock_irq(&log->io_list_lock);
+ }
+ }
+
+ mutex_unlock(&log->io_mutex);
+ return 0;
+}
+
+static void r5c_adjust_flush_threshold(struct r5conf *conf)
+{
+ struct r5l_log *log = conf->log;
+ int new_thres = conf->raid_disks - conf->max_degraded;
+
+ if (atomic_read(&conf->r5c_cached_stripes) * 2 > conf->max_nr_stripes)
+ new_thres = 1;
+ else if (atomic_read(&conf->r5c_cached_stripes) * 4 > conf->max_nr_stripes)
+ new_thres /= 2;
+ else if (atomic_read(&conf->r5c_cached_stripes) * 8 > conf->max_nr_stripes)
+ new_thres -= 1;
+
+ if (new_thres >= 1)
+ log->cache.flush_threshold = new_thres;
+}
+
+void r5c_do_reclaim(struct r5conf *conf)
+{
+ struct stripe_head *sh, *next;
+ struct r5l_log *log = conf->log;
+
+ assert_spin_locked(&conf->device_lock);
+
+ if (!log)
+ return;
+
+ r5c_adjust_flush_threshold(conf);
+ list_for_each_entry_safe(sh, next, &conf->r5c_cached_list, lru)
+ if (atomic_read(&sh->dev_in_cache) >= log->cache.flush_threshold)
+ r5c_flush_stripe(conf, sh);
+}
+
static int r5l_load_log(struct r5l_log *log)
{
struct md_rdev *rdev = log->rdev;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index cdd9c4b..127c5c2 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -261,8 +261,16 @@ static void do_release_stripe(struct r5conf *conf, struct stripe_head *sh,
< IO_THRESHOLD)
md_wakeup_thread(conf->mddev->thread);
atomic_dec(&conf->active_stripes);
- if (!test_bit(STRIPE_EXPANDING, &sh->state))
- list_add_tail(&sh->lru, temp_inactive_list);
+ if (!test_bit(STRIPE_EXPANDING, &sh->state)) {
+ if (sh->r5c_state == R5C_STATE_CLEAN)
+ list_add_tail(&sh->lru, temp_inactive_list);
+ else {
+ if (!test_and_set_bit(STRIPE_IN_R5C_CACHE, &sh->state)) {
+ atomic_inc(&conf->r5c_cached_stripes);
+ }
+ list_add_tail(&sh->lru, &conf->r5c_cached_list);
+ }
+ }
}
}
@@ -650,6 +658,7 @@ raid5_get_active_stripe(struct r5conf *conf, sector_t sector,
BUG_ON(list_empty(&sh->lru) &&
!test_bit(STRIPE_EXPANDING, &sh->state));
list_del_init(&sh->lru);
+
if (sh->group) {
sh->group->stripes_cnt--;
sh->group = NULL;
@@ -834,6 +843,11 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s)
might_sleep();
+ if (s->to_cache) {
+ r5c_cache_data(conf->log, sh, s);
+ return;
+ }
+
if (r5l_write_stripe(conf->log, sh) == 0)
return;
for (i = disks; i--; ) {
@@ -964,6 +978,7 @@ again:
if (test_bit(R5_SkipCopy, &sh->dev[i].flags))
WARN_ON(test_bit(R5_UPTODATE, &sh->dev[i].flags));
+
sh->dev[i].vec.bv_page = sh->dev[i].page;
bi->bi_vcnt = 1;
bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
@@ -1051,7 +1066,7 @@ again:
static struct dma_async_tx_descriptor *
async_copy_data(int frombio, struct bio *bio, struct page **page,
sector_t sector, struct dma_async_tx_descriptor *tx,
- struct stripe_head *sh)
+ struct stripe_head *sh, int no_skipcopy)
{
struct bio_vec bvl;
struct bvec_iter iter;
@@ -1091,7 +1106,8 @@ async_copy_data(int frombio, struct bio *bio, struct page **page,
if (frombio) {
if (sh->raid_conf->skip_copy &&
b_offset == 0 && page_offset == 0 &&
- clen == STRIPE_SIZE)
+ clen == STRIPE_SIZE &&
+ !no_skipcopy)
*page = bio_page;
else
tx = async_memcpy(*page, bio_page, page_offset,
@@ -1173,7 +1189,7 @@ static void ops_run_biofill(struct stripe_head *sh)
while (rbi && rbi->bi_iter.bi_sector <
dev->sector + STRIPE_SECTORS) {
tx = async_copy_data(0, rbi, &dev->page,
- dev->sector, tx, sh);
+ dev->sector, tx, sh, 0);
rbi = r5_next_bio(rbi, dev->sector);
}
}
@@ -1300,7 +1316,8 @@ static int set_syndrome_sources(struct page **srcs,
if (i == sh->qd_idx || i == sh->pd_idx ||
(srctype == SYNDROME_SRC_ALL) ||
(srctype == SYNDROME_SRC_WANT_DRAIN &&
- test_bit(R5_Wantdrain, &dev->flags)) ||
+ (test_bit(R5_Wantdrain, &dev->flags) ||
+ test_bit(R5_InCache, &dev->flags))) ||
(srctype == SYNDROME_SRC_WRITTEN &&
dev->written))
srcs[slot] = sh->dev[i].page;
@@ -1479,9 +1496,17 @@ ops_run_compute6_2(struct stripe_head *sh, struct raid5_percpu *percpu)
static void ops_complete_prexor(void *stripe_head_ref)
{
struct stripe_head *sh = stripe_head_ref;
+ int i;
pr_debug("%s: stripe %llu\n", __func__,
(unsigned long long)sh->sector);
+
+ for (i = sh->disks; i--; )
+ if (sh->dev[i].page != sh->dev[i].orig_page) {
+ struct page *p = sh->dev[i].page;
+ sh->dev[i].page = sh->dev[i].orig_page;
+ put_page(p);
+ }
}
static struct dma_async_tx_descriptor *
@@ -1503,7 +1528,7 @@ ops_run_prexor5(struct stripe_head *sh, struct raid5_percpu *percpu,
for (i = disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
/* Only process blocks that are known to be uptodate */
- if (test_bit(R5_Wantdrain, &dev->flags))
+ if (test_bit(R5_Wantdrain, &dev->flags) || test_bit(R5_InCache, &dev->flags))
xor_srcs[count++] = dev->page;
}
@@ -1554,6 +1579,10 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx)
again:
dev = &sh->dev[i];
+ if (test_and_clear_bit(R5_InCache, &dev->flags)) {
+ BUG_ON(atomic_read(&sh->dev_in_cache) == 0);
+ atomic_dec(&sh->dev_in_cache);
+ }
spin_lock_irq(&sh->stripe_lock);
chosen = dev->towrite;
dev->towrite = NULL;
@@ -1561,7 +1590,8 @@ again:
BUG_ON(dev->written);
wbi = dev->written = chosen;
spin_unlock_irq(&sh->stripe_lock);
- WARN_ON(dev->page != dev->orig_page);
+ if (!test_bit(R5_Wantcache, &dev->flags))
+ WARN_ON(dev->page != dev->orig_page);
while (wbi && wbi->bi_iter.bi_sector <
dev->sector + STRIPE_SECTORS) {
@@ -1573,8 +1603,9 @@ again:
set_bit(R5_Discard, &dev->flags);
else {
tx = async_copy_data(1, wbi, &dev->page,
- dev->sector, tx, sh);
- if (dev->page != dev->orig_page) {
+ dev->sector, tx, sh, test_bit(R5_Wantcache, &dev->flags));
+ if (dev->page != dev->orig_page &&
+ !test_bit(R5_Wantcache, &dev->flags)) {
set_bit(R5_SkipCopy, &dev->flags);
clear_bit(R5_UPTODATE, &dev->flags);
clear_bit(R5_OVERWRITE, &dev->flags);
@@ -1682,7 +1713,7 @@ again:
xor_dest = xor_srcs[count++] = sh->dev[pd_idx].page;
for (i = disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
- if (head_sh->dev[i].written)
+ if (head_sh->dev[i].written || test_bit(R5_InCache, &head_sh->dev[i].flags))
xor_srcs[count++] = dev->page;
}
} else {
@@ -1935,6 +1966,7 @@ static struct stripe_head *alloc_stripe(struct kmem_cache *sc, gfp_t gfp)
INIT_LIST_HEAD(&sh->batch_list);
INIT_LIST_HEAD(&sh->lru);
atomic_set(&sh->count, 1);
+ atomic_set(&sh->dev_in_cache, 0);
}
return sh;
}
@@ -2816,6 +2848,9 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s,
if (!expand)
clear_bit(R5_UPTODATE, &dev->flags);
s->locked++;
+ } else if (test_bit(R5_InCache, &dev->flags)) {
+ set_bit(R5_LOCKED, &dev->flags);
+ s->locked++;
}
}
/* if we are not expanding this is a proper write request, and
@@ -2855,6 +2890,9 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s,
set_bit(R5_LOCKED, &dev->flags);
clear_bit(R5_UPTODATE, &dev->flags);
s->locked++;
+ } else if (test_bit(R5_InCache, &dev->flags)) {
+ set_bit(R5_LOCKED, &dev->flags);
+ s->locked++;
}
}
if (!s->locked)
@@ -2913,6 +2951,8 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx,
*/
spin_lock_irq(&sh->stripe_lock);
/* Don't allow new IO added to stripes in batch list */
+ if (sh->r5c_state >= R5C_STATE_FROZEN)
+ goto overlap;
if (sh->batch_head)
goto overlap;
if (forwrite) {
@@ -3502,6 +3542,9 @@ static void handle_stripe_dirtying(struct r5conf *conf,
int rmw = 0, rcw = 0, i;
sector_t recovery_cp = conf->mddev->recovery_cp;
+ if (r5c_handle_stripe_dirtying(conf, sh, s, disks) == 0)
+ return;
+
/* Check whether resync is now happening or should start.
* If yes, then the array is dirty (after unclean shutdown or
* initial creation), so parity in some stripes might be inconsistent.
@@ -3522,9 +3565,11 @@ static void handle_stripe_dirtying(struct r5conf *conf,
} else for (i = disks; i--; ) {
/* would I have to read this buffer for read_modify_write */
struct r5dev *dev = &sh->dev[i];
- if ((dev->towrite || i == sh->pd_idx || i == sh->qd_idx) &&
+ if ((dev->towrite || i == sh->pd_idx || i == sh->qd_idx ||
+ test_bit(R5_InCache, &dev->flags)) &&
!test_bit(R5_LOCKED, &dev->flags) &&
- !(test_bit(R5_UPTODATE, &dev->flags) ||
+ /* !(test_bit(R5_UPTODATE, &dev->flags) || */
+ !((test_bit(R5_UPTODATE, &dev->flags) && (!test_bit(R5_InCache, &dev->flags) || (dev->page != dev->orig_page))) ||
test_bit(R5_Wantcompute, &dev->flags))) {
if (test_bit(R5_Insync, &dev->flags))
rmw++;
@@ -3536,13 +3581,15 @@ static void handle_stripe_dirtying(struct r5conf *conf,
i != sh->pd_idx && i != sh->qd_idx &&
!test_bit(R5_LOCKED, &dev->flags) &&
!(test_bit(R5_UPTODATE, &dev->flags) ||
- test_bit(R5_Wantcompute, &dev->flags))) {
+ test_bit(R5_InCache, &dev->flags) ||
+ test_bit(R5_Wantcompute, &dev->flags))) {
if (test_bit(R5_Insync, &dev->flags))
rcw++;
else
rcw += 2*disks;
}
}
+
pr_debug("for sector %llu, rmw=%d rcw=%d\n",
(unsigned long long)sh->sector, rmw, rcw);
set_bit(STRIPE_HANDLE, &sh->state);
@@ -3554,10 +3601,16 @@ static void handle_stripe_dirtying(struct r5conf *conf,
(unsigned long long)sh->sector, rmw);
for (i = disks; i--; ) {
struct r5dev *dev = &sh->dev[i];
- if ((dev->towrite || i == sh->pd_idx || i == sh->qd_idx) &&
+ if (test_bit(R5_InCache, &dev->flags) &&
+ dev->page == dev->orig_page)
+ dev->page = alloc_page(GFP_NOIO); /* allocate page for prexor */
+
+ if ((dev->towrite || i == sh->pd_idx || i == sh->qd_idx ||
+ test_bit(R5_InCache, &dev->flags)) &&
!test_bit(R5_LOCKED, &dev->flags) &&
- !(test_bit(R5_UPTODATE, &dev->flags) ||
- test_bit(R5_Wantcompute, &dev->flags)) &&
+ /* !(test_bit(R5_UPTODATE, &dev->flags) || */
+ !((test_bit(R5_UPTODATE, &dev->flags) && (!test_bit(R5_InCache, &dev->flags) || (dev->page != dev->orig_page))) ||
+ test_bit(R5_Wantcompute, &dev->flags)) &&
test_bit(R5_Insync, &dev->flags)) {
if (test_bit(STRIPE_PREREAD_ACTIVE,
&sh->state)) {
@@ -3583,6 +3636,7 @@ static void handle_stripe_dirtying(struct r5conf *conf,
i != sh->pd_idx && i != sh->qd_idx &&
!test_bit(R5_LOCKED, &dev->flags) &&
!(test_bit(R5_UPTODATE, &dev->flags) ||
+ test_bit(R5_InCache, &dev->flags) ||
test_bit(R5_Wantcompute, &dev->flags))) {
rcw++;
if (test_bit(R5_Insync, &dev->flags) &&
@@ -3610,6 +3664,10 @@ static void handle_stripe_dirtying(struct r5conf *conf,
!test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
set_bit(STRIPE_DELAYED, &sh->state);
+ if ((sh->r5c_state == R5C_STATE_FROZEN) && (rmw == 0 || rcw == 0)) {
+ r5c_set_state(sh, R5C_STATE_PARITY_RUN);
+ }
+
/* now if nothing is locked, and if we have enough data,
* we can start a write request
*/
@@ -3622,7 +3680,7 @@ static void handle_stripe_dirtying(struct r5conf *conf,
*/
if ((s->req_compute || !test_bit(STRIPE_COMPUTE_RUN, &sh->state)) &&
(s->locked == 0 && (rcw == 0 || rmw == 0) &&
- !test_bit(STRIPE_BIT_DELAY, &sh->state)))
+ !test_bit(STRIPE_BIT_DELAY, &sh->state)))
schedule_reconstruction(sh, s, rcw == 0, 0);
}
@@ -3935,6 +3993,38 @@ static void handle_stripe_expansion(struct r5conf *conf, struct stripe_head *sh)
async_tx_quiesce(&tx);
}
+static void r5c_return_dev_pending_writes(struct r5conf *conf, struct r5dev *dev,
+ struct bio_list *return_bi)
+{
+ struct bio *wbi, *wbi2;
+
+ wbi = dev->written;
+ dev->written = NULL;
+ while (wbi && wbi->bi_iter.bi_sector <
+ dev->sector + STRIPE_SECTORS) {
+ wbi2 = r5_next_bio(wbi, dev->sector);
+ if (!raid5_dec_bi_active_stripes(wbi)) {
+ md_write_end(conf->mddev);
+ bio_list_add(return_bi, wbi);
+ }
+ wbi = wbi2;
+ }
+}
+
+static void r5c_handle_cached_data_endio(struct r5conf *conf,
+ struct stripe_head *sh, int disks, struct bio_list *return_bi)
+{
+ int i;
+
+ for (i = sh->disks; i--; ) {
+ if (test_bit(R5_InCache, &sh->dev[i].flags) && sh->dev[i].written) {
+ set_bit(R5_UPTODATE, &sh->dev[i].flags);
+ r5c_return_dev_pending_writes(conf, &sh->dev[i], return_bi);
+ }
+ }
+ r5l_stripe_write_finished(sh);
+}
+
/*
* handle_stripe - do things to a stripe.
*
@@ -4113,6 +4203,13 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
if (rdev && !test_bit(Faulty, &rdev->flags))
do_recovery = 1;
}
+ if (test_bit(R5_InCache, &dev->flags)) {
+ s->in_cache++;
+ if (dev->written)
+ s->just_cached++;
+ if (i == sh->pd_idx || i == sh->qd_idx)
+ s->parity_cached++;
+ }
}
if (test_bit(STRIPE_SYNCING, &sh->state)) {
/* If there is a failed device being replaced,
@@ -4341,7 +4438,7 @@ static void handle_stripe(struct stripe_head *sh)
struct r5dev *dev = &sh->dev[i];
if (test_bit(R5_LOCKED, &dev->flags) &&
(i == sh->pd_idx || i == sh->qd_idx ||
- dev->written)) {
+ dev->written || test_bit(R5_InCache, &dev->flags))) {
pr_debug("Writing block %d\n", i);
set_bit(R5_Wantwrite, &dev->flags);
if (prexor)
@@ -4381,6 +4478,17 @@ static void handle_stripe(struct stripe_head *sh)
test_bit(R5_Discard, &qdev->flags))))))
handle_stripe_clean_event(conf, sh, disks, &s.return_bi);
+ if (s.just_cached)
+ r5c_handle_cached_data_endio(conf, sh, disks, &s.return_bi);
+
+ if (sh->r5c_state == R5C_STATE_PARITY_DONE)
+ r5l_stripe_write_finished(sh);
+
+ if (s.parity_cached == sh->raid_conf->max_degraded) {
+ r5c_set_state(sh, R5C_STATE_PARITY_DONE);
+ r5l_stripe_write_finished(sh);
+ }
+
/* Now we might consider reading some blocks, either to check/generate
* parity, or to satisfy requests
* or to load a block that is being partially written.
@@ -4392,13 +4500,17 @@ static void handle_stripe(struct stripe_head *sh)
|| s.expanding)
handle_stripe_fill(sh, &s, disks);
- /* Now to consider new write requests and what else, if anything
- * should be read. We do not handle new writes when:
+ r5c_handle_stripe_flush(conf, sh, &s, disks);
+
+ /* Now to consider new write requests, cache write back and what else,
+ * if anything should be read. We do not handle new writes when:
* 1/ A 'write' operation (copy+xor) is already in flight.
* 2/ A 'check' operation is in flight, as it may clobber the parity
* block.
+ * 3/ A r5c cache log write is in flight.
*/
- if (s.to_write && !sh->reconstruct_state && !sh->check_state)
+ if ((s.to_write || sh->r5c_state == R5C_STATE_FROZEN) &&
+ !sh->reconstruct_state && !sh->check_state && !sh->log_io)
handle_stripe_dirtying(conf, sh, &s, disks);
/* maybe we need to check and possibly fix the parity for this stripe
@@ -5853,6 +5965,7 @@ static void raid5d(struct md_thread *thread)
md_check_recovery(mddev);
spin_lock_irq(&conf->device_lock);
}
+ r5c_do_reclaim(conf);
}
pr_debug("%d stripes handled\n", handled);
@@ -6175,6 +6288,14 @@ static struct md_sysfs_entry
r5c_cache_stats = __ATTR(r5c_cache_stats, S_IRUGO,
r5c_stat_show, NULL);
+static struct md_sysfs_entry
+r5c_cached_stripes = __ATTR(r5c_cached_stripes, S_IRUGO | S_IWUSR,
+ r5c_cached_stripes_show, r5c_cached_stripes_store);
+
+static struct md_sysfs_entry
+r5c_cache_mode = __ATTR(r5c_cache_mode, S_IRUGO | S_IWUSR,
+ r5c_show_cache_mode, r5c_store_cache_mode);
+
static struct attribute *raid5_attrs[] = {
&raid5_stripecache_size.attr,
&raid5_stripecache_active.attr,
@@ -6183,6 +6304,8 @@ static struct attribute *raid5_attrs[] = {
&raid5_skip_copy.attr,
&raid5_rmw_level.attr,
&r5c_cache_stats.attr,
+ &r5c_cached_stripes.attr,
+ &r5c_cache_mode.attr,
NULL,
};
static struct attribute_group raid5_attrs_group = {
@@ -6524,6 +6647,9 @@ static struct r5conf *setup_conf(struct mddev *mddev)
for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++)
INIT_LIST_HEAD(conf->temp_inactive_list + i);
+ atomic_set(&conf->r5c_cached_stripes, 0);
+ INIT_LIST_HEAD(&conf->r5c_cached_list);
+
conf->level = mddev->new_level;
conf->chunk_sectors = mddev->new_chunk_sectors;
if (raid5_alloc_percpu(conf) != 0)
@@ -7578,8 +7704,10 @@ static void raid5_quiesce(struct mddev *mddev, int state)
/* '2' tells resync/reshape to pause so that all
* active stripes can drain
*/
+ r5c_flush_cache(conf);
conf->quiesce = 2;
wait_event_cmd(conf->wait_for_quiescent,
+ atomic_read(&conf->r5c_cached_stripes) == 0 &&
atomic_read(&conf->active_stripes) == 0 &&
atomic_read(&conf->active_aligned_reads) == 0,
unlock_all_device_hash_locks_irq(conf),
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index de11514..78fcc6d 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -194,6 +194,15 @@ enum reconstruct_states {
reconstruct_state_result,
};
+enum r5c_states {
+ R5C_STATE_CLEAN = 0, /* all data in RAID HDDs are up to date */
+ R5C_STATE_RUNNING = 1, /* stripe can accept new writes */
+ R5C_STATE_FROZEN = 2, /* Doesn't accept new data and is reclaiming */
+ R5C_STATE_PARITY_RUN = 3, /* stripe parity is being written to cache disk */
+ R5C_STATE_PARITY_DONE = 4, /* stripe parity is in cache disk */
+ R5C_STATE_INRAID = R5C_STATE_CLEAN, /* stripe data/parity are in raid disks */
+};
+
struct stripe_head {
struct hlist_node hash;
struct list_head lru; /* inactive_list or handle_list */
@@ -216,6 +225,7 @@ struct stripe_head {
*/
enum check_states check_state;
enum reconstruct_states reconstruct_state;
+ enum r5c_states r5c_state;
spinlock_t stripe_lock;
int cpu;
struct r5worker_group *group;
@@ -226,6 +236,7 @@ struct stripe_head {
struct r5l_io_unit *log_io;
struct list_head log_list;
+ atomic_t dev_in_cache;
/**
* struct stripe_operations
* @target - STRIPE_OP_COMPUTE_BLK target
@@ -263,6 +274,7 @@ struct stripe_head_state {
*/
int syncing, expanding, expanded, replacing;
int locked, uptodate, to_read, to_write, failed, written;
+ int to_cache, in_cache, just_cached, parity_cached;
int to_fill, compute, req_compute, non_overwrite;
int failed_num[2];
int p_failed, q_failed;
@@ -313,6 +325,8 @@ enum r5dev_flags {
*/
R5_Discard, /* Discard the stripe */
R5_SkipCopy, /* Don't copy data from bio to stripe cache */
+ R5_Wantcache, /* Want write data to write cache */
+ R5_InCache, /* Data in cache */
};
/*
@@ -345,7 +359,8 @@ enum {
STRIPE_BITMAP_PENDING, /* Being added to bitmap, don't add
* to batch yet.
*/
- STRIPE_LOG_TRAPPED, /* trapped into log */
+ STRIPE_LOG_TRAPPED, /* trapped into log */
+ STRIPE_IN_R5C_CACHE, /* in r5c cache (to-be/being handled or in conf->r5c_cached_list) */
};
#define STRIPE_EXPAND_SYNC_FLAGS \
@@ -521,6 +536,8 @@ struct r5conf {
*/
atomic_t active_stripes;
struct list_head inactive_list[NR_STRIPE_HASH_LOCKS];
+ atomic_t r5c_cached_stripes;
+ struct list_head r5c_cached_list;
atomic_t empty_inactive_list_nr;
struct llist_head released_stripes;
wait_queue_head_t wait_for_quiescent;
@@ -690,10 +707,29 @@ extern void r5l_stripe_write_finished(struct stripe_head *sh);
extern int r5l_handle_flush_request(struct r5l_log *log, struct bio *bio);
extern void r5l_quiesce(struct r5l_log *log, int state);
extern bool r5l_log_disk_error(struct r5conf *conf);
+extern void r5l_wake_reclaim(struct r5l_log *log, sector_t space);
extern struct bio *r5c_lookup_chunk(struct r5l_log *log, struct bio *raid_bio);
extern struct stripe_head *__find_stripe(struct r5conf *conf, sector_t sector,
short generation);
extern ssize_t r5c_stat_show(struct mddev *mddev, char* page);
+
+extern int r5c_handle_stripe_dirtying(struct r5conf *conf, struct stripe_head *sh,
+ struct stripe_head_state *s, int disks);
+extern int r5c_cache_data(struct r5l_log *log, struct stripe_head *sh,
+ struct stripe_head_state *s);
+extern int r5c_cache_parity(struct r5l_log *log, struct stripe_head *sh,
+ struct stripe_head_state *s);
+extern void r5c_handle_stripe_flush(struct r5conf *conf, struct stripe_head *sh,
+ struct stripe_head_state *s, int disks);
+extern void r5c_freeze_stripe_for_reclaim(struct stripe_head *sh);
+extern void r5c_do_reclaim(struct r5conf *conf);
+
+extern ssize_t r5c_cached_stripes_show(struct mddev *mddev, char* page);
+extern ssize_t r5c_cached_stripes_store(struct mddev *mddev, const char *page, size_t len);
+extern int r5c_flush_cache(struct r5conf *conf);
+extern ssize_t r5c_show_cache_mode(struct mddev *mddev, char *page);
+extern ssize_t r5c_store_cache_mode(struct mddev *mddev, const char *page, size_t len);
+extern void r5c_set_state(struct stripe_head *sh, enum r5c_states new_state);
#endif
--
2.8.0.rc2
^ permalink raw reply related
* [RFC 5/5] r5cache: naive reclaim approach
From: Song Liu @ 2016-05-27 5:29 UTC (permalink / raw)
To: linux-raid; +Cc: shli, nfbrown, dan.j.williams, hch, kernel-team, Song Liu
In-Reply-To: <1464326983-3798454-1-git-send-email-songliubraving@fb.com>
This patch adds a naive reclaim for r5c cache.
There are two limited resources, stripe cache and journal disk space.
For better performance, we priotize reclaim of stripes with more data
in cache. To free up more journal space, we free earliest data on
the journal.
The reclaim thread (r5l_reclaim_thread) wakes up every 5 seconds. In
this thread, r5c_do_reclaim reclaims stripe cache space, while
r5l_do_reclaim reclaims journal space.
r5c_cache keeps all data in cache (not fully committed to RAID) in
a list (stripe_in_cache). These stripes are in the order of their
first appearence on the journal. So the log tail (last_checkpoint)
should point to the journal_start of the first item in the list.
Signed-off-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Shaohua Li <shli@fb.com>
---
drivers/md/raid5-cache.c | 96 ++++++++++++++++++++++++++++++++++++++----------
drivers/md/raid5.c | 14 ++++++-
drivers/md/raid5.h | 2 +
3 files changed, 91 insertions(+), 21 deletions(-)
diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
index 66a3cd5..272c109 100644
--- a/drivers/md/raid5-cache.c
+++ b/drivers/md/raid5-cache.c
@@ -34,6 +34,9 @@
#define RECLAIM_MAX_FREE_SPACE (10 * 1024 * 1024 * 2) /* sector */
#define RECLAIM_MAX_FREE_SPACE_SHIFT (2)
+/* wake up reclaim thread periodically */
+#define RECLAIM_WAKEUP_INTERVAL (5 * HZ)
+
/*
* We only need 2 bios per I/O unit to make progress, but ensure we
* have a few more available to not get too tight.
@@ -52,6 +55,11 @@ struct r5c_cache {
int flush_threshold; /* flush the stripe when flush_threshold buffers are dirty */
int mode; /* enum r5c_cache_mode */
+ struct list_head stripe_in_cache; /* all stripes in the cache, with sh->journal_start in order */
+ spinlock_t stripe_in_cache_lock; /* lock for stripe_in_cache */
+
+ sector_t first_sector; /* first useful data on journal */
+
/* read stats */
atomic64_t read_full_hits; /* the whole chunk in cache */
atomic64_t read_partial_hits; /* some pages of the chunk in cache */
@@ -165,6 +173,8 @@ static void init_r5c_cache(struct r5conf *conf, struct r5c_cache *cache)
{
cache->flush_threshold = conf->raid_disks - conf->max_degraded; /* full stripe */
cache->mode = R5C_MODE_WRITE_BACK;
+ INIT_LIST_HEAD(&cache->stripe_in_cache);
+ spin_lock_init(&cache->stripe_in_cache_lock);
atomic64_set(&cache->read_full_hits, 0);
atomic64_set(&cache->read_partial_hits, 0);
@@ -497,6 +507,7 @@ static int r5l_log_stripe(struct r5l_log *log, struct stripe_head *sh,
int meta_size;
int ret;
struct r5l_io_unit *io;
+ unsigned long flags;
meta_size =
((sizeof(struct r5l_payload_data_parity) + sizeof(__le32))
@@ -542,6 +553,16 @@ static int r5l_log_stripe(struct r5l_log *log, struct stripe_head *sh,
atomic_inc(&io->pending_stripe);
sh->log_io = io;
+ spin_lock_irqsave(&log->cache.stripe_in_cache_lock, flags);
+ spin_lock(&sh->stripe_lock);
+ if (sh->journal_start == -1L) {
+ BUG_ON(!list_empty(&sh->r5c));
+ sh->journal_start = log->next_checkpoint;
+ list_add_tail(&sh->r5c,
+ &log->cache.stripe_in_cache);
+ }
+ spin_unlock(&sh->stripe_lock);
+ spin_unlock_irqrestore(&log->cache.stripe_in_cache_lock, flags);
return 0;
}
@@ -863,6 +884,10 @@ static void r5l_write_super_and_discard_space(struct r5l_log *log,
blkdev_issue_discard(bdev, log->rdev->data_offset, end,
GFP_NOIO, 0);
}
+ mutex_lock(&log->io_mutex);
+ log->last_checkpoint = end;
+ log->last_cp_seq = log->next_cp_seq;
+ mutex_unlock(&log->io_mutex);
}
static void r5l_do_reclaim(struct r5l_log *log)
@@ -901,19 +926,32 @@ static void r5l_do_reclaim(struct r5l_log *log)
if (reclaimable == 0)
return;
- /*
- * write_super will flush cache of each raid disk. We must write super
- * here, because the log area might be reused soon and we don't want to
- * confuse recovery
- */
- r5l_write_super_and_discard_space(log, next_checkpoint);
+ r5l_run_no_space_stripes(log);
+}
- mutex_lock(&log->io_mutex);
- log->last_checkpoint = next_checkpoint;
- log->last_cp_seq = next_cp_seq;
- mutex_unlock(&log->io_mutex);
+static void r5c_update_super(struct r5conf *conf)
+{
+ struct list_head *l;
+ struct stripe_head *sh;
+ struct r5l_log *log = conf->log;
+ sector_t end = -1L;
+ unsigned long flags;
- r5l_run_no_space_stripes(log);
+ if (list_empty(&conf->log->cache.stripe_in_cache)) {
+ /* all stripes flushed */
+ r5l_write_super_and_discard_space(log, log->next_checkpoint);
+ return;
+ }
+ spin_lock_irqsave(&log->cache.stripe_in_cache_lock, flags);
+ l = conf->log->cache.stripe_in_cache.next;
+ sh = list_entry(l, struct stripe_head, r5c);
+ spin_lock(&sh->stripe_lock);
+ end = sh->journal_start;
+ spin_unlock(&sh->stripe_lock);
+ spin_unlock_irqrestore(&log->cache.stripe_in_cache_lock, flags);
+
+ if (end != log->last_checkpoint && end != -1L)
+ r5l_write_super_and_discard_space(log, sh->journal_start);
}
static void r5l_reclaim_thread(struct md_thread *thread)
@@ -924,12 +962,11 @@ static void r5l_reclaim_thread(struct md_thread *thread)
if (!log)
return;
-/*
- spin_lock_irq(&conf->device_lock);
+
r5c_do_reclaim(conf);
- spin_unlock_irq(&conf->device_lock);
-*/
r5l_do_reclaim(log);
+ r5c_update_super(conf);
+ md_wakeup_thread(mddev->thread);
}
void r5l_wake_reclaim(struct r5l_log *log, sector_t space)
@@ -973,6 +1010,7 @@ void r5l_quiesce(struct r5l_log *log, int state)
r5l_wake_reclaim(log, -1L);
md_unregister_thread(&log->reclaim_thread);
r5l_do_reclaim(log);
+ r5c_update_super(log->rdev->mddev->private);
}
}
@@ -1627,6 +1665,7 @@ void r5c_handle_stripe_flush(struct r5conf *conf,
int disks) {
int i;
int do_wakeup = 0;
+ unsigned long flags;
if (sh->r5c_state == R5C_STATE_PARITY_DONE) {
r5c_set_state(sh, R5C_STATE_INRAID);
@@ -1636,6 +1675,12 @@ void r5c_handle_stripe_flush(struct r5conf *conf,
if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
do_wakeup = 1;
}
+ spin_lock_irqsave(&conf->log->cache.stripe_in_cache_lock, flags);
+ list_del_init(&sh->r5c);
+ spin_unlock_irqrestore(&conf->log->cache.stripe_in_cache_lock, flags);
+ spin_lock_irqsave(&sh->stripe_lock, flags);
+ sh->journal_start = -1L;
+ spin_unlock_irqrestore(&sh->stripe_lock, flags);
}
if (do_wakeup)
wake_up(&conf->wait_for_overlap);
@@ -1717,6 +1762,9 @@ static void r5c_adjust_flush_threshold(struct r5conf *conf)
else if (atomic_read(&conf->r5c_cached_stripes) * 8 > conf->max_nr_stripes)
new_thres -= 1;
+ if (test_bit(R5_INACTIVE_BLOCKED, &conf->cache_state))
+ new_thres = 1;
+
if (new_thres >= 1)
log->cache.flush_threshold = new_thres;
}
@@ -1725,16 +1773,23 @@ void r5c_do_reclaim(struct r5conf *conf)
{
struct stripe_head *sh, *next;
struct r5l_log *log = conf->log;
-
- assert_spin_locked(&conf->device_lock);
+ int count = 0;
+ unsigned long flags;
if (!log)
return;
+ spin_lock_irqsave(&conf->device_lock, flags);
r5c_adjust_flush_threshold(conf);
- list_for_each_entry_safe(sh, next, &conf->r5c_cached_list, lru)
- if (atomic_read(&sh->dev_in_cache) >= log->cache.flush_threshold)
+ list_for_each_entry_safe(sh, next, &conf->r5c_cached_list, lru) {
+ if (atomic_read(&sh->dev_in_cache) >= log->cache.flush_threshold) {
+ count ++;
r5c_flush_stripe(conf, sh);
+ }
+ }
+ spin_unlock_irqrestore(&conf->device_lock, flags);
+ if (test_bit(R5_INACTIVE_BLOCKED, &conf->cache_state))
+ wake_up(&conf->wait_for_overlap);
}
static int r5l_load_log(struct r5l_log *log)
@@ -1849,6 +1904,8 @@ int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev)
log->rdev->mddev, "reclaim");
if (!log->reclaim_thread)
goto reclaim_thread;
+ log->reclaim_thread->timeout = RECLAIM_WAKEUP_INTERVAL;
+
init_waitqueue_head(&log->iounit_wait);
INIT_LIST_HEAD(&log->no_mem_stripes);
@@ -1857,7 +1914,6 @@ int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev)
spin_lock_init(&log->no_space_stripes_lock);
init_r5c_cache(conf, &log->cache);
-
if (r5l_load_log(log))
goto error;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 127c5c2..3e2bdc5 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -636,6 +636,8 @@ raid5_get_active_stripe(struct r5conf *conf, sector_t sector,
if (!sh) {
set_bit(R5_INACTIVE_BLOCKED,
&conf->cache_state);
+ if (conf->log)
+ r5l_wake_reclaim(conf->log, 0);
wait_event_lock_irq(
conf->wait_for_stripe,
!list_empty(conf->inactive_list + hash) &&
@@ -670,6 +672,15 @@ raid5_get_active_stripe(struct r5conf *conf, sector_t sector,
} while (sh == NULL);
spin_unlock_irq(conf->hash_locks + hash);
+
+ if (conf->log &&
+ (atomic_read(&conf->active_stripes) +
+ atomic_read(&conf->r5c_cached_stripes) >
+ conf->max_nr_stripes * 3 / 4)) {
+ set_bit(R5_INACTIVE_BLOCKED, &conf->cache_state);
+ r5l_wake_reclaim(conf->log, 0);
+ }
+
return sh;
}
@@ -1965,8 +1976,10 @@ static struct stripe_head *alloc_stripe(struct kmem_cache *sc, gfp_t gfp)
spin_lock_init(&sh->batch_lock);
INIT_LIST_HEAD(&sh->batch_list);
INIT_LIST_HEAD(&sh->lru);
+ INIT_LIST_HEAD(&sh->r5c);
atomic_set(&sh->count, 1);
atomic_set(&sh->dev_in_cache, 0);
+ sh->journal_start = -1L;
}
return sh;
}
@@ -5965,7 +5978,6 @@ static void raid5d(struct md_thread *thread)
md_check_recovery(mddev);
spin_lock_irq(&conf->device_lock);
}
- r5c_do_reclaim(conf);
}
pr_debug("%d stripes handled\n", handled);
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index 78fcc6d..4fe06cc 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -237,6 +237,8 @@ struct stripe_head {
struct r5l_io_unit *log_io;
struct list_head log_list;
atomic_t dev_in_cache;
+ sector_t journal_start; /* first meta block on the journal */
+ struct list_head r5c; /* for r5c_cache->stripe_in_cache */
/**
* struct stripe_operations
* @target - STRIPE_OP_COMPUTE_BLK target
--
2.8.0.rc2
^ permalink raw reply related
* Re: [RFC 3/3] md: dm-crypt: Introduce the bulk mode method when sending request
From: Baolin Wang @ 2016-05-27 6:03 UTC (permalink / raw)
To: Mike Snitzer
Cc: Jens Axboe, Alasdair G Kergon, open list:DEVICE-MAPPER (LVM),
Herbert Xu, David Miller, Sagi Grimberg,
open list:SOFTWARE RAID (Multiple Disks) SUPPORT,
Martin K. Petersen, Joonsoo Kim, tadeusz.struk, smueller,
Ming Lei, Eric Biggers, linux-block, Keith Busch, LKML,
Masanari Iida, Mark Brown, Arnd Bergmann, linux-crypto, Tejun Heo,
Dan Williams, Shaohua Li, Kent Overstreet <kent.overs>
In-Reply-To: <20160526140440.GA24865@redhat.com>
On 26 May 2016 at 22:04, Mike Snitzer <snitzer@redhat.com> wrote:
> Comments inlined.
>
> In general the most concerning bit is the need for memory allocation in
> the IO path (see comment/question below near call to sg_alloc_table).
> In DM targets we make heavy use of .ctr preallocated memory and/or
> per-bio-data to avoid memory allocations in the IO path.
Make sense.
>
> On Wed, May 25 2016 at 2:12am -0400,
> Baolin Wang <baolin.wang@linaro.org> wrote:
>
>> In now dm-crypt code, it is ineffective to map one segment (always one
>> sector) of one bio with just only one scatterlist at one time for hardware
>> crypto engine. Especially for some encryption mode (like ecb or xts mode)
>> cooperating with the crypto engine, they just need one initial IV or null
>> IV instead of different IV for each sector. In this situation We can consider
>> to use multiple scatterlists to map the whole bio and send all scatterlists
>> of one bio to crypto engine to encrypt or decrypt, which can improve the
>> hardware engine's efficiency.
>>
>> With this optimization, On my test setup (beaglebone black board) using 64KB
>> I/Os on an eMMC storage device I saw about 60% improvement in throughput for
>> encrypted writes, and about 100% improvement for encrypted reads. But this
>> is not fit for other modes which need different IV for each sector.
>>
>> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
>> ---
>> drivers/md/dm-crypt.c | 188 +++++++++++++++++++++++++++++++++++++++++++++----
>> 1 file changed, 176 insertions(+), 12 deletions(-)
>>
>> diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
>> index 4f3cb35..1c86ea7 100644
>> --- a/drivers/md/dm-crypt.c
>> +++ b/drivers/md/dm-crypt.c
>> @@ -33,6 +33,7 @@
>> #include <linux/device-mapper.h>
>>
>> #define DM_MSG_PREFIX "crypt"
>> +#define DM_MAX_SG_LIST 1024
>>
>> /*
>> * context holding the current state of a multi-part conversion
>> @@ -46,6 +47,8 @@ struct convert_context {
>> sector_t cc_sector;
>> atomic_t cc_pending;
>> struct skcipher_request *req;
>> + struct sg_table sgt_in;
>> + struct sg_table sgt_out;
>> };
>>
>> /*
>> @@ -803,6 +806,108 @@ static struct crypt_iv_operations crypt_iv_tcw_ops = {
>> .post = crypt_iv_tcw_post
>> };
>>
>> +/*
>> + * Check how many sg entry numbers are needed when map one bio
>> + * with scatterlists in advance.
>> + */
>> +static unsigned int crypt_sg_entry(struct bio *bio_t)
>> +{
>> + struct request_queue *q = bdev_get_queue(bio_t->bi_bdev);
>> + int cluster = blk_queue_cluster(q);
>> + struct bio_vec bvec, bvprv = { NULL };
>> + struct bvec_iter biter;
>> + unsigned long nbytes = 0, sg_length = 0;
>> + unsigned int sg_cnt = 0, first_bvec = 0;
>> +
>> + if (bio_t->bi_rw & REQ_DISCARD) {
>> + if (bio_t->bi_vcnt)
>> + return 1;
>> + return 0;
>> + }
>> +
>> + if (bio_t->bi_rw & REQ_WRITE_SAME)
>> + return 1;
>> +
>> + bio_for_each_segment(bvec, bio_t, biter) {
>> + nbytes = bvec.bv_len;
>> +
>> + if (!cluster) {
>> + sg_cnt++;
>> + continue;
>> + }
>> +
>> + if (!first_bvec) {
>> + first_bvec = 1;
>> + goto new_segment;
>> + }
>> +
>> + if (sg_length + nbytes > queue_max_segment_size(q))
>> + goto new_segment;
>> +
>> + if (!BIOVEC_PHYS_MERGEABLE(&bvprv, &bvec))
>> + goto new_segment;
>> +
>> + if (!BIOVEC_SEG_BOUNDARY(q, &bvprv, &bvec))
>> + goto new_segment;
>> +
>> + sg_length += nbytes;
>> + continue;
>> +
>> +new_segment:
>> + memcpy(&bvprv, &bvec, sizeof(struct bio_vec));
>> + sg_length = nbytes;
>> + sg_cnt++;
>> + }
>> +
>> + return sg_cnt;
>> +}
>> +
>> +static int crypt_convert_alloc_table(struct crypt_config *cc,
>> + struct convert_context *ctx)
>> +{
>> + struct bio *bio_in = ctx->bio_in;
>> + struct bio *bio_out = ctx->bio_out;
>> + unsigned int mode = skcipher_is_bulk_mode(any_tfm(cc));
>
> please use: bool bulk_mode = ...
OK.
>
>> + unsigned int sg_in_max, sg_out_max;
>> + int ret = 0;
>> +
>> + if (!mode)
>> + goto out2;
>
> Please use more descriptive label names than out[1-3]
OK.
>
>> +
>> + /*
>> + * Need to calculate how many sg entry need to be used
>> + * for this bio.
>> + */
>> + sg_in_max = crypt_sg_entry(bio_in) + 1;
>
> The return from crypt_sg_entry() is pretty awkward, given you just go on
> to add 1; as is the bounds checking.. the magic value of 2 needs to be
> be made clearer.
I'll remove the crypt_sg_entry() function.
>
>> + if (sg_in_max > DM_MAX_SG_LIST || sg_in_max <= 2)
>> + goto out2;
>> +
>> + ret = sg_alloc_table(&ctx->sgt_in, sg_in_max, GFP_KERNEL);
>
> Is it safe to be using GFP_KERNEL here? AFAIK this is in the IO mapping
> path and we try to avoid memory allocations at all costs -- due to the
> risk of deadlock when issuing IO to stacked block devices (dm-crypt
> could be part of a much more elaborate IO stack).
OK. I'll move the sg table allocation to be preallocated in the .ctr function.
>
>> + if (ret)
>> + goto out2;
>> +
>> + if (bio_data_dir(bio_in) == READ)
>> + goto out1;
>> +
>> + sg_out_max = crypt_sg_entry(bio_out) + 1;
>> + if (sg_out_max > DM_MAX_SG_LIST || sg_out_max <= 2)
>> + goto out3;
>> +
>> + ret = sg_alloc_table(&ctx->sgt_out, sg_out_max, GFP_KERNEL);
>> + if (ret)
>> + goto out3;
>> +
>> + return 0;
>> +
>> +out3:
>
> out_free_table?
>
>> + sg_free_table(&ctx->sgt_in);
>> +out2:
>
> out_skip_alloc?
>
>> + ctx->sgt_in.orig_nents = 0;
>> +out1:
>
> out_skip_write?
>
>> + ctx->sgt_out.orig_nents = 0;
>> + return ret;
>> +}
>> +
>> static void crypt_convert_init(struct crypt_config *cc,
>> struct convert_context *ctx,
>> struct bio *bio_out, struct bio *bio_in,
>> @@ -843,7 +948,13 @@ static int crypt_convert_block(struct crypt_config *cc,
>> {
>> struct bio_vec bv_in = bio_iter_iovec(ctx->bio_in, ctx->iter_in);
>> struct bio_vec bv_out = bio_iter_iovec(ctx->bio_out, ctx->iter_out);
>> + unsigned int mode = skcipher_is_bulk_mode(any_tfm(cc));
>
> again please use: bool bulk_mode = ...
OK.
>
>> + struct bio *bio_in = ctx->bio_in;
>> + struct bio *bio_out = ctx->bio_out;
>> + unsigned int total_bytes = bio_in->bi_iter.bi_size;
>> struct dm_crypt_request *dmreq;
>> + struct scatterlist *sg_in;
>> + struct scatterlist *sg_out;
>> u8 *iv;
>> int r;
>>
>> @@ -852,16 +963,6 @@ static int crypt_convert_block(struct crypt_config *cc,
>>
>> dmreq->iv_sector = ctx->cc_sector;
>> dmreq->ctx = ctx;
>> - sg_init_table(&dmreq->sg_in, 1);
>> - sg_set_page(&dmreq->sg_in, bv_in.bv_page, 1 << SECTOR_SHIFT,
>> - bv_in.bv_offset);
>> -
>> - sg_init_table(&dmreq->sg_out, 1);
>> - sg_set_page(&dmreq->sg_out, bv_out.bv_page, 1 << SECTOR_SHIFT,
>> - bv_out.bv_offset);
>> -
>> - bio_advance_iter(ctx->bio_in, &ctx->iter_in, 1 << SECTOR_SHIFT);
>> - bio_advance_iter(ctx->bio_out, &ctx->iter_out, 1 << SECTOR_SHIFT);
>>
>> if (cc->iv_gen_ops) {
>> r = cc->iv_gen_ops->generator(cc, iv, dmreq);
>> @@ -869,8 +970,63 @@ static int crypt_convert_block(struct crypt_config *cc,
>> return r;
>> }
>>
>> - skcipher_request_set_crypt(req, &dmreq->sg_in, &dmreq->sg_out,
>> - 1 << SECTOR_SHIFT, iv);
>> + if (mode && ctx->sgt_in.orig_nents > 0) {
>> + struct scatterlist *sg = NULL;
>> + unsigned int total_sg_in, total_sg_out;
>> +
>> + total_sg_in = blk_bio_map_sg(bdev_get_queue(bio_in->bi_bdev),
>> + bio_in, ctx->sgt_in.sgl, &sg);
>> + if ((total_sg_in <= 0) ||
>> + (total_sg_in > ctx->sgt_in.orig_nents)) {
>> + DMERR("%s in sg map error %d, sg table nents[%d]\n",
>> + __func__, total_sg_in, ctx->sgt_in.orig_nents);
>> + return -EINVAL;
>> + }
>> +
>> + if (sg)
>> + sg_mark_end(sg);
>> +
>> + ctx->iter_in.bi_size -= total_bytes;
>> + sg_in = ctx->sgt_in.sgl;
>> + sg_out = ctx->sgt_in.sgl;
>> +
>> + if (bio_data_dir(bio_in) == READ)
>> + goto set_crypt;
>> +
>> + sg = NULL;
>> + total_sg_out = blk_bio_map_sg(bdev_get_queue(bio_out->bi_bdev),
>> + bio_out, ctx->sgt_out.sgl, &sg);
>> + if ((total_sg_out <= 0) ||
>> + (total_sg_out > ctx->sgt_out.orig_nents)) {
>> + DMERR("%s out sg map error %d, sg table nents[%d]\n",
>> + __func__, total_sg_out, ctx->sgt_out.orig_nents);
>> + return -EINVAL;
>> + }
>> +
>> + if (sg)
>> + sg_mark_end(sg);
>> +
>> + ctx->iter_out.bi_size -= total_bytes;
>> + sg_out = ctx->sgt_out.sgl;
>> + } else {
>> + sg_init_table(&dmreq->sg_in, 1);
>> + sg_set_page(&dmreq->sg_in, bv_in.bv_page, 1 << SECTOR_SHIFT,
>> + bv_in.bv_offset);
>> +
>> + sg_init_table(&dmreq->sg_out, 1);
>> + sg_set_page(&dmreq->sg_out, bv_out.bv_page, 1 << SECTOR_SHIFT,
>> + bv_out.bv_offset);
>> +
>> + bio_advance_iter(ctx->bio_in, &ctx->iter_in, 1 << SECTOR_SHIFT);
>> + bio_advance_iter(ctx->bio_out, &ctx->iter_out, 1 << SECTOR_SHIFT);
>> +
>> + sg_in = &dmreq->sg_in;
>> + sg_out = &dmreq->sg_out;
>> + total_bytes = 1 << SECTOR_SHIFT;
>> + }
>> +
>> +set_crypt:
>> + skcipher_request_set_crypt(req, sg_in, sg_out, total_bytes, iv);
>
> Given how long this code has gotten I'd prefer to see this factored out
> to a new setup method.
I'll refactor this long function. Thanks for your comments.
>
>> if (bio_data_dir(ctx->bio_in) == WRITE)
>> r = crypto_skcipher_encrypt(req);
>> @@ -1081,6 +1237,8 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
>> if (io->ctx.req)
>> crypt_free_req(cc, io->ctx.req, base_bio);
>>
>> + sg_free_table(&io->ctx.sgt_in);
>> + sg_free_table(&io->ctx.sgt_out);
>> base_bio->bi_error = error;
>> bio_endio(base_bio);
>> }
>> @@ -1312,6 +1470,9 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
>> io->ctx.iter_out = clone->bi_iter;
>>
>> sector += bio_sectors(clone);
>> + r = crypt_convert_alloc_table(cc, &io->ctx);
>> + if (r < 0)
>> + io->error = -EIO;
>>
>> crypt_inc_pending(io);
>> r = crypt_convert(cc, &io->ctx);
>> @@ -1343,6 +1504,9 @@ static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
>>
>> crypt_convert_init(cc, &io->ctx, io->base_bio, io->base_bio,
>> io->sector);
>> + r = crypt_convert_alloc_table(cc, &io->ctx);
>> + if (r < 0)
>> + io->error = -EIO;
>>
>> r = crypt_convert(cc, &io->ctx);
>> if (r < 0)
>> --
>> 1.7.9.5
>>
>> --
>> dm-devel mailing list
>> dm-devel@redhat.com
>> https://www.redhat.com/mailman/listinfo/dm-devel
--
Baolin.wang
Best Regards
^ permalink raw reply
* Re: [RFC 2/3] crypto: Introduce CRYPTO_ALG_BULK flag
From: Milan Broz @ 2016-05-27 6:31 UTC (permalink / raw)
To: Baolin Wang, axboe, agk, snitzer, dm-devel, herbert, davem
Cc: ebiggers3, js1304, tadeusz.struk, smueller, standby24x7, shli,
dan.j.williams, martin.petersen, sagig, kent.overstreet,
keith.busch, tj, ming.lei, broonie, arnd, linux-crypto,
linux-block, linux-raid, linux-kernel
In-Reply-To: <7f0fef5fe473a451e352b2d42e1eed483fd28667.1464144791.git.baolin.wang@linaro.org>
On 05/25/2016 08:12 AM, Baolin Wang wrote:
> Now some cipher hardware engines prefer to handle bulk block rather than one
> sector (512 bytes) created by dm-crypt, cause these cipher engines can handle
> the intermediate values (IV) by themselves in one bulk block. This means we
> can increase the size of the request by merging request rather than always 512
> bytes and thus increase the hardware engine processing speed.
Hi,
could you please elaborate how exactly you are processing independently
encrypted sectors? For example with XTS mode. Do you play internally with
tweak calculation? Does this keep 512 bytes sector encryption blocks independent?
(If not, it is breaking compatibility everywhere and you are reinventing
disk encryption logic here - just for performance reason for some hw
not designed for this task... But that was said several times already.)
> So introduce 'CRYPTO_ALG_BULK' flag to indicate this cipher can support bulk
> mode.
What exactly skcipher will do if this flag is set?
Which drivers it should use? I do not see any posted patch that uses this flag yet.
How we can test it?
Milan
>
> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
> ---
> include/crypto/skcipher.h | 7 +++++++
> include/linux/crypto.h | 6 ++++++
> 2 files changed, 13 insertions(+)
>
> diff --git a/include/crypto/skcipher.h b/include/crypto/skcipher.h
> index 0f987f5..d89d29a 100644
> --- a/include/crypto/skcipher.h
> +++ b/include/crypto/skcipher.h
> @@ -519,5 +519,12 @@ static inline void skcipher_request_set_crypt(
> req->iv = iv;
> }
>
> +static inline unsigned int skcipher_is_bulk_mode(struct crypto_skcipher *sk_tfm)
> +{
> + struct crypto_tfm *tfm = crypto_skcipher_tfm(sk_tfm);
> +
> + return crypto_tfm_alg_bulk(tfm);
> +}
> +
> #endif /* _CRYPTO_SKCIPHER_H */
>
> diff --git a/include/linux/crypto.h b/include/linux/crypto.h
> index 6e28c89..a315487 100644
> --- a/include/linux/crypto.h
> +++ b/include/linux/crypto.h
> @@ -63,6 +63,7 @@
> #define CRYPTO_ALG_DEAD 0x00000020
> #define CRYPTO_ALG_DYING 0x00000040
> #define CRYPTO_ALG_ASYNC 0x00000080
> +#define CRYPTO_ALG_BULK 0x00000100
>
> /*
> * Set this bit if and only if the algorithm requires another algorithm of
> @@ -623,6 +624,11 @@ static inline u32 crypto_tfm_alg_type(struct crypto_tfm *tfm)
> return tfm->__crt_alg->cra_flags & CRYPTO_ALG_TYPE_MASK;
> }
>
> +static inline unsigned int crypto_tfm_alg_bulk(struct crypto_tfm *tfm)
> +{
> + return tfm->__crt_alg->cra_flags & CRYPTO_ALG_BULK;
> +}
> +
> static inline unsigned int crypto_tfm_alg_blocksize(struct crypto_tfm *tfm)
> {
> return tfm->__crt_alg->cra_blocksize;
>
^ permalink raw reply
* Re: [RFC 2/3] crypto: Introduce CRYPTO_ALG_BULK flag
From: Baolin Wang @ 2016-05-27 7:04 UTC (permalink / raw)
To: Milan Broz
Cc: Jens Axboe, Alasdair G Kergon, Mike Snitzer,
open list:DEVICE-MAPPER (LVM), Herbert Xu, David Miller,
Eric Biggers, Joonsoo Kim, tadeusz.struk, smueller, Masanari Iida,
Shaohua Li, Dan Williams, Martin K. Petersen, Sagi Grimberg,
Kent Overstreet, Keith Busch, Tejun Heo, Ming Lei, Mark Brown,
Arnd Bergmann, linux-crypto, linux-block,
open list:SOFTWARE RAID (Multiple Disks) SUPPORT
In-Reply-To: <5747E9CF.7010706@gmail.com>
Hi Milan,
On 27 May 2016 at 14:31, Milan Broz <gmazyland@gmail.com> wrote:
> On 05/25/2016 08:12 AM, Baolin Wang wrote:
>> Now some cipher hardware engines prefer to handle bulk block rather than one
>> sector (512 bytes) created by dm-crypt, cause these cipher engines can handle
>> the intermediate values (IV) by themselves in one bulk block. This means we
>> can increase the size of the request by merging request rather than always 512
>> bytes and thus increase the hardware engine processing speed.
>
> Hi,
>
> could you please elaborate how exactly you are processing independently
> encrypted sectors? For example with XTS mode. Do you play internally with
> tweak calculation? Does this keep 512 bytes sector encryption blocks independent?
>
> (If not, it is breaking compatibility everywhere and you are reinventing
> disk encryption logic here - just for performance reason for some hw
> not designed for this task... But that was said several times already.)
These are what the cipher hardware engine and engine driver should do,
for software we just need send one initial IV and bulk data to crypto
layer, which is enough.
>
>> So introduce 'CRYPTO_ALG_BULK' flag to indicate this cipher can support bulk
>> mode.
>
> What exactly skcipher will do if this flag is set?
I think that depends on how to implement the cipher engine driver.
>
> Which drivers it should use? I do not see any posted patch that uses this flag yet.
> How we can test it?
Some cipher engine drivers which support bulk mode should use this
flag. Yeah, we need upstream one cipher driver with this flag for
testing.
>
> Milan
>
>>
>> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
>> ---
>> include/crypto/skcipher.h | 7 +++++++
>> include/linux/crypto.h | 6 ++++++
>> 2 files changed, 13 insertions(+)
>>
>> diff --git a/include/crypto/skcipher.h b/include/crypto/skcipher.h
>> index 0f987f5..d89d29a 100644
>> --- a/include/crypto/skcipher.h
>> +++ b/include/crypto/skcipher.h
>> @@ -519,5 +519,12 @@ static inline void skcipher_request_set_crypt(
>> req->iv = iv;
>> }
>>
>> +static inline unsigned int skcipher_is_bulk_mode(struct crypto_skcipher *sk_tfm)
>> +{
>> + struct crypto_tfm *tfm = crypto_skcipher_tfm(sk_tfm);
>> +
>> + return crypto_tfm_alg_bulk(tfm);
>> +}
>> +
>> #endif /* _CRYPTO_SKCIPHER_H */
>>
>> diff --git a/include/linux/crypto.h b/include/linux/crypto.h
>> index 6e28c89..a315487 100644
>> --- a/include/linux/crypto.h
>> +++ b/include/linux/crypto.h
>> @@ -63,6 +63,7 @@
>> #define CRYPTO_ALG_DEAD 0x00000020
>> #define CRYPTO_ALG_DYING 0x00000040
>> #define CRYPTO_ALG_ASYNC 0x00000080
>> +#define CRYPTO_ALG_BULK 0x00000100
>>
>> /*
>> * Set this bit if and only if the algorithm requires another algorithm of
>> @@ -623,6 +624,11 @@ static inline u32 crypto_tfm_alg_type(struct crypto_tfm *tfm)
>> return tfm->__crt_alg->cra_flags & CRYPTO_ALG_TYPE_MASK;
>> }
>>
>> +static inline unsigned int crypto_tfm_alg_bulk(struct crypto_tfm *tfm)
>> +{
>> + return tfm->__crt_alg->cra_flags & CRYPTO_ALG_BULK;
>> +}
>> +
>> static inline unsigned int crypto_tfm_alg_blocksize(struct crypto_tfm *tfm)
>> {
>> return tfm->__crt_alg->cra_blocksize;
>>
>
--
Baolin.wang
Best Regards
^ permalink raw reply
* Re: [RFC 2/3] crypto: Introduce CRYPTO_ALG_BULK flag
From: Milan Broz @ 2016-05-27 7:53 UTC (permalink / raw)
To: Baolin Wang
Cc: Jens Axboe, Alasdair G Kergon, Mike Snitzer,
open list:DEVICE-MAPPER (LVM), Herbert Xu, David Miller,
Eric Biggers, Joonsoo Kim, tadeusz.struk, smueller, Masanari Iida,
Shaohua Li, Dan Williams, Martin K. Petersen, Sagi Grimberg,
Kent Overstreet, Keith Busch, Tejun Heo, Ming Lei, Mark Brown,
Arnd Bergmann, linux-crypto, linux-block,
open list:SOFTWARE RAID (Multiple Disks) SUPPORT
In-Reply-To: <CAMz4kuK2yFqtdNJBn1POOo9Y_4=Rk2q8BP8P1P+JdYJk2oLnFw@mail.gmail.com>
On 05/27/2016 09:04 AM, Baolin Wang wrote:
> Hi Milan,
>
> On 27 May 2016 at 14:31, Milan Broz <gmazyland@gmail.com> wrote:
>> On 05/25/2016 08:12 AM, Baolin Wang wrote:
>>> Now some cipher hardware engines prefer to handle bulk block rather than one
>>> sector (512 bytes) created by dm-crypt, cause these cipher engines can handle
>>> the intermediate values (IV) by themselves in one bulk block. This means we
>>> can increase the size of the request by merging request rather than always 512
>>> bytes and thus increase the hardware engine processing speed.
>>
>> Hi,
>>
>> could you please elaborate how exactly you are processing independently
>> encrypted sectors? For example with XTS mode. Do you play internally with
>> tweak calculation? Does this keep 512 bytes sector encryption blocks independent?
>>
>> (If not, it is breaking compatibility everywhere and you are reinventing
>> disk encryption logic here - just for performance reason for some hw
>> not designed for this task... But that was said several times already.)
>
> These are what the cipher hardware engine and engine driver should do,
> for software we just need send one initial IV and bulk data to crypto
> layer, which is enough.
Hi,
Thanks for answer.
So this is just doing some kind of batch processing optimization inside the driver?
I still do not understand why it is not possible to "batch" these requests inside
you driver (with async API) then and process them in one go without any changes
to dmcrypt though. (But I am not familiar with these drivers.)
If I understand it correctly, subsequent IVs are calculated inside
your driver just based on some initial value?
This can work only for sequential IVs (or, better said, for predictable
IVs like plain64, implemented inside your hw/driver).
It cannot work for IVs/tweaks that are randomized or keyed (like ESSIV).
Yes, I know that using ESSIV for XTS is considered overkill but I do not
see you are checking this condition anywhere in code (we can definitely
configure such a mapping).
>>> So introduce 'CRYPTO_ALG_BULK' flag to indicate this cipher can support bulk
>>> mode.
>>
>> What exactly skcipher will do if this flag is set?
>
> I think that depends on how to implement the cipher engine driver.
I do not care about implementation details, I just would like to see
some high-level description for the new API flag.
(Or at least read the code that implements it :)
>> Which drivers it should use? I do not see any posted patch that uses this flag yet.
>> How we can test it?
>
> Some cipher engine drivers which support bulk mode should use this
> flag. Yeah, we need upstream one cipher driver with this flag for
> testing.
I think if you introduce new flag, you should also post drivers that uses it.
Otherwise it is just unused code for mainline.
And I definitely would like to test it somewhere and see real
performance data (not just simple dd).
Thanks,
Milan
>
>>
>> Milan
>>
>>>
>>> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
>>> ---
>>> include/crypto/skcipher.h | 7 +++++++
>>> include/linux/crypto.h | 6 ++++++
>>> 2 files changed, 13 insertions(+)
>>>
>>> diff --git a/include/crypto/skcipher.h b/include/crypto/skcipher.h
>>> index 0f987f5..d89d29a 100644
>>> --- a/include/crypto/skcipher.h
>>> +++ b/include/crypto/skcipher.h
>>> @@ -519,5 +519,12 @@ static inline void skcipher_request_set_crypt(
>>> req->iv = iv;
>>> }
>>>
>>> +static inline unsigned int skcipher_is_bulk_mode(struct crypto_skcipher *sk_tfm)
>>> +{
>>> + struct crypto_tfm *tfm = crypto_skcipher_tfm(sk_tfm);
>>> +
>>> + return crypto_tfm_alg_bulk(tfm);
>>> +}
>>> +
>>> #endif /* _CRYPTO_SKCIPHER_H */
>>>
>>> diff --git a/include/linux/crypto.h b/include/linux/crypto.h
>>> index 6e28c89..a315487 100644
>>> --- a/include/linux/crypto.h
>>> +++ b/include/linux/crypto.h
>>> @@ -63,6 +63,7 @@
>>> #define CRYPTO_ALG_DEAD 0x00000020
>>> #define CRYPTO_ALG_DYING 0x00000040
>>> #define CRYPTO_ALG_ASYNC 0x00000080
>>> +#define CRYPTO_ALG_BULK 0x00000100
>>>
>>> /*
>>> * Set this bit if and only if the algorithm requires another algorithm of
>>> @@ -623,6 +624,11 @@ static inline u32 crypto_tfm_alg_type(struct crypto_tfm *tfm)
>>> return tfm->__crt_alg->cra_flags & CRYPTO_ALG_TYPE_MASK;
>>> }
>>>
>>> +static inline unsigned int crypto_tfm_alg_bulk(struct crypto_tfm *tfm)
>>> +{
>>> + return tfm->__crt_alg->cra_flags & CRYPTO_ALG_BULK;
>>> +}
>>> +
>>> static inline unsigned int crypto_tfm_alg_blocksize(struct crypto_tfm *tfm)
>>> {
>>> return tfm->__crt_alg->cra_blocksize;
>>>
>>
>
>
>
^ permalink raw reply
* Re: [RFC 2/3] crypto: Introduce CRYPTO_ALG_BULK flag
From: Baolin Wang @ 2016-05-27 9:04 UTC (permalink / raw)
To: Milan Broz
Cc: Jens Axboe, Alasdair G Kergon, Mike Snitzer,
open list:DEVICE-MAPPER (LVM), Herbert Xu, David Miller,
Eric Biggers, Joonsoo Kim, tadeusz.struk, smueller, Masanari Iida,
Shaohua Li, Dan Williams, Martin K. Petersen, Sagi Grimberg,
Kent Overstreet, Keith Busch, Tejun Heo, Ming Lei, Mark Brown,
Arnd Bergmann, linux-crypto, linux-block,
open list:SOFTWARE RAID (Multiple Disks) SUPPORT
In-Reply-To: <5747FD04.4090707@gmail.com>
On 27 May 2016 at 15:53, Milan Broz <gmazyland@gmail.com> wrote:
> On 05/27/2016 09:04 AM, Baolin Wang wrote:
>> Hi Milan,
>>
>> On 27 May 2016 at 14:31, Milan Broz <gmazyland@gmail.com> wrote:
>>> On 05/25/2016 08:12 AM, Baolin Wang wrote:
>>>> Now some cipher hardware engines prefer to handle bulk block rather than one
>>>> sector (512 bytes) created by dm-crypt, cause these cipher engines can handle
>>>> the intermediate values (IV) by themselves in one bulk block. This means we
>>>> can increase the size of the request by merging request rather than always 512
>>>> bytes and thus increase the hardware engine processing speed.
>>>
>>> Hi,
>>>
>>> could you please elaborate how exactly you are processing independently
>>> encrypted sectors? For example with XTS mode. Do you play internally with
>>> tweak calculation? Does this keep 512 bytes sector encryption blocks independent?
>>>
>>> (If not, it is breaking compatibility everywhere and you are reinventing
>>> disk encryption logic here - just for performance reason for some hw
>>> not designed for this task... But that was said several times already.)
>>
>> These are what the cipher hardware engine and engine driver should do,
>> for software we just need send one initial IV and bulk data to crypto
>> layer, which is enough.
>
> Hi,
>
> Thanks for answer.
>
> So this is just doing some kind of batch processing optimization inside the driver?
> I still do not understand why it is not possible to "batch" these requests inside
> you driver (with async API) then and process them in one go without any changes
> to dmcrypt though. (But I am not familiar with these drivers.)
I think it is not only for the driver level, but also for cipher API.
If one cipher engine can support bulk mode, then we should send bulk
data to it, not only one sector, which need to be implemented at top
API level, such as:
skcipher_request_set_crypt(req, sgt_in.sgl, sgt_out.sgl, total_bytes,
iv); /* send sg table to crypt layer */
If move these optimization into driver, it means we need to merge
requests from dm-crypt together to be one big request for engine
driver, but it will be low efficiency and not help. Why we can not
send one bulk request at first in dm-crypt? We just need to set the
different parameters for skcipher_request_set_crypt() function
according to different cipher mode in dm-crypt.
>
> If I understand it correctly, subsequent IVs are calculated inside
> your driver just based on some initial value?
Yes, something like this.
> This can work only for sequential IVs (or, better said, for predictable
> IVs like plain64, implemented inside your hw/driver).
>
> It cannot work for IVs/tweaks that are randomized or keyed (like ESSIV).
> Yes, I know that using ESSIV for XTS is considered overkill but I do not
> see you are checking this condition anywhere in code (we can definitely
> configure such a mapping).
So if some ciphers (like: aes(cbc)) can not support bulk mode, then we
should not add the CRYPTO_ALG_BULK flag for this cipher in the cipher
driver.
>
>>>> So introduce 'CRYPTO_ALG_BULK' flag to indicate this cipher can support bulk
>>>> mode.
>>>
>>> What exactly skcipher will do if this flag is set?
>>
>> I think that depends on how to implement the cipher engine driver.
>
> I do not care about implementation details, I just would like to see
> some high-level description for the new API flag.
> (Or at least read the code that implements it :)
I think for high-level description, we can use sg table to map one
bulk block and send to crypt layer by skcipher_request_set_crypt()
function.
>
>>> Which drivers it should use? I do not see any posted patch that uses this flag yet.
>>> How we can test it?
>>
>> Some cipher engine drivers which support bulk mode should use this
>> flag. Yeah, we need upstream one cipher driver with this flag for
>> testing.
>
> I think if you introduce new flag, you should also post drivers that uses it.
> Otherwise it is just unused code for mainline.
Yeah, that's right.
>
> And I definitely would like to test it somewhere and see real
> performance data (not just simple dd).
OK. Thanks.
>
> Thanks,
> Milan
>
>>
>>>
>>> Milan
>>>
>>>>
>>>> Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
>>>> ---
>>>> include/crypto/skcipher.h | 7 +++++++
>>>> include/linux/crypto.h | 6 ++++++
>>>> 2 files changed, 13 insertions(+)
>>>>
>>>> diff --git a/include/crypto/skcipher.h b/include/crypto/skcipher.h
>>>> index 0f987f5..d89d29a 100644
>>>> --- a/include/crypto/skcipher.h
>>>> +++ b/include/crypto/skcipher.h
>>>> @@ -519,5 +519,12 @@ static inline void skcipher_request_set_crypt(
>>>> req->iv = iv;
>>>> }
>>>>
>>>> +static inline unsigned int skcipher_is_bulk_mode(struct crypto_skcipher *sk_tfm)
>>>> +{
>>>> + struct crypto_tfm *tfm = crypto_skcipher_tfm(sk_tfm);
>>>> +
>>>> + return crypto_tfm_alg_bulk(tfm);
>>>> +}
>>>> +
>>>> #endif /* _CRYPTO_SKCIPHER_H */
>>>>
>>>> diff --git a/include/linux/crypto.h b/include/linux/crypto.h
>>>> index 6e28c89..a315487 100644
>>>> --- a/include/linux/crypto.h
>>>> +++ b/include/linux/crypto.h
>>>> @@ -63,6 +63,7 @@
>>>> #define CRYPTO_ALG_DEAD 0x00000020
>>>> #define CRYPTO_ALG_DYING 0x00000040
>>>> #define CRYPTO_ALG_ASYNC 0x00000080
>>>> +#define CRYPTO_ALG_BULK 0x00000100
>>>>
>>>> /*
>>>> * Set this bit if and only if the algorithm requires another algorithm of
>>>> @@ -623,6 +624,11 @@ static inline u32 crypto_tfm_alg_type(struct crypto_tfm *tfm)
>>>> return tfm->__crt_alg->cra_flags & CRYPTO_ALG_TYPE_MASK;
>>>> }
>>>>
>>>> +static inline unsigned int crypto_tfm_alg_bulk(struct crypto_tfm *tfm)
>>>> +{
>>>> + return tfm->__crt_alg->cra_flags & CRYPTO_ALG_BULK;
>>>> +}
>>>> +
>>>> static inline unsigned int crypto_tfm_alg_blocksize(struct crypto_tfm *tfm)
>>>> {
>>>> return tfm->__crt_alg->cra_blocksize;
>>>>
>>>
>>
>>
>>
--
Baolin.wang
Best Regards
^ permalink raw reply
* Re: BLKZEROOUT not zeroing md dev on VMDK
From: Tom Yan @ 2016-05-27 9:30 UTC (permalink / raw)
To: Sitsofe Wheeler
Cc: Darrick J. Wong, Shaohua Li, Jens Axboe, Arvind Kumar,
VMware PV-Drivers, linux-raid, linux-scsi@vger.kernel.org,
linux-block, linux-kernel@vger.kernel.org
In-Reply-To: <CALjAwxgkgPZdXFkOuD-7Dsbxf9oVEGuXTDLnyqu8rTrJio_4mA@mail.gmail.com>
There seems to be some sort of race condition between
blkdev_issue_zeroout() and the scsi disk driver (disabling write same
after an illegal request). On my UAS drive, sometimes `blkdiscard -z
/dev/sdX` will return right away, even though if I then check
`write_same_max_bytes` it has turned 0. Sometimes it will just write
zero with SCSI WRITE even if `write_same_max_bytes` is 33553920 before
I issue `blkdiscard -z` (`write_same_max_bytes` also turned 0, as
expected).
Not sure if it is directly related to the case here though.
On 27 May 2016 at 12:45, Sitsofe Wheeler <sitsofe@gmail.com> wrote:
> On 27 May 2016 at 05:18, Darrick J. Wong <darrick.wong@oracle.com> wrote:
>>
>> It's possible that the pvscsi device advertised WRITE SAME, but if the device
>> sends back ILLEGAL REQUEST then the SCSI disk driver will set
>> write_same_max_bytes=0. Subsequent BLKZEROOUT attempts will then issue writes
>> of zeroes to the drive.
>
> Thanks for following up on this but that's not what happens on the md
> device - you can go on to issue as many BLKZEROOUT requests as you
> like but the md disk is never zeroed nor is an error returned.
>
> I filed a bug at https://bugzilla.kernel.org/show_bug.cgi?id=118581
> (see https://bugzilla.kernel.org/show_bug.cgi?id=118581#c6 for
> alternative reproduction steps that use scsi_debug and can be reworked
> to impact device mapper) and Shaohua Li noted that
> blkdev_issue_write_same could return 0 even when the disk didn't
> support write same (see
> https://bugzilla.kernel.org/show_bug.cgi?id=118581#c8 ).
>
> Shaohua went on to create a patch for this ("block: correctly fallback
> for zeroout" - https://patchwork.kernel.org/patch/9137311/ ) which has
> yet to be reviewed.
>
> --
> Sitsofe | http://sucs.org/~sits/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [RFC v2 0/3] Introduce the bulk mode method when sending request to crypto layer
From: Baolin Wang @ 2016-05-27 11:11 UTC (permalink / raw)
To: axboe, agk, snitzer, dm-devel, herbert, davem
Cc: ebiggers3, js1304, tadeusz.struk, smueller, standby24x7, shli,
dan.j.williams, martin.petersen, sagig, kent.overstreet,
keith.busch, tj, ming.lei, broonie, arnd, linux-crypto,
linux-block, linux-raid, linux-kernel, baolin.wang
This patchset will check if the cipher can support bulk mode, then dm-crypt
will handle different ways to send requests to crypto layer according to
cipher mode. For bulk mode, we can use sg table to map the whole bio and
send all scatterlists of one bio to crypto engine to encrypt or decrypt,
which can improve the hardware engine's efficiency.
As Milan pointed out we need one driver with using the new 'CRYPTO_ALG_BULK'
flag, I'll add one cipher engine driver with 'CRYPTO_ALG_BULK' flag to test
this optimization in next version, if I am sure this optimization is on the
right direction according to your comments.
Looking forward to any comments and suggestions. Thanks.
Changes since v1:
- Refactor the blk_bio_map_sg() function to avoid duplicated code.
- Move the sg table allocation to crypt_ctr_cipher() function to avoid memory
allocation in the IO path.
- Remove the crypt_sg_entry() function.
- Other optimization.
Baolin Wang (3):
block: Introduce blk_bio_map_sg() to map one bio
crypto: Introduce CRYPTO_ALG_BULK flag
md: dm-crypt: Introduce the bulk mode method when sending request
block/blk-merge.c | 36 +++++++++--
drivers/md/dm-crypt.c | 145 ++++++++++++++++++++++++++++++++++++++++++++-
include/crypto/skcipher.h | 7 +++
include/linux/blkdev.h | 2 +
include/linux/crypto.h | 6 ++
5 files changed, 190 insertions(+), 6 deletions(-)
--
1.7.9.5
^ permalink raw reply
* [RFC v2 1/3] block: Introduce blk_bio_map_sg() to map one bio
From: Baolin Wang @ 2016-05-27 11:11 UTC (permalink / raw)
To: axboe, agk, snitzer, dm-devel, herbert, davem
Cc: ebiggers3, js1304, tadeusz.struk, smueller, standby24x7, shli,
dan.j.williams, martin.petersen, sagig, kent.overstreet,
keith.busch, tj, ming.lei, broonie, arnd, linux-crypto,
linux-block, linux-raid, linux-kernel, baolin.wang
In-Reply-To: <cover.1464346333.git.baolin.wang@linaro.org>
In dm-crypt, it need to map one bio to scatterlist for improving the
hardware engine encryption efficiency. Thus this patch introduces the
blk_bio_map_sg() function to map one bio with scatterlists.
For avoiding the duplicated code in __blk_bios_map_sg() function, add
one parameter to distinguish bio map or request map.
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
---
block/blk-merge.c | 36 +++++++++++++++++++++++++++++++-----
include/linux/blkdev.h | 2 ++
2 files changed, 33 insertions(+), 5 deletions(-)
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 2613531..badae44 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -376,7 +376,7 @@ new_segment:
static int __blk_bios_map_sg(struct request_queue *q, struct bio *bio,
struct scatterlist *sglist,
- struct scatterlist **sg)
+ struct scatterlist **sg, bool single_bio)
{
struct bio_vec bvec, bvprv = { NULL };
struct bvec_iter iter;
@@ -408,13 +408,39 @@ single_segment:
return 1;
}
- for_each_bio(bio)
+ if (!single_bio) {
+ for_each_bio(bio)
+ bio_for_each_segment(bvec, bio, iter)
+ __blk_segment_map_sg(q, &bvec, sglist, &bvprv,
+ sg, &nsegs, &cluster);
+ } else {
bio_for_each_segment(bvec, bio, iter)
- __blk_segment_map_sg(q, &bvec, sglist, &bvprv, sg,
- &nsegs, &cluster);
+ __blk_segment_map_sg(q, &bvec, sglist, &bvprv,
+ sg, &nsegs, &cluster);
+ }
+
+ return nsegs;
+}
+
+/*
+ * Map a bio to scatterlist, return number of sg entries setup. Caller must
+ * make sure sg can hold bio segments entries.
+ */
+int blk_bio_map_sg(struct request_queue *q, struct bio *bio,
+ struct scatterlist *sglist)
+{
+ struct scatterlist *sg = NULL;
+ int nsegs = 0;
+
+ if (bio)
+ nsegs = __blk_bios_map_sg(q, bio, sglist, &sg, true);
+
+ if (sg)
+ sg_mark_end(sg);
return nsegs;
}
+EXPORT_SYMBOL(blk_bio_map_sg);
/*
* map a request to scatterlist, return number of sg entries setup. Caller
@@ -427,7 +453,7 @@ int blk_rq_map_sg(struct request_queue *q, struct request *rq,
int nsegs = 0;
if (rq->bio)
- nsegs = __blk_bios_map_sg(q, rq->bio, sglist, &sg);
+ nsegs = __blk_bios_map_sg(q, rq->bio, sglist, &sg, false);
if (unlikely(rq->cmd_flags & REQ_COPY_USER) &&
(blk_rq_bytes(rq) & q->dma_pad_mask)) {
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 1fd8fdf..5868062 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1013,6 +1013,8 @@ extern void blk_queue_write_cache(struct request_queue *q, bool enabled, bool fu
extern struct backing_dev_info *blk_get_backing_dev_info(struct block_device *bdev);
extern int blk_rq_map_sg(struct request_queue *, struct request *, struct scatterlist *);
+extern int blk_bio_map_sg(struct request_queue *q, struct bio *bio,
+ struct scatterlist *sglist);
extern void blk_dump_rq_flags(struct request *, char *);
extern long nr_blockdev_pages(void);
--
1.7.9.5
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox