Commit d8355e74 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-5.11/dm-changes' of...

Merge tag 'for-5.11/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm

Pull device mapper updates from Mike Snitzer:

 - Add DM verity support for signature verification with 2nd keyring

 - Fix DM verity to skip verity work if IO completes with error while
   system is shutting down

 - Add new DM multipath "IO affinity" path selector that maps IO
   destined to a given path to a specific CPU based on user provided
   mapping

 - Rename DM multipath path selector source files to have "dm-ps" prefix

 - Add REQ_NOWAIT support to some other simple DM targets that don't
   block in more elaborate ways waiting for IO

 - Export DM crypt's kcryptd workqueue via sysfs (WQ_SYSFS)

 - Fix error return code in DM's target_message() if empty message is
   received

 - A handful of other small cleanups

* tag 'for-5.11/dm-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm cache: simplify the return expression of load_mapping()
  dm ebs: avoid double unlikely() notation when using IS_ERR()
  dm verity: skip verity work if I/O error when system is shutting down
  dm crypt: export sysfs of kcryptd workqueue
  dm ioctl: fix error return code in target_message
  dm crypt: Constify static crypt_iv_operations
  dm: add support for REQ_NOWAIT to various targets
  dm: rename multipath path selector source files to have "dm-ps" prefix
  dm mpath: add IO affinity path selector
  dm verity: Add support for signature verification with 2nd keyring
  dm: remove unnecessary current->bio_list check when submitting split bio
parents 4e31dcc0 b7770923
...@@ -134,7 +134,12 @@ root_hash_sig_key_desc <key_description> ...@@ -134,7 +134,12 @@ root_hash_sig_key_desc <key_description>
the pkcs7 signature of the roothash. The pkcs7 signature is used to validate the pkcs7 signature of the roothash. The pkcs7 signature is used to validate
the root hash during the creation of the device mapper block device. the root hash during the creation of the device mapper block device.
Verification of roothash depends on the config DM_VERITY_VERIFY_ROOTHASH_SIG Verification of roothash depends on the config DM_VERITY_VERIFY_ROOTHASH_SIG
being set in the kernel. being set in the kernel. The signatures are checked against the builtin
trusted keyring by default, or the secondary trusted keyring if
DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING is set. The secondary
trusted keyring includes by default the builtin trusted keyring, and it can
also gain new certificates at run time if they are signed by a certificate
already in the secondary trusted keyring.
Theory of operation Theory of operation
=================== ===================
......
...@@ -463,6 +463,15 @@ config DM_MULTIPATH_HST ...@@ -463,6 +463,15 @@ config DM_MULTIPATH_HST
If unsure, say N. If unsure, say N.
config DM_MULTIPATH_IOA
tristate "I/O Path Selector based on CPU submission"
depends on DM_MULTIPATH
help
This path selector selects the path based on the CPU the IO is
executed on and the CPU to path mapping setup at path addition time.
If unsure, say N.
config DM_DELAY config DM_DELAY
tristate "I/O delaying target" tristate "I/O delaying target"
depends on BLK_DEV_DM depends on BLK_DEV_DM
...@@ -535,6 +544,17 @@ config DM_VERITY_VERIFY_ROOTHASH_SIG ...@@ -535,6 +544,17 @@ config DM_VERITY_VERIFY_ROOTHASH_SIG
pre-generated tree of cryptographic checksums passed has a pkcs#7 pre-generated tree of cryptographic checksums passed has a pkcs#7
signature file that can validate the roothash of the tree. signature file that can validate the roothash of the tree.
By default, rely on the builtin trusted keyring.
If unsure, say N.
config DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING
bool "Verity data device root hash signature verification with secondary keyring"
depends on DM_VERITY_VERIFY_ROOTHASH_SIG
depends on SECONDARY_TRUSTED_KEYRING
help
Rely on the secondary trusted keyring to verify dm-verity signatures.
If unsure, say N. If unsure, say N.
config DM_VERITY_FEC config DM_VERITY_FEC
......
...@@ -7,11 +7,15 @@ dm-mod-y += dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \ ...@@ -7,11 +7,15 @@ dm-mod-y += dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o dm-stats.o \ dm-ioctl.o dm-io.o dm-kcopyd.o dm-sysfs.o dm-stats.o \
dm-rq.o dm-rq.o
dm-multipath-y += dm-path-selector.o dm-mpath.o dm-multipath-y += dm-path-selector.o dm-mpath.o
dm-historical-service-time-y += dm-ps-historical-service-time.o
dm-io-affinity-y += dm-ps-io-affinity.o
dm-queue-length-y += dm-ps-queue-length.o
dm-round-robin-y += dm-ps-round-robin.o
dm-service-time-y += dm-ps-service-time.o
dm-snapshot-y += dm-snap.o dm-exception-store.o dm-snap-transient.o \ dm-snapshot-y += dm-snap.o dm-exception-store.o dm-snap-transient.o \
dm-snap-persistent.o dm-snap-persistent.o
dm-mirror-y += dm-raid1.o dm-mirror-y += dm-raid1.o
dm-log-userspace-y \ dm-log-userspace-y += dm-log-userspace-base.o dm-log-userspace-transfer.o
+= dm-log-userspace-base.o dm-log-userspace-transfer.o
dm-bio-prison-y += dm-bio-prison-v1.o dm-bio-prison-v2.o dm-bio-prison-y += dm-bio-prison-v1.o dm-bio-prison-v2.o
dm-thin-pool-y += dm-thin.o dm-thin-metadata.o dm-thin-pool-y += dm-thin.o dm-thin-metadata.o
dm-cache-y += dm-cache-target.o dm-cache-metadata.o dm-cache-policy.o \ dm-cache-y += dm-cache-target.o dm-cache-metadata.o dm-cache-policy.o \
...@@ -21,9 +25,10 @@ dm-ebs-y += dm-ebs-target.o ...@@ -21,9 +25,10 @@ dm-ebs-y += dm-ebs-target.o
dm-era-y += dm-era-target.o dm-era-y += dm-era-target.o
dm-clone-y += dm-clone-target.o dm-clone-metadata.o dm-clone-y += dm-clone-target.o dm-clone-metadata.o
dm-verity-y += dm-verity-target.o dm-verity-y += dm-verity-target.o
dm-zoned-y += dm-zoned-target.o dm-zoned-metadata.o dm-zoned-reclaim.o
md-mod-y += md.o md-bitmap.o md-mod-y += md.o md-bitmap.o
raid456-y += raid5.o raid5-cache.o raid5-ppl.o raid456-y += raid5.o raid5-cache.o raid5-ppl.o
dm-zoned-y += dm-zoned-target.o dm-zoned-metadata.o dm-zoned-reclaim.o
linear-y += md-linear.o linear-y += md-linear.o
multipath-y += md-multipath.o multipath-y += md-multipath.o
faulty-y += md-faulty.o faulty-y += md-faulty.o
...@@ -59,6 +64,7 @@ obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o ...@@ -59,6 +64,7 @@ obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o
obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o obj-$(CONFIG_DM_MULTIPATH_QL) += dm-queue-length.o
obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o obj-$(CONFIG_DM_MULTIPATH_ST) += dm-service-time.o
obj-$(CONFIG_DM_MULTIPATH_HST) += dm-historical-service-time.o obj-$(CONFIG_DM_MULTIPATH_HST) += dm-historical-service-time.o
obj-$(CONFIG_DM_MULTIPATH_IOA) += dm-io-affinity.o
obj-$(CONFIG_DM_SWITCH) += dm-switch.o obj-$(CONFIG_DM_SWITCH) += dm-switch.o
obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o
obj-$(CONFIG_DM_PERSISTENT_DATA) += persistent-data/ obj-$(CONFIG_DM_PERSISTENT_DATA) += persistent-data/
......
...@@ -2840,7 +2840,6 @@ static void cache_postsuspend(struct dm_target *ti) ...@@ -2840,7 +2840,6 @@ static void cache_postsuspend(struct dm_target *ti)
static int load_mapping(void *context, dm_oblock_t oblock, dm_cblock_t cblock, static int load_mapping(void *context, dm_oblock_t oblock, dm_cblock_t cblock,
bool dirty, uint32_t hint, bool hint_valid) bool dirty, uint32_t hint, bool hint_valid)
{ {
int r;
struct cache *cache = context; struct cache *cache = context;
if (dirty) { if (dirty) {
...@@ -2849,11 +2848,7 @@ static int load_mapping(void *context, dm_oblock_t oblock, dm_cblock_t cblock, ...@@ -2849,11 +2848,7 @@ static int load_mapping(void *context, dm_oblock_t oblock, dm_cblock_t cblock,
} else } else
clear_bit(from_cblock(cblock), cache->dirty_bitset); clear_bit(from_cblock(cblock), cache->dirty_bitset);
r = policy_load_mapping(cache->policy, oblock, cblock, dirty, hint, hint_valid); return policy_load_mapping(cache->policy, oblock, cblock, dirty, hint, hint_valid);
if (r)
return r;
return 0;
} }
/* /*
......
...@@ -1090,16 +1090,16 @@ static const struct crypt_iv_operations crypt_iv_tcw_ops = { ...@@ -1090,16 +1090,16 @@ static const struct crypt_iv_operations crypt_iv_tcw_ops = {
.post = crypt_iv_tcw_post .post = crypt_iv_tcw_post
}; };
static struct crypt_iv_operations crypt_iv_random_ops = { static const struct crypt_iv_operations crypt_iv_random_ops = {
.generator = crypt_iv_random_gen .generator = crypt_iv_random_gen
}; };
static struct crypt_iv_operations crypt_iv_eboiv_ops = { static const struct crypt_iv_operations crypt_iv_eboiv_ops = {
.ctr = crypt_iv_eboiv_ctr, .ctr = crypt_iv_eboiv_ctr,
.generator = crypt_iv_eboiv_gen .generator = crypt_iv_eboiv_gen
}; };
static struct crypt_iv_operations crypt_iv_elephant_ops = { static const struct crypt_iv_operations crypt_iv_elephant_ops = {
.ctr = crypt_iv_elephant_ctr, .ctr = crypt_iv_elephant_ctr,
.dtr = crypt_iv_elephant_dtr, .dtr = crypt_iv_elephant_dtr,
.init = crypt_iv_elephant_init, .init = crypt_iv_elephant_init,
...@@ -3166,11 +3166,12 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) ...@@ -3166,11 +3166,12 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
} }
if (test_bit(DM_CRYPT_SAME_CPU, &cc->flags)) if (test_bit(DM_CRYPT_SAME_CPU, &cc->flags))
cc->crypt_queue = alloc_workqueue("kcryptd/%s", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM, cc->crypt_queue = alloc_workqueue("kcryptd-%s", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM,
1, devname); 1, devname);
else else
cc->crypt_queue = alloc_workqueue("kcryptd/%s", cc->crypt_queue = alloc_workqueue("kcryptd-%s",
WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND, WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM |
WQ_UNBOUND | WQ_SYSFS,
num_online_cpus(), devname); num_online_cpus(), devname);
if (!cc->crypt_queue) { if (!cc->crypt_queue) {
ti->error = "Couldn't create kcryptd queue"; ti->error = "Couldn't create kcryptd queue";
......
...@@ -86,7 +86,7 @@ static int __ebs_rw_bvec(struct ebs_c *ec, int rw, struct bio_vec *bv, struct bv ...@@ -86,7 +86,7 @@ static int __ebs_rw_bvec(struct ebs_c *ec, int rw, struct bio_vec *bv, struct bv
else else
ba = dm_bufio_new(ec->bufio, block, &b); ba = dm_bufio_new(ec->bufio, block, &b);
if (unlikely(IS_ERR(ba))) { if (IS_ERR(ba)) {
/* /*
* Carry on with next buffer, if any, to issue all possible * Carry on with next buffer, if any, to issue all possible
* data but return error. * data but return error.
......
...@@ -1600,6 +1600,7 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para ...@@ -1600,6 +1600,7 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para
if (!argc) { if (!argc) {
DMWARN("Empty message received."); DMWARN("Empty message received.");
r = -EINVAL;
goto out_argv; goto out_argv;
} }
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2020 Oracle Corporation
*
* Module Author: Mike Christie
*/
#include "dm-path-selector.h"
#include <linux/device-mapper.h>
#include <linux/module.h>
#define DM_MSG_PREFIX "multipath io-affinity"
struct path_info {
struct dm_path *path;
cpumask_var_t cpumask;
refcount_t refcount;
bool failed;
};
struct selector {
struct path_info **path_map;
cpumask_var_t path_mask;
atomic_t map_misses;
};
static void ioa_free_path(struct selector *s, unsigned int cpu)
{
struct path_info *pi = s->path_map[cpu];
if (!pi)
return;
if (refcount_dec_and_test(&pi->refcount)) {
cpumask_clear_cpu(cpu, s->path_mask);
free_cpumask_var(pi->cpumask);
kfree(pi);
s->path_map[cpu] = NULL;
}
}
static int ioa_add_path(struct path_selector *ps, struct dm_path *path,
int argc, char **argv, char **error)
{
struct selector *s = ps->context;
struct path_info *pi = NULL;
unsigned int cpu;
int ret;
if (argc != 1) {
*error = "io-affinity ps: invalid number of arguments";
return -EINVAL;
}
pi = kzalloc(sizeof(*pi), GFP_KERNEL);
if (!pi) {
*error = "io-affinity ps: Error allocating path context";
return -ENOMEM;
}
pi->path = path;
path->pscontext = pi;
refcount_set(&pi->refcount, 1);
if (!zalloc_cpumask_var(&pi->cpumask, GFP_KERNEL)) {
*error = "io-affinity ps: Error allocating cpumask context";
ret = -ENOMEM;
goto free_pi;
}
ret = cpumask_parse(argv[0], pi->cpumask);
if (ret) {
*error = "io-affinity ps: invalid cpumask";
ret = -EINVAL;
goto free_mask;
}
for_each_cpu(cpu, pi->cpumask) {
if (cpu >= nr_cpu_ids) {
DMWARN_LIMIT("Ignoring mapping for CPU %u. Max CPU is %u",
cpu, nr_cpu_ids);
break;
}
if (s->path_map[cpu]) {
DMWARN("CPU mapping for %u exists. Ignoring.", cpu);
continue;
}
cpumask_set_cpu(cpu, s->path_mask);
s->path_map[cpu] = pi;
refcount_inc(&pi->refcount);
continue;
}
if (refcount_dec_and_test(&pi->refcount)) {
*error = "io-affinity ps: No new/valid CPU mapping found";
ret = -EINVAL;
goto free_mask;
}
return 0;
free_mask:
free_cpumask_var(pi->cpumask);
free_pi:
kfree(pi);
return ret;
}
static int ioa_create(struct path_selector *ps, unsigned argc, char **argv)
{
struct selector *s;
s = kmalloc(sizeof(*s), GFP_KERNEL);
if (!s)
return -ENOMEM;
s->path_map = kzalloc(nr_cpu_ids * sizeof(struct path_info *),
GFP_KERNEL);
if (!s->path_map)
goto free_selector;
if (!zalloc_cpumask_var(&s->path_mask, GFP_KERNEL))
goto free_map;
atomic_set(&s->map_misses, 0);
ps->context = s;
return 0;
free_map:
kfree(s->path_map);
free_selector:
kfree(s);
return -ENOMEM;
}
static void ioa_destroy(struct path_selector *ps)
{
struct selector *s = ps->context;
unsigned cpu;
for_each_cpu(cpu, s->path_mask)
ioa_free_path(s, cpu);
free_cpumask_var(s->path_mask);
kfree(s->path_map);
kfree(s);
ps->context = NULL;
}
static int ioa_status(struct path_selector *ps, struct dm_path *path,
status_type_t type, char *result, unsigned int maxlen)
{
struct selector *s = ps->context;
struct path_info *pi;
int sz = 0;
if (!path) {
DMEMIT("0 ");
return sz;
}
switch(type) {
case STATUSTYPE_INFO:
DMEMIT("%d ", atomic_read(&s->map_misses));
break;
case STATUSTYPE_TABLE:
pi = path->pscontext;
DMEMIT("%*pb ", cpumask_pr_args(pi->cpumask));
break;
}
return sz;
}
static void ioa_fail_path(struct path_selector *ps, struct dm_path *p)
{
struct path_info *pi = p->pscontext;
pi->failed = true;
}
static int ioa_reinstate_path(struct path_selector *ps, struct dm_path *p)
{
struct path_info *pi = p->pscontext;
pi->failed = false;
return 0;
}
static struct dm_path *ioa_select_path(struct path_selector *ps,
size_t nr_bytes)
{
unsigned int cpu, node;
struct selector *s = ps->context;
const struct cpumask *cpumask;
struct path_info *pi;
int i;
cpu = get_cpu();
pi = s->path_map[cpu];
if (pi && !pi->failed)
goto done;
/*
* Perf is not optimal, but we at least try the local node then just
* try not to fail.
*/
if (!pi)
atomic_inc(&s->map_misses);
node = cpu_to_node(cpu);
cpumask = cpumask_of_node(node);
for_each_cpu(i, cpumask) {
pi = s->path_map[i];
if (pi && !pi->failed)
goto done;
}
for_each_cpu(i, s->path_mask) {
pi = s->path_map[i];
if (pi && !pi->failed)
goto done;
}
pi = NULL;
done:
put_cpu();
return pi ? pi->path : NULL;
}
static struct path_selector_type ioa_ps = {
.name = "io-affinity",
.module = THIS_MODULE,
.table_args = 1,
.info_args = 1,
.create = ioa_create,
.destroy = ioa_destroy,
.status = ioa_status,
.add_path = ioa_add_path,
.fail_path = ioa_fail_path,
.reinstate_path = ioa_reinstate_path,
.select_path = ioa_select_path,
};
static int __init dm_ioa_init(void)
{
int ret = dm_register_path_selector(&ioa_ps);
if (ret < 0)
DMERR("register failed %d", ret);
return ret;
}
static void __exit dm_ioa_exit(void)
{
int ret = dm_unregister_path_selector(&ioa_ps);
if (ret < 0)
DMERR("unregister failed %d", ret);
}
module_init(dm_ioa_init);
module_exit(dm_ioa_exit);
MODULE_DESCRIPTION(DM_NAME " multipath path selector that selects paths based on the CPU IO is being executed on");
MODULE_AUTHOR("Mike Christie <michael.christie@oracle.com>");
MODULE_LICENSE("GPL");
...@@ -496,7 +496,7 @@ static void stripe_io_hints(struct dm_target *ti, ...@@ -496,7 +496,7 @@ static void stripe_io_hints(struct dm_target *ti,
static struct target_type stripe_target = { static struct target_type stripe_target = {
.name = "striped", .name = "striped",
.version = {1, 6, 0}, .version = {1, 6, 0},
.features = DM_TARGET_PASSES_INTEGRITY, .features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_NOWAIT,
.module = THIS_MODULE, .module = THIS_MODULE,
.ctr = stripe_ctr, .ctr = stripe_ctr,
.dtr = stripe_dtr, .dtr = stripe_dtr,
......
...@@ -550,6 +550,7 @@ static int switch_iterate_devices(struct dm_target *ti, ...@@ -550,6 +550,7 @@ static int switch_iterate_devices(struct dm_target *ti,
static struct target_type switch_target = { static struct target_type switch_target = {
.name = "switch", .name = "switch",
.version = {1, 1, 0}, .version = {1, 1, 0},
.features = DM_TARGET_NOWAIT,
.module = THIS_MODULE, .module = THIS_MODULE,
.ctr = switch_ctr, .ctr = switch_ctr,
.dtr = switch_dtr, .dtr = switch_dtr,
......
...@@ -178,6 +178,7 @@ static void unstripe_io_hints(struct dm_target *ti, ...@@ -178,6 +178,7 @@ static void unstripe_io_hints(struct dm_target *ti,
static struct target_type unstripe_target = { static struct target_type unstripe_target = {
.name = "unstriped", .name = "unstriped",
.version = {1, 1, 0}, .version = {1, 1, 0},
.features = DM_TARGET_NOWAIT,
.module = THIS_MODULE, .module = THIS_MODULE,
.ctr = unstripe_ctr, .ctr = unstripe_ctr,
.dtr = unstripe_dtr, .dtr = unstripe_dtr,
......
...@@ -537,6 +537,15 @@ static int verity_verify_io(struct dm_verity_io *io) ...@@ -537,6 +537,15 @@ static int verity_verify_io(struct dm_verity_io *io)
return 0; return 0;
} }
/*
* Skip verity work in response to I/O error when system is shutting down.
*/
static inline bool verity_is_system_shutting_down(void)
{
return system_state == SYSTEM_HALT || system_state == SYSTEM_POWER_OFF
|| system_state == SYSTEM_RESTART;
}
/* /*
* End one "io" structure with a given error. * End one "io" structure with a given error.
*/ */
...@@ -564,7 +573,8 @@ static void verity_end_io(struct bio *bio) ...@@ -564,7 +573,8 @@ static void verity_end_io(struct bio *bio)
{ {
struct dm_verity_io *io = bio->bi_private; struct dm_verity_io *io = bio->bi_private;
if (bio->bi_status && !verity_fec_is_enabled(io->v)) { if (bio->bi_status &&
(!verity_fec_is_enabled(io->v) || verity_is_system_shutting_down())) {
verity_finish_io(io, bio->bi_status); verity_finish_io(io, bio->bi_status);
return; return;
} }
......
...@@ -119,8 +119,13 @@ int verity_verify_root_hash(const void *root_hash, size_t root_hash_len, ...@@ -119,8 +119,13 @@ int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
} }
ret = verify_pkcs7_signature(root_hash, root_hash_len, sig_data, ret = verify_pkcs7_signature(root_hash, root_hash_len, sig_data,
sig_len, NULL, VERIFYING_UNSPECIFIED_SIGNATURE, sig_len,
NULL, NULL); #ifdef CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG_SECONDARY_KEYRING
VERIFY_USE_SECONDARY_KEYRING,
#else
NULL,
#endif
VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL);
return ret; return ret;
} }
......
...@@ -59,6 +59,7 @@ static int zero_map(struct dm_target *ti, struct bio *bio) ...@@ -59,6 +59,7 @@ static int zero_map(struct dm_target *ti, struct bio *bio)
static struct target_type zero_target = { static struct target_type zero_target = {
.name = "zero", .name = "zero",
.version = {1, 1, 0}, .version = {1, 1, 0},
.features = DM_TARGET_NOWAIT,
.module = THIS_MODULE, .module = THIS_MODULE,
.ctr = zero_ctr, .ctr = zero_ctr,
.map = zero_map, .map = zero_map,
......
...@@ -1586,7 +1586,7 @@ static blk_qc_t __split_and_process_bio(struct mapped_device *md, ...@@ -1586,7 +1586,7 @@ static blk_qc_t __split_and_process_bio(struct mapped_device *md,
ci.sector_count = bio_sectors(bio); ci.sector_count = bio_sectors(bio);
while (ci.sector_count && !error) { while (ci.sector_count && !error) {
error = __split_and_process_non_flush(&ci); error = __split_and_process_non_flush(&ci);
if (current->bio_list && ci.sector_count && !error) { if (ci.sector_count && !error) {
/* /*
* Remainder must be passed to submit_bio_noacct() * Remainder must be passed to submit_bio_noacct()
* so that it gets handled *after* bios already submitted * so that it gets handled *after* bios already submitted
......
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