Commit 4d7696f1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'md/3.12' of git://neil.brown.name/md

Pull md update from Neil Brown:
 "Headline item is multithreading for RAID5 so that more IO/sec can be
  supported on fast (SSD) devices.  Also TILE-Gx SIMD suppor for RAID6
  calculations and an assortment of bug fixes"

* tag 'md/3.12' of git://neil.brown.name/md:
  raid5: only wakeup necessary threads
  md/raid5: flush out all pending requests before proceeding with reshape.
  md/raid5: use seqcount to protect access to shape in make_request.
  raid5: sysfs entry to control worker thread number
  raid5: offload stripe handle to workqueue
  raid5: fix stripe release order
  raid5: make release_stripe lockless
  md: avoid deadlock when dirty buffers during md_stop.
  md: Don't test all of mddev->flags at once.
  md: Fix apparent cut-and-paste error in super_90_validate
  raid6/test: replace echo -e with printf
  RAID: add tilegx SIMD implementation of raid6
  md: fix safe_mode buglet.
  md: don't call md_allow_write in get_bitmap_file.
parents b05430fc bfc90cb0
...@@ -1180,7 +1180,7 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev) ...@@ -1180,7 +1180,7 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev)
mddev->bitmap_info.offset = mddev->bitmap_info.offset =
mddev->bitmap_info.default_offset; mddev->bitmap_info.default_offset;
mddev->bitmap_info.space = mddev->bitmap_info.space =
mddev->bitmap_info.space; mddev->bitmap_info.default_space;
} }
} else if (mddev->pers == NULL) { } else if (mddev->pers == NULL) {
...@@ -3429,7 +3429,7 @@ safe_delay_store(struct mddev *mddev, const char *cbuf, size_t len) ...@@ -3429,7 +3429,7 @@ safe_delay_store(struct mddev *mddev, const char *cbuf, size_t len)
mddev->safemode_delay = (msec*HZ)/1000; mddev->safemode_delay = (msec*HZ)/1000;
if (mddev->safemode_delay == 0) if (mddev->safemode_delay == 0)
mddev->safemode_delay = 1; mddev->safemode_delay = 1;
if (mddev->safemode_delay < old_delay) if (mddev->safemode_delay < old_delay || old_delay == 0)
md_safemode_timeout((unsigned long)mddev); md_safemode_timeout((unsigned long)mddev);
} }
return len; return len;
...@@ -5144,7 +5144,7 @@ int md_run(struct mddev *mddev) ...@@ -5144,7 +5144,7 @@ int md_run(struct mddev *mddev)
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
if (mddev->flags) if (mddev->flags & MD_UPDATE_SB_FLAGS)
md_update_sb(mddev, 0); md_update_sb(mddev, 0);
md_new_event(mddev); md_new_event(mddev);
...@@ -5289,7 +5289,7 @@ static void __md_stop_writes(struct mddev *mddev) ...@@ -5289,7 +5289,7 @@ static void __md_stop_writes(struct mddev *mddev)
md_super_wait(mddev); md_super_wait(mddev);
if (mddev->ro == 0 && if (mddev->ro == 0 &&
(!mddev->in_sync || mddev->flags)) { (!mddev->in_sync || (mddev->flags & MD_UPDATE_SB_FLAGS))) {
/* mark array as shutdown cleanly */ /* mark array as shutdown cleanly */
mddev->in_sync = 1; mddev->in_sync = 1;
md_update_sb(mddev, 1); md_update_sb(mddev, 1);
...@@ -5337,8 +5337,14 @@ static int md_set_readonly(struct mddev *mddev, struct block_device *bdev) ...@@ -5337,8 +5337,14 @@ static int md_set_readonly(struct mddev *mddev, struct block_device *bdev)
err = -EBUSY; err = -EBUSY;
goto out; goto out;
} }
if (bdev) if (bdev && !test_bit(MD_STILL_CLOSED, &mddev->flags)) {
sync_blockdev(bdev); /* Someone opened the device since we flushed it
* so page cache could be dirty and it is too late
* to flush. So abort
*/
mutex_unlock(&mddev->open_mutex);
return -EBUSY;
}
if (mddev->pers) { if (mddev->pers) {
__md_stop_writes(mddev); __md_stop_writes(mddev);
...@@ -5373,14 +5379,14 @@ static int do_md_stop(struct mddev * mddev, int mode, ...@@ -5373,14 +5379,14 @@ static int do_md_stop(struct mddev * mddev, int mode,
mutex_unlock(&mddev->open_mutex); mutex_unlock(&mddev->open_mutex);
return -EBUSY; return -EBUSY;
} }
if (bdev) if (bdev && !test_bit(MD_STILL_CLOSED, &mddev->flags)) {
/* It is possible IO was issued on some other /* Someone opened the device since we flushed it
* open file which was closed before we took ->open_mutex. * so page cache could be dirty and it is too late
* As that was not the last close __blkdev_put will not * to flush. So abort
* have called sync_blockdev, so we must.
*/ */
sync_blockdev(bdev); mutex_unlock(&mddev->open_mutex);
return -EBUSY;
}
if (mddev->pers) { if (mddev->pers) {
if (mddev->ro) if (mddev->ro)
set_disk_ro(disk, 0); set_disk_ro(disk, 0);
...@@ -5628,10 +5634,7 @@ static int get_bitmap_file(struct mddev * mddev, void __user * arg) ...@@ -5628,10 +5634,7 @@ static int get_bitmap_file(struct mddev * mddev, void __user * arg)
char *ptr, *buf = NULL; char *ptr, *buf = NULL;
int err = -ENOMEM; int err = -ENOMEM;
if (md_allow_write(mddev)) file = kmalloc(sizeof(*file), GFP_NOIO);
file = kmalloc(sizeof(*file), GFP_NOIO);
else
file = kmalloc(sizeof(*file), GFP_KERNEL);
if (!file) if (!file)
goto out; goto out;
...@@ -6420,6 +6423,20 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, ...@@ -6420,6 +6423,20 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode,
!test_bit(MD_RECOVERY_NEEDED, !test_bit(MD_RECOVERY_NEEDED,
&mddev->flags), &mddev->flags),
msecs_to_jiffies(5000)); msecs_to_jiffies(5000));
if (cmd == STOP_ARRAY || cmd == STOP_ARRAY_RO) {
/* Need to flush page cache, and ensure no-one else opens
* and writes
*/
mutex_lock(&mddev->open_mutex);
if (atomic_read(&mddev->openers) > 1) {
mutex_unlock(&mddev->open_mutex);
err = -EBUSY;
goto abort;
}
set_bit(MD_STILL_CLOSED, &mddev->flags);
mutex_unlock(&mddev->open_mutex);
sync_blockdev(bdev);
}
err = mddev_lock(mddev); err = mddev_lock(mddev);
if (err) { if (err) {
printk(KERN_INFO printk(KERN_INFO
...@@ -6673,6 +6690,7 @@ static int md_open(struct block_device *bdev, fmode_t mode) ...@@ -6673,6 +6690,7 @@ static int md_open(struct block_device *bdev, fmode_t mode)
err = 0; err = 0;
atomic_inc(&mddev->openers); atomic_inc(&mddev->openers);
clear_bit(MD_STILL_CLOSED, &mddev->flags);
mutex_unlock(&mddev->open_mutex); mutex_unlock(&mddev->open_mutex);
check_disk_change(bdev); check_disk_change(bdev);
...@@ -7817,7 +7835,7 @@ void md_check_recovery(struct mddev *mddev) ...@@ -7817,7 +7835,7 @@ void md_check_recovery(struct mddev *mddev)
sysfs_notify_dirent_safe(mddev->sysfs_state); sysfs_notify_dirent_safe(mddev->sysfs_state);
} }
if (mddev->flags) if (mddev->flags & MD_UPDATE_SB_FLAGS)
md_update_sb(mddev, 0); md_update_sb(mddev, 0);
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) &&
......
...@@ -204,12 +204,16 @@ struct mddev { ...@@ -204,12 +204,16 @@ struct mddev {
struct md_personality *pers; struct md_personality *pers;
dev_t unit; dev_t unit;
int md_minor; int md_minor;
struct list_head disks; struct list_head disks;
unsigned long flags; unsigned long flags;
#define MD_CHANGE_DEVS 0 /* Some device status has changed */ #define MD_CHANGE_DEVS 0 /* Some device status has changed */
#define MD_CHANGE_CLEAN 1 /* transition to or from 'clean' */ #define MD_CHANGE_CLEAN 1 /* transition to or from 'clean' */
#define MD_CHANGE_PENDING 2 /* switch from 'clean' to 'active' in progress */ #define MD_CHANGE_PENDING 2 /* switch from 'clean' to 'active' in progress */
#define MD_UPDATE_SB_FLAGS (1 | 2 | 4) /* If these are set, md_update_sb needed */
#define MD_ARRAY_FIRST_USE 3 /* First use of array, needs initialization */ #define MD_ARRAY_FIRST_USE 3 /* First use of array, needs initialization */
#define MD_STILL_CLOSED 4 /* If set, then array has not been opened since
* md_ioctl checked on it.
*/
int suspended; int suspended;
atomic_t active_io; atomic_t active_io;
...@@ -218,7 +222,7 @@ struct mddev { ...@@ -218,7 +222,7 @@ struct mddev {
* are happening, so run/ * are happening, so run/
* takeover/stop are not safe * takeover/stop are not safe
*/ */
int ready; /* See when safe to pass int ready; /* See when safe to pass
* IO requests down */ * IO requests down */
struct gendisk *gendisk; struct gendisk *gendisk;
......
This diff is collapsed.
...@@ -197,6 +197,7 @@ enum reconstruct_states { ...@@ -197,6 +197,7 @@ enum reconstruct_states {
struct stripe_head { struct stripe_head {
struct hlist_node hash; struct hlist_node hash;
struct list_head lru; /* inactive_list or handle_list */ struct list_head lru; /* inactive_list or handle_list */
struct llist_node release_list;
struct r5conf *raid_conf; struct r5conf *raid_conf;
short generation; /* increments with every short generation; /* increments with every
* reshape */ * reshape */
...@@ -211,6 +212,8 @@ struct stripe_head { ...@@ -211,6 +212,8 @@ struct stripe_head {
enum check_states check_state; enum check_states check_state;
enum reconstruct_states reconstruct_state; enum reconstruct_states reconstruct_state;
spinlock_t stripe_lock; spinlock_t stripe_lock;
int cpu;
struct r5worker_group *group;
/** /**
* struct stripe_operations * struct stripe_operations
* @target - STRIPE_OP_COMPUTE_BLK target * @target - STRIPE_OP_COMPUTE_BLK target
...@@ -321,6 +324,7 @@ enum { ...@@ -321,6 +324,7 @@ enum {
STRIPE_OPS_REQ_PENDING, STRIPE_OPS_REQ_PENDING,
STRIPE_ON_UNPLUG_LIST, STRIPE_ON_UNPLUG_LIST,
STRIPE_DISCARD, STRIPE_DISCARD,
STRIPE_ON_RELEASE_LIST,
}; };
/* /*
...@@ -363,6 +367,19 @@ struct disk_info { ...@@ -363,6 +367,19 @@ struct disk_info {
struct md_rdev *rdev, *replacement; struct md_rdev *rdev, *replacement;
}; };
struct r5worker {
struct work_struct work;
struct r5worker_group *group;
bool working;
};
struct r5worker_group {
struct list_head handle_list;
struct r5conf *conf;
struct r5worker *workers;
int stripes_cnt;
};
struct r5conf { struct r5conf {
struct hlist_head *stripe_hashtbl; struct hlist_head *stripe_hashtbl;
struct mddev *mddev; struct mddev *mddev;
...@@ -386,6 +403,7 @@ struct r5conf { ...@@ -386,6 +403,7 @@ struct r5conf {
int prev_chunk_sectors; int prev_chunk_sectors;
int prev_algo; int prev_algo;
short generation; /* increments with every reshape */ short generation; /* increments with every reshape */
seqcount_t gen_lock; /* lock against generation changes */
unsigned long reshape_checkpoint; /* Time we last updated unsigned long reshape_checkpoint; /* Time we last updated
* metadata */ * metadata */
long long min_offset_diff; /* minimum difference between long long min_offset_diff; /* minimum difference between
...@@ -445,6 +463,7 @@ struct r5conf { ...@@ -445,6 +463,7 @@ struct r5conf {
*/ */
atomic_t active_stripes; atomic_t active_stripes;
struct list_head inactive_list; struct list_head inactive_list;
struct llist_head released_stripes;
wait_queue_head_t wait_for_stripe; wait_queue_head_t wait_for_stripe;
wait_queue_head_t wait_for_overlap; wait_queue_head_t wait_for_overlap;
int inactive_blocked; /* release of inactive stripes blocked, int inactive_blocked; /* release of inactive stripes blocked,
...@@ -458,6 +477,9 @@ struct r5conf { ...@@ -458,6 +477,9 @@ struct r5conf {
* the new thread here until we fully activate the array. * the new thread here until we fully activate the array.
*/ */
struct md_thread *thread; struct md_thread *thread;
struct r5worker_group *worker_groups;
int group_cnt;
int worker_cnt_per_group;
}; };
/* /*
......
...@@ -101,6 +101,7 @@ extern const struct raid6_calls raid6_altivec8; ...@@ -101,6 +101,7 @@ extern const struct raid6_calls raid6_altivec8;
extern const struct raid6_calls raid6_avx2x1; extern const struct raid6_calls raid6_avx2x1;
extern const struct raid6_calls raid6_avx2x2; extern const struct raid6_calls raid6_avx2x2;
extern const struct raid6_calls raid6_avx2x4; extern const struct raid6_calls raid6_avx2x4;
extern const struct raid6_calls raid6_tilegx8;
struct raid6_recov_calls { struct raid6_recov_calls {
void (*data2)(int, size_t, int, int, void **); void (*data2)(int, size_t, int, int, void **);
......
...@@ -6,6 +6,7 @@ raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \ ...@@ -6,6 +6,7 @@ raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \
raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o
raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o
raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o neon4.o neon8.o raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o neon4.o neon8.o
raid6_pq-$(CONFIG_TILEGX) += tilegx8.o
hostprogs-y += mktables hostprogs-y += mktables
...@@ -110,6 +111,11 @@ $(obj)/neon8.c: UNROLL := 8 ...@@ -110,6 +111,11 @@ $(obj)/neon8.c: UNROLL := 8
$(obj)/neon8.c: $(src)/neon.uc $(src)/unroll.awk FORCE $(obj)/neon8.c: $(src)/neon.uc $(src)/unroll.awk FORCE
$(call if_changed,unroll) $(call if_changed,unroll)
targets += tilegx8.c
$(obj)/tilegx8.c: UNROLL := 8
$(obj)/tilegx8.c: $(src)/tilegx.uc $(src)/unroll.awk FORCE
$(call if_changed,unroll)
quiet_cmd_mktable = TABLE $@ quiet_cmd_mktable = TABLE $@
cmd_mktable = $(obj)/mktables > $@ || ( rm -f $@ && exit 1 ) cmd_mktable = $(obj)/mktables > $@ || ( rm -f $@ && exit 1 )
......
...@@ -65,6 +65,9 @@ const struct raid6_calls * const raid6_algos[] = { ...@@ -65,6 +65,9 @@ const struct raid6_calls * const raid6_algos[] = {
&raid6_altivec2, &raid6_altivec2,
&raid6_altivec4, &raid6_altivec4,
&raid6_altivec8, &raid6_altivec8,
#endif
#if defined(CONFIG_TILEGX)
&raid6_tilegx8,
#endif #endif
&raid6_intx1, &raid6_intx1,
&raid6_intx2, &raid6_intx2,
......
...@@ -40,13 +40,16 @@ else ifeq ($(HAS_NEON),yes) ...@@ -40,13 +40,16 @@ else ifeq ($(HAS_NEON),yes)
OBJS += neon.o neon1.o neon2.o neon4.o neon8.o OBJS += neon.o neon1.o neon2.o neon4.o neon8.o
CFLAGS += -DCONFIG_KERNEL_MODE_NEON=1 CFLAGS += -DCONFIG_KERNEL_MODE_NEON=1
else else
HAS_ALTIVEC := $(shell echo -e '\#include <altivec.h>\nvector int a;' |\ HAS_ALTIVEC := $(shell printf '\#include <altivec.h>\nvector int a;\n' |\
gcc -c -x c - >&/dev/null && \ gcc -c -x c - >&/dev/null && \
rm ./-.o && echo yes) rm ./-.o && echo yes)
ifeq ($(HAS_ALTIVEC),yes) ifeq ($(HAS_ALTIVEC),yes)
OBJS += altivec1.o altivec2.o altivec4.o altivec8.o OBJS += altivec1.o altivec2.o altivec4.o altivec8.o
endif endif
endif endif
ifeq ($(ARCH),tilegx)
OBJS += tilegx8.o
endif
.c.o: .c.o:
$(CC) $(CFLAGS) -c -o $@ $< $(CC) $(CFLAGS) -c -o $@ $<
...@@ -109,11 +112,15 @@ int16.c: int.uc ../unroll.awk ...@@ -109,11 +112,15 @@ int16.c: int.uc ../unroll.awk
int32.c: int.uc ../unroll.awk int32.c: int.uc ../unroll.awk
$(AWK) ../unroll.awk -vN=32 < int.uc > $@ $(AWK) ../unroll.awk -vN=32 < int.uc > $@
tilegx8.c: tilegx.uc ../unroll.awk
$(AWK) ../unroll.awk -vN=8 < tilegx.uc > $@
tables.c: mktables tables.c: mktables
./mktables > tables.c ./mktables > tables.c
clean: clean:
rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c neon*.c tables.c raid6test rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c neon*.c tables.c raid6test
rm -f tilegx*.c
spotless: clean spotless: clean
rm -f *~ rm -f *~
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2002 H. Peter Anvin - All Rights Reserved
* Copyright 2012 Tilera Corporation - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 53 Temple Place Ste 330,
* Boston MA 02111-1307, USA; either version 2 of the License, or
* (at your option) any later version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/*
* tilegx$#.c
*
* $#-way unrolled TILE-Gx SIMD for RAID-6 math.
*
* This file is postprocessed using unroll.awk.
*
*/
#include <linux/raid/pq.h>
/* Create 8 byte copies of constant byte */
# define NBYTES(x) (__insn_v1addi(0, x))
# define NSIZE 8
/*
* The SHLBYTE() operation shifts each byte left by 1, *not*
* rolling over into the next byte
*/
static inline __attribute_const__ u64 SHLBYTE(u64 v)
{
/* Vector One Byte Shift Left Immediate. */
return __insn_v1shli(v, 1);
}
/*
* The MASK() operation returns 0xFF in any byte for which the high
* bit is 1, 0x00 for any byte for which the high bit is 0.
*/
static inline __attribute_const__ u64 MASK(u64 v)
{
/* Vector One Byte Shift Right Signed Immediate. */
return __insn_v1shrsi(v, 7);
}
void raid6_tilegx$#_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
u8 **dptr = (u8 **)ptrs;
u64 *p, *q;
int d, z, z0;
u64 wd$$, wq$$, wp$$, w1$$, w2$$;
u64 x1d = NBYTES(0x1d);
u64 * z0ptr;
z0 = disks - 3; /* Highest data disk */
p = (u64 *)dptr[z0+1]; /* XOR parity */
q = (u64 *)dptr[z0+2]; /* RS syndrome */
z0ptr = (u64 *)&dptr[z0][0];
for ( d = 0 ; d < bytes ; d += NSIZE*$# ) {
wq$$ = wp$$ = *z0ptr++;
for ( z = z0-1 ; z >= 0 ; z-- ) {
wd$$ = *(u64 *)&dptr[z][d+$$*NSIZE];
wp$$ = wp$$ ^ wd$$;
w2$$ = MASK(wq$$);
w1$$ = SHLBYTE(wq$$);
w2$$ = w2$$ & x1d;
w1$$ = w1$$ ^ w2$$;
wq$$ = w1$$ ^ wd$$;
}
*p++ = wp$$;
*q++ = wq$$;
}
}
const struct raid6_calls raid6_tilegx$# = {
raid6_tilegx$#_gen_syndrome,
NULL,
"tilegx$#",
0
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment