summaryrefslogtreecommitdiff
path: root/drivers/block
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/brd.c1
-rw-r--r--drivers/block/drbd/drbd_main.c3
-rw-r--r--drivers/block/drbd/drbd_nl.c4
-rw-r--r--drivers/block/loop.c186
-rw-r--r--drivers/block/loop.h1
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c8
-rw-r--r--drivers/block/nbd.c8
-rw-r--r--drivers/block/null_blk.c124
-rw-r--r--drivers/block/paride/pcd.c2
-rw-r--r--drivers/block/rbd.c2558
-rw-r--r--drivers/block/rsxx/dev.c6
-rw-r--r--drivers/block/skd_main.c4
-rw-r--r--drivers/block/swim.c49
-rw-r--r--drivers/block/swim3.c6
-rw-r--r--drivers/block/umem.c7
-rw-r--r--drivers/block/xen-blkfront.c10
-rw-r--r--drivers/block/zram/zram_drv.c17
-rw-r--r--drivers/block/zram/zram_drv.h17
18 files changed, 1312 insertions, 1699 deletions
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index deea78e485da..66cb0f857f64 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -24,7 +24,6 @@
#include <linux/uaccess.h>
-#define SECTOR_SHIFT 9
#define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
#define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT)
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 0a0394aa1b9c..185f1ef00a7c 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -2816,7 +2816,7 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
drbd_init_set_defaults(device);
- q = blk_alloc_queue(GFP_KERNEL);
+ q = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE, &resource->req_lock);
if (!q)
goto out_no_q;
device->rq_queue = q;
@@ -2848,7 +2848,6 @@ enum drbd_ret_code drbd_create_device(struct drbd_config_context *adm_ctx, unsig
/* Setting the max_hw_sectors to an odd value of 8kibyte here
This triggers a max_bio_size message upon first attach or connect */
blk_queue_max_hw_sectors(q, DRBD_MAX_BIO_SIZE_SAFE >> 8);
- q->queue_lock = &resource->req_lock;
device->md_io.page = alloc_page(GFP_KERNEL);
if (!device->md_io.page)
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index a12f77e6891e..b4f02768ba47 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -1212,10 +1212,10 @@ static void decide_on_discard_support(struct drbd_device *device,
* topology on all peers. */
blk_queue_discard_granularity(q, 512);
q->limits.max_discard_sectors = drbd_max_discard_sectors(connection);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
q->limits.max_write_zeroes_sectors = drbd_max_discard_sectors(connection);
} else {
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q);
blk_queue_discard_granularity(q, 0);
q->limits.max_discard_sectors = 0;
q->limits.max_write_zeroes_sectors = 0;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index ee62d2d517bf..5d4e31655d96 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -214,10 +214,10 @@ static void __loop_update_dio(struct loop_device *lo, bool dio)
blk_mq_freeze_queue(lo->lo_queue);
lo->use_dio = use_dio;
if (use_dio) {
- queue_flag_clear_unlocked(QUEUE_FLAG_NOMERGES, lo->lo_queue);
+ blk_queue_flag_clear(QUEUE_FLAG_NOMERGES, lo->lo_queue);
lo->lo_flags |= LO_FLAGS_DIRECT_IO;
} else {
- queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, lo->lo_queue);
+ blk_queue_flag_set(QUEUE_FLAG_NOMERGES, lo->lo_queue);
lo->lo_flags &= ~LO_FLAGS_DIRECT_IO;
}
blk_mq_unfreeze_queue(lo->lo_queue);
@@ -451,25 +451,47 @@ static int lo_req_flush(struct loop_device *lo, struct request *rq)
static void lo_complete_rq(struct request *rq)
{
struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ blk_status_t ret = BLK_STS_OK;
- if (unlikely(req_op(cmd->rq) == REQ_OP_READ && cmd->use_aio &&
- cmd->ret >= 0 && cmd->ret < blk_rq_bytes(cmd->rq))) {
- struct bio *bio = cmd->rq->bio;
-
- bio_advance(bio, cmd->ret);
- zero_fill_bio(bio);
+ if (!cmd->use_aio || cmd->ret < 0 || cmd->ret == blk_rq_bytes(rq) ||
+ req_op(rq) != REQ_OP_READ) {
+ if (cmd->ret < 0)
+ ret = BLK_STS_IOERR;
+ goto end_io;
}
- blk_mq_end_request(rq, cmd->ret < 0 ? BLK_STS_IOERR : BLK_STS_OK);
+ /*
+ * Short READ - if we got some data, advance our request and
+ * retry it. If we got no data, end the rest with EIO.
+ */
+ if (cmd->ret) {
+ blk_update_request(rq, BLK_STS_OK, cmd->ret);
+ cmd->ret = 0;
+ blk_mq_requeue_request(rq, true);
+ } else {
+ if (cmd->use_aio) {
+ struct bio *bio = rq->bio;
+
+ while (bio) {
+ zero_fill_bio(bio);
+ bio = bio->bi_next;
+ }
+ }
+ ret = BLK_STS_IOERR;
+end_io:
+ blk_mq_end_request(rq, ret);
+ }
}
static void lo_rw_aio_do_completion(struct loop_cmd *cmd)
{
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
+
if (!atomic_dec_and_test(&cmd->ref))
return;
kfree(cmd->bvec);
cmd->bvec = NULL;
- blk_mq_complete_request(cmd->rq);
+ blk_mq_complete_request(rq);
}
static void lo_rw_aio_complete(struct kiocb *iocb, long ret, long ret2)
@@ -487,7 +509,7 @@ static int lo_rw_aio(struct loop_device *lo, struct loop_cmd *cmd,
{
struct iov_iter iter;
struct bio_vec *bvec;
- struct request *rq = cmd->rq;
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
struct bio *bio = rq->bio;
struct file *file = lo->lo_backing_file;
unsigned int offset;
@@ -817,7 +839,7 @@ static void loop_config_discard(struct loop_device *lo)
q->limits.discard_alignment = 0;
blk_queue_max_discard_sectors(q, 0);
blk_queue_max_write_zeroes_sectors(q, 0);
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, q);
return;
}
@@ -826,7 +848,7 @@ static void loop_config_discard(struct loop_device *lo)
blk_queue_max_discard_sectors(q, UINT_MAX >> 9);
blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> 9);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
}
static void loop_unprepare_queue(struct loop_device *lo)
@@ -1103,11 +1125,15 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
if (info->lo_encrypt_type) {
unsigned int type = info->lo_encrypt_type;
- if (type >= MAX_LO_CRYPT)
- return -EINVAL;
+ if (type >= MAX_LO_CRYPT) {
+ err = -EINVAL;
+ goto exit;
+ }
xfer = xfer_funcs[type];
- if (xfer == NULL)
- return -EINVAL;
+ if (xfer == NULL) {
+ err = -EINVAL;
+ goto exit;
+ }
} else
xfer = NULL;
@@ -1167,21 +1193,17 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
static int
loop_get_status(struct loop_device *lo, struct loop_info64 *info)
{
- struct file *file = lo->lo_backing_file;
+ struct file *file;
struct kstat stat;
- int error;
+ int ret;
- if (lo->lo_state != Lo_bound)
+ if (lo->lo_state != Lo_bound) {
+ mutex_unlock(&lo->lo_ctl_mutex);
return -ENXIO;
- error = vfs_getattr(&file->f_path, &stat,
- STATX_INO, AT_STATX_SYNC_AS_STAT);
- if (error)
- return error;
+ }
+
memset(info, 0, sizeof(*info));
info->lo_number = lo->lo_number;
- info->lo_device = huge_encode_dev(stat.dev);
- info->lo_inode = stat.ino;
- info->lo_rdevice = huge_encode_dev(lo->lo_device ? stat.rdev : stat.dev);
info->lo_offset = lo->lo_offset;
info->lo_sizelimit = lo->lo_sizelimit;
info->lo_flags = lo->lo_flags;
@@ -1194,7 +1216,19 @@ loop_get_status(struct loop_device *lo, struct loop_info64 *info)
memcpy(info->lo_encrypt_key, lo->lo_encrypt_key,
lo->lo_encrypt_key_size);
}
- return 0;
+
+ /* Drop lo_ctl_mutex while we call into the filesystem. */
+ file = get_file(lo->lo_backing_file);
+ mutex_unlock(&lo->lo_ctl_mutex);
+ ret = vfs_getattr(&file->f_path, &stat, STATX_INO,
+ AT_STATX_SYNC_AS_STAT);
+ if (!ret) {
+ info->lo_device = huge_encode_dev(stat.dev);
+ info->lo_inode = stat.ino;
+ info->lo_rdevice = huge_encode_dev(stat.rdev);
+ }
+ fput(file);
+ return ret;
}
static void
@@ -1275,12 +1309,13 @@ static int
loop_get_status_old(struct loop_device *lo, struct loop_info __user *arg) {
struct loop_info info;
struct loop_info64 info64;
- int err = 0;
+ int err;
- if (!arg)
- err = -EINVAL;
- if (!err)
- err = loop_get_status(lo, &info64);
+ if (!arg) {
+ mutex_unlock(&lo->lo_ctl_mutex);
+ return -EINVAL;
+ }
+ err = loop_get_status(lo, &info64);
if (!err)
err = loop_info64_to_old(&info64, &info);
if (!err && copy_to_user(arg, &info, sizeof(info)))
@@ -1292,12 +1327,13 @@ loop_get_status_old(struct loop_device *lo, struct loop_info __user *arg) {
static int
loop_get_status64(struct loop_device *lo, struct loop_info64 __user *arg) {
struct loop_info64 info64;
- int err = 0;
+ int err;
- if (!arg)
- err = -EINVAL;
- if (!err)
- err = loop_get_status(lo, &info64);
+ if (!arg) {
+ mutex_unlock(&lo->lo_ctl_mutex);
+ return -EINVAL;
+ }
+ err = loop_get_status(lo, &info64);
if (!err && copy_to_user(arg, &info64, sizeof(info64)))
err = -EFAULT;
@@ -1352,7 +1388,10 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
struct loop_device *lo = bdev->bd_disk->private_data;
int err;
- mutex_lock_nested(&lo->lo_ctl_mutex, 1);
+ err = mutex_lock_killable_nested(&lo->lo_ctl_mutex, 1);
+ if (err)
+ goto out_unlocked;
+
switch (cmd) {
case LOOP_SET_FD:
err = loop_set_fd(lo, mode, bdev, arg);
@@ -1374,7 +1413,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
break;
case LOOP_GET_STATUS:
err = loop_get_status_old(lo, (struct loop_info __user *) arg);
- break;
+ /* loop_get_status() unlocks lo_ctl_mutex */
+ goto out_unlocked;
case LOOP_SET_STATUS64:
err = -EPERM;
if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
@@ -1383,7 +1423,8 @@ static int lo_ioctl(struct block_device *bdev, fmode_t mode,
break;
case LOOP_GET_STATUS64:
err = loop_get_status64(lo, (struct loop_info64 __user *) arg);
- break;
+ /* loop_get_status() unlocks lo_ctl_mutex */
+ goto out_unlocked;
case LOOP_SET_CAPACITY:
err = -EPERM;
if ((mode & FMODE_WRITE) || capable(CAP_SYS_ADMIN))
@@ -1516,12 +1557,13 @@ loop_get_status_compat(struct loop_device *lo,
struct compat_loop_info __user *arg)
{
struct loop_info64 info64;
- int err = 0;
+ int err;
- if (!arg)
- err = -EINVAL;
- if (!err)
- err = loop_get_status(lo, &info64);
+ if (!arg) {
+ mutex_unlock(&lo->lo_ctl_mutex);
+ return -EINVAL;
+ }
+ err = loop_get_status(lo, &info64);
if (!err)
err = loop_info64_to_compat(&info64, arg);
return err;
@@ -1535,16 +1577,20 @@ static int lo_compat_ioctl(struct block_device *bdev, fmode_t mode,
switch(cmd) {
case LOOP_SET_STATUS:
- mutex_lock(&lo->lo_ctl_mutex);
- err = loop_set_status_compat(
- lo, (const struct compat_loop_info __user *) arg);
- mutex_unlock(&lo->lo_ctl_mutex);
+ err = mutex_lock_killable(&lo->lo_ctl_mutex);
+ if (!err) {
+ err = loop_set_status_compat(lo,
+ (const struct compat_loop_info __user *)arg);
+ mutex_unlock(&lo->lo_ctl_mutex);
+ }
break;
case LOOP_GET_STATUS:
- mutex_lock(&lo->lo_ctl_mutex);
- err = loop_get_status_compat(
- lo, (struct compat_loop_info __user *) arg);
- mutex_unlock(&lo->lo_ctl_mutex);
+ err = mutex_lock_killable(&lo->lo_ctl_mutex);
+ if (!err) {
+ err = loop_get_status_compat(lo,
+ (struct compat_loop_info __user *)arg);
+ /* loop_get_status() unlocks lo_ctl_mutex */
+ }
break;
case LOOP_SET_CAPACITY:
case LOOP_CLR_FD:
@@ -1678,15 +1724,16 @@ EXPORT_SYMBOL(loop_unregister_transfer);
static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
- struct loop_cmd *cmd = blk_mq_rq_to_pdu(bd->rq);
- struct loop_device *lo = cmd->rq->q->queuedata;
+ struct request *rq = bd->rq;
+ struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
+ struct loop_device *lo = rq->q->queuedata;
- blk_mq_start_request(bd->rq);
+ blk_mq_start_request(rq);
if (lo->lo_state != Lo_bound)
return BLK_STS_IOERR;
- switch (req_op(cmd->rq)) {
+ switch (req_op(rq)) {
case REQ_OP_FLUSH:
case REQ_OP_DISCARD:
case REQ_OP_WRITE_ZEROES:
@@ -1699,8 +1746,8 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
/* always use the first bio's css */
#ifdef CONFIG_BLK_CGROUP
- if (cmd->use_aio && cmd->rq->bio && cmd->rq->bio->bi_css) {
- cmd->css = cmd->rq->bio->bi_css;
+ if (cmd->use_aio && rq->bio && rq->bio->bi_css) {
+ cmd->css = rq->bio->bi_css;
css_get(cmd->css);
} else
#endif
@@ -1712,8 +1759,9 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx,
static void loop_handle_cmd(struct loop_cmd *cmd)
{
- const bool write = op_is_write(req_op(cmd->rq));
- struct loop_device *lo = cmd->rq->q->queuedata;
+ struct request *rq = blk_mq_rq_from_pdu(cmd);
+ const bool write = op_is_write(req_op(rq));
+ struct loop_device *lo = rq->q->queuedata;
int ret = 0;
if (write && (lo->lo_flags & LO_FLAGS_READ_ONLY)) {
@@ -1721,12 +1769,12 @@ static void loop_handle_cmd(struct loop_cmd *cmd)
goto failed;
}
- ret = do_req_filebacked(lo, cmd->rq);
+ ret = do_req_filebacked(lo, rq);
failed:
/* complete non-aio request */
if (!cmd->use_aio || ret) {
cmd->ret = ret ? -EIO : 0;
- blk_mq_complete_request(cmd->rq);
+ blk_mq_complete_request(rq);
}
}
@@ -1743,9 +1791,7 @@ static int loop_init_request(struct blk_mq_tag_set *set, struct request *rq,
{
struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
- cmd->rq = rq;
kthread_init_work(&cmd->work, loop_queue_work);
-
return 0;
}
@@ -1808,7 +1854,7 @@ static int loop_add(struct loop_device **l, int i)
* page. For directio mode, merge does help to dispatch bigger request
* to underlayer disk. We will enable merge once directio is enabled.
*/
- queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, lo->lo_queue);
+ blk_queue_flag_set(QUEUE_FLAG_NOMERGES, lo->lo_queue);
err = -ENOMEM;
disk = lo->lo_disk = alloc_disk(1 << part_shift);
@@ -1864,8 +1910,8 @@ out:
static void loop_remove(struct loop_device *lo)
{
- blk_cleanup_queue(lo->lo_queue);
del_gendisk(lo->lo_disk);
+ blk_cleanup_queue(lo->lo_queue);
blk_mq_free_tag_set(&lo->tag_set);
put_disk(lo->lo_disk);
kfree(lo);
@@ -1949,7 +1995,9 @@ static long loop_control_ioctl(struct file *file, unsigned int cmd,
ret = loop_lookup(&lo, parm);
if (ret < 0)
break;
- mutex_lock(&lo->lo_ctl_mutex);
+ ret = mutex_lock_killable(&lo->lo_ctl_mutex);
+ if (ret)
+ break;
if (lo->lo_state != Lo_unbound) {
ret = -EBUSY;
mutex_unlock(&lo->lo_ctl_mutex);
diff --git a/drivers/block/loop.h b/drivers/block/loop.h
index 0f45416e4fcf..b78de9879f4f 100644
--- a/drivers/block/loop.h
+++ b/drivers/block/loop.h
@@ -66,7 +66,6 @@ struct loop_device {
struct loop_cmd {
struct kthread_work work;
- struct request *rq;
bool use_aio; /* use AIO interface to handle I/O */
atomic_t ref; /* only for aio */
long ret;
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index b8af7352a18f..769c551e3d71 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -159,7 +159,7 @@ static bool mtip_check_surprise_removal(struct pci_dev *pdev)
if (vendor_id == 0xFFFF) {
dd->sr = true;
if (dd->queue)
- set_bit(QUEUE_FLAG_DEAD, &dd->queue->queue_flags);
+ blk_queue_flag_set(QUEUE_FLAG_DEAD, dd->queue);
else
dev_warn(&dd->pdev->dev,
"%s: dd->queue is NULL\n", __func__);
@@ -3855,8 +3855,8 @@ skip_create_disk:
goto start_service_thread;
/* Set device limits. */
- set_bit(QUEUE_FLAG_NONROT, &dd->queue->queue_flags);
- clear_bit(QUEUE_FLAG_ADD_RANDOM, &dd->queue->queue_flags);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, dd->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, dd->queue);
blk_queue_max_segments(dd->queue, MTIP_MAX_SG);
blk_queue_physical_block_size(dd->queue, 4096);
blk_queue_max_hw_sectors(dd->queue, 0xffff);
@@ -3866,7 +3866,7 @@ skip_create_disk:
/* Signal trim support */
if (dd->trim_supp == true) {
- set_bit(QUEUE_FLAG_DISCARD, &dd->queue->queue_flags);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, dd->queue);
dd->queue->limits.discard_granularity = 4096;
blk_queue_max_discard_sectors(dd->queue,
MTIP_MAX_TRIM_ENTRY_LEN * MTIP_MAX_TRIM_ENTRIES);
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 86258b00a1d4..afbc202ca6fd 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -964,7 +964,7 @@ static void nbd_parse_flags(struct nbd_device *nbd)
else
set_disk_ro(nbd->disk, false);
if (config->flags & NBD_FLAG_SEND_TRIM)
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, nbd->disk->queue);
if (config->flags & NBD_FLAG_SEND_FLUSH) {
if (config->flags & NBD_FLAG_SEND_FUA)
blk_queue_write_cache(nbd->disk->queue, true, true);
@@ -1040,7 +1040,7 @@ static void nbd_config_put(struct nbd_device *nbd)
nbd->config = NULL;
nbd->tag_set.timeout = 0;
- queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, nbd->disk->queue);
mutex_unlock(&nbd->config_lock);
nbd_put(nbd);
@@ -1488,8 +1488,8 @@ static int nbd_dev_add(int index)
/*
* Tell the block layer that we are not a rotational device
*/
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, disk->queue);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, disk->queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, disk->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, disk->queue);
disk->queue->limits.discard_granularity = 512;
blk_queue_max_discard_sectors(disk->queue, UINT_MAX);
blk_queue_max_segment_size(disk->queue, UINT_MAX);
diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c
index 287a09611c0f..a76553293a31 100644
--- a/drivers/block/null_blk.c
+++ b/drivers/block/null_blk.c
@@ -16,10 +16,8 @@
#include <linux/badblocks.h>
#include <linux/fault-inject.h>
-#define SECTOR_SHIFT 9
#define PAGE_SECTORS_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
#define PAGE_SECTORS (1 << PAGE_SECTORS_SHIFT)
-#define SECTOR_SIZE (1 << SECTOR_SHIFT)
#define SECTOR_MASK (PAGE_SECTORS - 1)
#define FREE_BATCH 16
@@ -29,6 +27,7 @@
#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
static DECLARE_FAULT_ATTR(null_timeout_attr);
+static DECLARE_FAULT_ATTR(null_requeue_attr);
#endif
static inline u64 mb_per_tick(int mbps)
@@ -53,6 +52,7 @@ struct nullb_queue {
wait_queue_head_t wait;
unsigned int queue_depth;
struct nullb_device *dev;
+ unsigned int requeue_selection;
struct nullb_cmd *cmds;
};
@@ -72,6 +72,7 @@ enum nullb_device_flags {
NULLB_DEV_FL_CACHE = 3,
};
+#define MAP_SZ ((PAGE_SIZE >> SECTOR_SHIFT) + 2)
/*
* nullb_page is a page in memory for nullb devices.
*
@@ -86,10 +87,10 @@ enum nullb_device_flags {
*/
struct nullb_page {
struct page *page;
- unsigned long bitmap;
+ DECLARE_BITMAP(bitmap, MAP_SZ);
};
-#define NULLB_PAGE_LOCK (sizeof(unsigned long) * 8 - 1)
-#define NULLB_PAGE_FREE (sizeof(unsigned long) * 8 - 2)
+#define NULLB_PAGE_LOCK (MAP_SZ - 1)
+#define NULLB_PAGE_FREE (MAP_SZ - 2)
struct nullb_device {
struct nullb *nullb;
@@ -170,6 +171,9 @@ MODULE_PARM_DESC(home_node, "Home node for the device");
#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
static char g_timeout_str[80];
module_param_string(timeout, g_timeout_str, sizeof(g_timeout_str), S_IRUGO);
+
+static char g_requeue_str[80];
+module_param_string(requeue, g_requeue_str, sizeof(g_requeue_str), S_IRUGO);
#endif
static int g_queue_mode = NULL_Q_MQ;
@@ -728,7 +732,7 @@ static struct nullb_page *null_alloc_page(gfp_t gfp_flags)
if (!t_page->page)
goto out_freepage;
- t_page->bitmap = 0;
+ memset(t_page->bitmap, 0, sizeof(t_page->bitmap));
return t_page;
out_freepage:
kfree(t_page);
@@ -738,13 +742,20 @@ out:
static void null_free_page(struct nullb_page *t_page)
{
- __set_bit(NULLB_PAGE_FREE, &t_page->bitmap);
- if (test_bit(NULLB_PAGE_LOCK, &t_page->bitmap))
+ __set_bit(NULLB_PAGE_FREE, t_page->bitmap);
+ if (test_bit(NULLB_PAGE_LOCK, t_page->bitmap))
return;
__free_page(t_page->page);
kfree(t_page);
}
+static bool null_page_empty(struct nullb_page *page)
+{
+ int size = MAP_SZ - 2;
+
+ return find_first_bit(page->bitmap, size) == size;
+}
+
static void null_free_sector(struct nullb *nullb, sector_t sector,
bool is_cache)
{
@@ -759,9 +770,9 @@ static void null_free_sector(struct nullb *nullb, sector_t sector,
t_page = radix_tree_lookup(root, idx);
if (t_page) {
- __clear_bit(sector_bit, &t_page->bitmap);
+ __clear_bit(sector_bit, t_page->bitmap);
- if (!t_page->bitmap) {
+ if (null_page_empty(t_page)) {
ret = radix_tree_delete_item(root, idx, t_page);
WARN_ON(ret != t_page);
null_free_page(ret);
@@ -832,7 +843,7 @@ static struct nullb_page *__null_lookup_page(struct nullb *nullb,
t_page = radix_tree_lookup(root, idx);
WARN_ON(t_page && t_page->page->index != idx);
- if (t_page && (for_write || test_bit(sector_bit, &t_page->bitmap)))
+ if (t_page && (for_write || test_bit(sector_bit, t_page->bitmap)))
return t_page;
return NULL;
@@ -895,10 +906,10 @@ static int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page)
t_page = null_insert_page(nullb, idx << PAGE_SECTORS_SHIFT, true);
- __clear_bit(NULLB_PAGE_LOCK, &c_page->bitmap);
- if (test_bit(NULLB_PAGE_FREE, &c_page->bitmap)) {
+ __clear_bit(NULLB_PAGE_LOCK, c_page->bitmap);
+ if (test_bit(NULLB_PAGE_FREE, c_page->bitmap)) {
null_free_page(c_page);
- if (t_page && t_page->bitmap == 0) {
+ if (t_page && null_page_empty(t_page)) {
ret = radix_tree_delete_item(&nullb->dev->data,
idx, t_page);
null_free_page(t_page);
@@ -914,11 +925,11 @@ static int null_flush_cache_page(struct nullb *nullb, struct nullb_page *c_page)
for (i = 0; i < PAGE_SECTORS;
i += (nullb->dev->blocksize >> SECTOR_SHIFT)) {
- if (test_bit(i, &c_page->bitmap)) {
+ if (test_bit(i, c_page->bitmap)) {
offset = (i << SECTOR_SHIFT);
memcpy(dst + offset, src + offset,
nullb->dev->blocksize);
- __set_bit(i, &t_page->bitmap);
+ __set_bit(i, t_page->bitmap);
}
}
@@ -955,10 +966,10 @@ again:
* We found the page which is being flushed to disk by other
* threads
*/
- if (test_bit(NULLB_PAGE_LOCK, &c_pages[i]->bitmap))
+ if (test_bit(NULLB_PAGE_LOCK, c_pages[i]->bitmap))
c_pages[i] = NULL;
else
- __set_bit(NULLB_PAGE_LOCK, &c_pages[i]->bitmap);
+ __set_bit(NULLB_PAGE_LOCK, c_pages[i]->bitmap);
}
one_round = 0;
@@ -1011,7 +1022,7 @@ static int copy_to_nullb(struct nullb *nullb, struct page *source,
kunmap_atomic(dst);
kunmap_atomic(src);
- __set_bit(sector & SECTOR_MASK, &t_page->bitmap);
+ __set_bit(sector & SECTOR_MASK, t_page->bitmap);
if (is_fua)
null_free_sector(nullb, sector, true);
@@ -1380,7 +1391,15 @@ static bool should_timeout_request(struct request *rq)
if (g_timeout_str[0])
return should_fail(&null_timeout_attr, 1);
#endif
+ return false;
+}
+static bool should_requeue_request(struct request *rq)
+{
+#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
+ if (g_requeue_str[0])
+ return should_fail(&null_requeue_attr, 1);
+#endif
return false;
}
@@ -1391,11 +1410,17 @@ static void null_request_fn(struct request_queue *q)
while ((rq = blk_fetch_request(q)) != NULL) {
struct nullb_cmd *cmd = rq->special;
- if (!should_timeout_request(rq)) {
- spin_unlock_irq(q->queue_lock);
- null_handle_cmd(cmd);
- spin_lock_irq(q->queue_lock);
+ /* just ignore the request */
+ if (should_timeout_request(rq))
+ continue;
+ if (should_requeue_request(rq)) {
+ blk_requeue_request(q, rq);
+ continue;
}
+
+ spin_unlock_irq(q->queue_lock);
+ null_handle_cmd(cmd);
+ spin_lock_irq(q->queue_lock);
}
}
@@ -1422,10 +1447,23 @@ static blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_mq_start_request(bd->rq);
- if (!should_timeout_request(bd->rq))
- return null_handle_cmd(cmd);
+ if (should_requeue_request(bd->rq)) {
+ /*
+ * Alternate between hitting the core BUSY path, and the
+ * driver driven requeue path
+ */
+ nq->requeue_selection++;
+ if (nq->requeue_selection & 1)
+ return BLK_STS_RESOURCE;
+ else {
+ blk_mq_requeue_request(bd->rq, true);
+ return BLK_STS_OK;
+ }
+ }
+ if (should_timeout_request(bd->rq))
+ return BLK_STS_OK;
- return BLK_STS_OK;
+ return null_handle_cmd(cmd);
}
static const struct blk_mq_ops null_mq_ops = {
@@ -1485,7 +1523,7 @@ static void null_config_discard(struct nullb *nullb)
nullb->q->limits.discard_granularity = nullb->dev->blocksize;
nullb->q->limits.discard_alignment = nullb->dev->blocksize;
blk_queue_max_discard_sectors(nullb->q, UINT_MAX >> 9);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, nullb->q);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, nullb->q);
}
static int null_open(struct block_device *bdev, fmode_t mode)
@@ -1659,16 +1697,27 @@ static void null_validate_conf(struct nullb_device *dev)
dev->mbps = 0;
}
-static bool null_setup_fault(void)
-{
#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
- if (!g_timeout_str[0])
+static bool __null_setup_fault(struct fault_attr *attr, char *str)
+{
+ if (!str[0])
return true;
- if (!setup_fault_attr(&null_timeout_attr, g_timeout_str))
+ if (!setup_fault_attr(attr, str))
return false;
- null_timeout_attr.verbose = 0;
+ attr->verbose = 0;
+ return true;
+}
+#endif
+
+static bool null_setup_fault(void)
+{
+#ifdef CONFIG_BLK_DEV_NULL_BLK_FAULT_INJECTION
+ if (!__null_setup_fault(&null_timeout_attr, g_timeout_str))
+ return false;
+ if (!__null_setup_fault(&null_requeue_attr, g_requeue_str))
+ return false;
#endif
return true;
}
@@ -1717,7 +1766,8 @@ static int null_add_dev(struct nullb_device *dev)
}
null_init_queues(nullb);
} else if (dev->queue_mode == NULL_Q_BIO) {
- nullb->q = blk_alloc_queue_node(GFP_KERNEL, dev->home_node);
+ nullb->q = blk_alloc_queue_node(GFP_KERNEL, dev->home_node,
+ NULL);
if (!nullb->q) {
rv = -ENOMEM;
goto out_cleanup_queues;
@@ -1758,8 +1808,8 @@ static int null_add_dev(struct nullb_device *dev)
}
nullb->q->queuedata = nullb;
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, nullb->q);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, nullb->q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, nullb->q);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, nullb->q);
mutex_lock(&lock);
nullb->index = ida_simple_get(&nullb_indexes, 0, 0, GFP_KERNEL);
@@ -1802,10 +1852,6 @@ static int __init null_init(void)
struct nullb *nullb;
struct nullb_device *dev;
- /* check for nullb_page.bitmap */
- if (sizeof(unsigned long) * 8 - 2 < (PAGE_SIZE >> SECTOR_SHIFT))
- return -EINVAL;
-
if (g_bs > PAGE_SIZE) {
pr_warn("null_blk: invalid block size\n");
pr_warn("null_blk: defaults block size to %lu\n", PAGE_SIZE);
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index 7b8c6368beb7..a026211afb51 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -230,6 +230,8 @@ static int pcd_block_open(struct block_device *bdev, fmode_t mode)
struct pcd_unit *cd = bdev->bd_disk->private_data;
int ret;
+ check_disk_change(bdev);
+
mutex_lock(&pcd_mutex);
ret = cdrom_open(&cd->info, bdev, mode);
mutex_unlock(&pcd_mutex);
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 8e40da093766..8e8b04cc569a 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -32,6 +32,7 @@
#include <linux/ceph/osd_client.h>
#include <linux/ceph/mon_client.h>
#include <linux/ceph/cls_lock_client.h>
+#include <linux/ceph/striper.h>
#include <linux/ceph/decode.h>
#include <linux/parser.h>
#include <linux/bsearch.h>
@@ -51,15 +52,6 @@
#define RBD_DEBUG /* Activate rbd_assert() calls */
/*
- * The basic unit of block I/O is a sector. It is interpreted in a
- * number of contexts in Linux (blk, bio, genhd), but the default is
- * universally 512 bytes. These symbols are just slightly more
- * meaningful than the bare numbers they represent.
- */
-#define SECTOR_SHIFT 9
-#define SECTOR_SIZE (1ULL << SECTOR_SHIFT)
-
-/*
* Increment the given counter and return its updated value.
* If the counter is already 0 it will not be incremented.
* If the counter is already at its maximum value returns
@@ -209,95 +201,81 @@ struct rbd_client {
};
struct rbd_img_request;
-typedef void (*rbd_img_callback_t)(struct rbd_img_request *);
-
-#define BAD_WHICH U32_MAX /* Good which or bad which, which? */
-
-struct rbd_obj_request;
-typedef void (*rbd_obj_callback_t)(struct rbd_obj_request *);
enum obj_request_type {
- OBJ_REQUEST_NODATA, OBJ_REQUEST_BIO, OBJ_REQUEST_PAGES
+ OBJ_REQUEST_NODATA = 1,
+ OBJ_REQUEST_BIO, /* pointer into provided bio (list) */
+ OBJ_REQUEST_BVECS, /* pointer into provided bio_vec array */
+ OBJ_REQUEST_OWN_BVECS, /* private bio_vec array, doesn't own pages */
};
enum obj_operation_type {
+ OBJ_OP_READ = 1,
OBJ_OP_WRITE,
- OBJ_OP_READ,
OBJ_OP_DISCARD,
};
-enum obj_req_flags {
- OBJ_REQ_DONE, /* completion flag: not done = 0, done = 1 */
- OBJ_REQ_IMG_DATA, /* object usage: standalone = 0, image = 1 */
- OBJ_REQ_KNOWN, /* EXISTS flag valid: no = 0, yes = 1 */
- OBJ_REQ_EXISTS, /* target exists: no = 0, yes = 1 */
+/*
+ * Writes go through the following state machine to deal with
+ * layering:
+ *
+ * need copyup
+ * RBD_OBJ_WRITE_GUARD ---------------> RBD_OBJ_WRITE_COPYUP
+ * | ^ |
+ * v \------------------------------/
+ * done
+ * ^
+ * |
+ * RBD_OBJ_WRITE_FLAT
+ *
+ * Writes start in RBD_OBJ_WRITE_GUARD or _FLAT, depending on whether
+ * there is a parent or not.
+ */
+enum rbd_obj_write_state {
+ RBD_OBJ_WRITE_FLAT = 1,
+ RBD_OBJ_WRITE_GUARD,
+ RBD_OBJ_WRITE_COPYUP,
};
struct rbd_obj_request {
- u64 object_no;
- u64 offset; /* object start byte */
- u64 length; /* bytes from offset */
- unsigned long flags;
-
- /*
- * An object request associated with an image will have its
- * img_data flag set; a standalone object request will not.
- *
- * A standalone object request will have which == BAD_WHICH
- * and a null obj_request pointer.
- *
- * An object request initiated in support of a layered image
- * object (to check for its existence before a write) will
- * have which == BAD_WHICH and a non-null obj_request pointer.
- *
- * Finally, an object request for rbd image data will have
- * which != BAD_WHICH, and will have a non-null img_request
- * pointer. The value of which will be in the range
- * 0..(img_request->obj_request_count-1).
- */
+ struct ceph_object_extent ex;
union {
- struct rbd_obj_request *obj_request; /* STAT op */
- struct {
- struct rbd_img_request *img_request;
- u64 img_offset;
- /* links for img_request->obj_requests list */
- struct list_head links;
- };
+ bool tried_parent; /* for reads */
+ enum rbd_obj_write_state write_state; /* for writes */
};
- u32 which; /* posn image request list */
- enum obj_request_type type;
+ struct rbd_img_request *img_request;
+ struct ceph_file_extent *img_extents;
+ u32 num_img_extents;
+
union {
- struct bio *bio_list;
+ struct ceph_bio_iter bio_pos;
struct {
- struct page **pages;
- u32 page_count;
+ struct ceph_bvec_iter bvec_pos;
+ u32 bvec_count;
+ u32 bvec_idx;
};
};
- struct page **copyup_pages;
- u32 copyup_page_count;
+ struct bio_vec *copyup_bvecs;
+ u32 copyup_bvec_count;
struct ceph_osd_request *osd_req;
u64 xferred; /* bytes transferred */
int result;
- rbd_obj_callback_t callback;
-
struct kref kref;
};
enum img_req_flags {
- IMG_REQ_WRITE, /* I/O direction: read = 0, write = 1 */
IMG_REQ_CHILD, /* initiator: block = 0, child image = 1 */
IMG_REQ_LAYERED, /* ENOENT handling: normal = 0, layered = 1 */
- IMG_REQ_DISCARD, /* discard: normal = 0, discard request = 1 */
};
struct rbd_img_request {
struct rbd_device *rbd_dev;
- u64 offset; /* starting image byte offset */
- u64 length; /* byte count from offset */
+ enum obj_operation_type op_type;
+ enum obj_request_type data_type;
unsigned long flags;
union {
u64 snap_id; /* for reads */
@@ -307,26 +285,21 @@ struct rbd_img_request {
struct request *rq; /* block request */
struct rbd_obj_request *obj_request; /* obj req initiator */
};
- struct page **copyup_pages;
- u32 copyup_page_count;
- spinlock_t completion_lock;/* protects next_completion */
- u32 next_completion;
- rbd_img_callback_t callback;
+ spinlock_t completion_lock;
u64 xferred;/* aggregate bytes transferred */
int result; /* first nonzero obj_request result */
+ struct list_head object_extents; /* obj_req.ex structs */
u32 obj_request_count;
- struct list_head obj_requests; /* rbd_obj_request structs */
+ u32 pending_count;
struct kref kref;
};
#define for_each_obj_request(ireq, oreq) \
- list_for_each_entry(oreq, &(ireq)->obj_requests, links)
-#define for_each_obj_request_from(ireq, oreq) \
- list_for_each_entry_from(oreq, &(ireq)->obj_requests, links)
+ list_for_each_entry(oreq, &(ireq)->object_extents, ex.oe_item)
#define for_each_obj_request_safe(ireq, oreq, n) \
- list_for_each_entry_safe_reverse(oreq, n, &(ireq)->obj_requests, links)
+ list_for_each_entry_safe(oreq, n, &(ireq)->object_extents, ex.oe_item)
enum rbd_watch_state {
RBD_WATCH_STATE_UNREGISTERED,
@@ -442,8 +415,6 @@ static DEFINE_SPINLOCK(rbd_client_list_lock);
static struct kmem_cache *rbd_img_request_cache;
static struct kmem_cache *rbd_obj_request_cache;
-static struct bio_set *rbd_bio_clone;
-
static int rbd_major;
static DEFINE_IDA(rbd_dev_id_ida);
@@ -456,8 +427,6 @@ static bool single_major = true;
module_param(single_major, bool, S_IRUGO);
MODULE_PARM_DESC(single_major, "Use a single major number for all rbd devices (default: true)");
-static int rbd_img_request_submit(struct rbd_img_request *img_request);
-
static ssize_t rbd_add(struct bus_type *bus, const char *buf,
size_t count);
static ssize_t rbd_remove(struct bus_type *bus, const char *buf,
@@ -467,7 +436,6 @@ static ssize_t rbd_add_single_major(struct bus_type *bus, const char *buf,
static ssize_t rbd_remove_single_major(struct bus_type *bus, const char *buf,
size_t count);
static int rbd_dev_image_probe(struct rbd_device *rbd_dev, int depth);
-static void rbd_spec_put(struct rbd_spec *spec);
static int rbd_dev_id_to_minor(int dev_id)
{
@@ -586,9 +554,6 @@ void rbd_warn(struct rbd_device *rbd_dev, const char *fmt, ...)
# define rbd_assert(expr) ((void) 0)
#endif /* !RBD_DEBUG */
-static void rbd_osd_copyup_callback(struct rbd_obj_request *obj_request);
-static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request);
-static void rbd_img_parent_read(struct rbd_obj_request *obj_request);
static void rbd_dev_remove_parent(struct rbd_device *rbd_dev);
static int rbd_dev_refresh(struct rbd_device *rbd_dev);
@@ -767,6 +732,7 @@ static struct rbd_client *rbd_client_find(struct ceph_options *ceph_opts)
*/
enum {
Opt_queue_depth,
+ Opt_lock_timeout,
Opt_last_int,
/* int args above */
Opt_last_string,
@@ -775,11 +741,13 @@ enum {
Opt_read_write,
Opt_lock_on_read,
Opt_exclusive,
+ Opt_notrim,
Opt_err
};
static match_table_t rbd_opts_tokens = {
{Opt_queue_depth, "queue_depth=%d"},
+ {Opt_lock_timeout, "lock_timeout=%d"},
/* int args above */
/* string args above */
{Opt_read_only, "read_only"},
@@ -788,20 +756,25 @@ static match_table_t rbd_opts_tokens = {
{Opt_read_write, "rw"}, /* Alternate spelling */
{Opt_lock_on_read, "lock_on_read"},
{Opt_exclusive, "exclusive"},
+ {Opt_notrim, "notrim"},
{Opt_err, NULL}
};
struct rbd_options {
int queue_depth;
+ unsigned long lock_timeout;
bool read_only;
bool lock_on_read;
bool exclusive;
+ bool trim;
};
#define RBD_QUEUE_DEPTH_DEFAULT BLKDEV_MAX_RQ
+#define RBD_LOCK_TIMEOUT_DEFAULT 0 /* no timeout */
#define RBD_READ_ONLY_DEFAULT false
#define RBD_LOCK_ON_READ_DEFAULT false
#define RBD_EXCLUSIVE_DEFAULT false
+#define RBD_TRIM_DEFAULT true
static int parse_rbd_opts_token(char *c, void *private)
{
@@ -831,6 +804,14 @@ static int parse_rbd_opts_token(char *c, void *private)
}
rbd_opts->queue_depth = intval;
break;
+ case Opt_lock_timeout:
+ /* 0 is "wait forever" (i.e. infinite timeout) */
+ if (intval < 0 || intval > INT_MAX / 1000) {
+ pr_err("lock_timeout out of range\n");
+ return -EINVAL;
+ }
+ rbd_opts->lock_timeout = msecs_to_jiffies(intval * 1000);
+ break;
case Opt_read_only:
rbd_opts->read_only = true;
break;
@@ -843,6 +824,9 @@ static int parse_rbd_opts_token(char *c, void *private)
case Opt_exclusive:
rbd_opts->exclusive = true;
break;
+ case Opt_notrim:
+ rbd_opts->trim = false;
+ break;
default:
/* libceph prints "bad option" msg */
return -EINVAL;
@@ -866,26 +850,6 @@ static char* obj_op_name(enum obj_operation_type op_type)
}
/*
- * Get a ceph client with specific addr and configuration, if one does
- * not exist create it. Either way, ceph_opts is consumed by this
- * function.
- */
-static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts)
-{
- struct rbd_client *rbdc;
-
- mutex_lock_nested(&client_mutex, SINGLE_DEPTH_NESTING);
- rbdc = rbd_client_find(ceph_opts);
- if (rbdc) /* using an existing client */
- ceph_destroy_options(ceph_opts);
- else
- rbdc = rbd_client_create(ceph_opts);
- mutex_unlock(&client_mutex);
-
- return rbdc;
-}
-
-/*
* Destroy ceph client
*
* Caller must hold rbd_client_list_lock.
@@ -913,6 +877,56 @@ static void rbd_put_client(struct rbd_client *rbdc)
kref_put(&rbdc->kref, rbd_client_release);
}
+static int wait_for_latest_osdmap(struct ceph_client *client)
+{
+ u64 newest_epoch;
+ int ret;
+
+ ret = ceph_monc_get_version(&client->monc, "osdmap", &newest_epoch);
+ if (ret)
+ return ret;
+
+ if (client->osdc.osdmap->epoch >= newest_epoch)
+ return 0;
+
+ ceph_osdc_maybe_request_map(&client->osdc);
+ return ceph_monc_wait_osdmap(&client->monc, newest_epoch,
+ client->options->mount_timeout);
+}
+
+/*
+ * Get a ceph client with specific addr and configuration, if one does
+ * not exist create it. Either way, ceph_opts is consumed by this
+ * function.
+ */
+static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts)
+{
+ struct rbd_client *rbdc;
+ int ret;
+
+ mutex_lock_nested(&client_mutex, SINGLE_DEPTH_NESTING);
+ rbdc = rbd_client_find(ceph_opts);
+ if (rbdc) {
+ ceph_destroy_options(ceph_opts);
+
+ /*
+ * Using an existing client. Make sure ->pg_pools is up to
+ * date before we look up the pool id in do_rbd_add().
+ */
+ ret = wait_for_latest_osdmap(rbdc->client);
+ if (ret) {
+ rbd_warn(NULL, "failed to get latest osdmap: %d", ret);
+ rbd_put_client(rbdc);
+ rbdc = ERR_PTR(ret);
+ }
+ } else {
+ rbdc = rbd_client_create(ceph_opts);
+ }
+ mutex_unlock(&client_mutex);
+
+ return rbdc;
+}
+
static bool rbd_image_format_valid(u32 image_format)
{
return image_format == 1 || image_format == 2;
@@ -1232,272 +1246,59 @@ static void rbd_dev_mapping_clear(struct rbd_device *rbd_dev)
rbd_dev->mapping.features = 0;
}
-static u64 rbd_segment_offset(struct rbd_device *rbd_dev, u64 offset)
-{
- u64 segment_size = rbd_obj_bytes(&rbd_dev->header);
-
- return offset & (segment_size - 1);
-}
-
-static u64 rbd_segment_length(struct rbd_device *rbd_dev,
- u64 offset, u64 length)
-{
- u64 segment_size = rbd_obj_bytes(&rbd_dev->header);
-
- offset &= segment_size - 1;
-
- rbd_assert(length <= U64_MAX - offset);
- if (offset + length > segment_size)
- length = segment_size - offset;
-
- return length;
-}
-
-/*
- * bio helpers
- */
-
-static void bio_chain_put(struct bio *chain)
-{
- struct bio *tmp;
-
- while (chain) {
- tmp = chain;
- chain = chain->bi_next;
- bio_put(tmp);
- }
-}
-
-/*
- * zeros a bio chain, starting at specific offset
- */
-static void zero_bio_chain(struct bio *chain, int start_ofs)
+static void zero_bvec(struct bio_vec *bv)
{
- struct bio_vec bv;
- struct bvec_iter iter;
- unsigned long flags;
void *buf;
- int pos = 0;
-
- while (chain) {
- bio_for_each_segment(bv, chain, iter) {
- if (pos + bv.bv_len > start_ofs) {
- int remainder = max(start_ofs - pos, 0);
- buf = bvec_kmap_irq(&bv, &flags);
- memset(buf + remainder, 0,
- bv.bv_len - remainder);
- flush_dcache_page(bv.bv_page);
- bvec_kunmap_irq(buf, &flags);
- }
- pos += bv.bv_len;
- }
+ unsigned long flags;
- chain = chain->bi_next;
- }
+ buf = bvec_kmap_irq(bv, &flags);
+ memset(buf, 0, bv->bv_len);
+ flush_dcache_page(bv->bv_page);
+ bvec_kunmap_irq(buf, &flags);
}
-/*
- * similar to zero_bio_chain(), zeros data defined by a page array,
- * starting at the given byte offset from the start of the array and
- * continuing up to the given end offset. The pages array is
- * assumed to be big enough to hold all bytes up to the end.
- */
-static void zero_pages(struct page **pages, u64 offset, u64 end)
+static void zero_bios(struct ceph_bio_iter *bio_pos, u32 off, u32 bytes)
{
- struct page **page = &pages[offset >> PAGE_SHIFT];
-
- rbd_assert(end > offset);
- rbd_assert(end - offset <= (u64)SIZE_MAX);
- while (offset < end) {
- size_t page_offset;
- size_t length;
- unsigned long flags;
- void *kaddr;
+ struct ceph_bio_iter it = *bio_pos;
- page_offset = offset & ~PAGE_MASK;
- length = min_t(size_t, PAGE_SIZE - page_offset, end - offset);
- local_irq_save(flags);
- kaddr = kmap_atomic(*page);
- memset(kaddr + page_offset, 0, length);
- flush_dcache_page(*page);
- kunmap_atomic(kaddr);
- local_irq_restore(flags);
-
- offset += length;
- page++;
- }
+ ceph_bio_iter_advance(&it, off);
+ ceph_bio_iter_advance_step(&it, bytes, ({
+ zero_bvec(&bv);
+ }));
}
-/*
- * Clone a portion of a bio, starting at the given byte offset
- * and continuing for the number of bytes indicated.
- */
-static struct bio *bio_clone_range(struct bio *bio_src,
- unsigned int offset,
- unsigned int len,
- gfp_t gfpmask)
+static void zero_bvecs(struct ceph_bvec_iter *bvec_pos, u32 off, u32 bytes)
{
- struct bio *bio;
-
- bio = bio_clone_fast(bio_src, gfpmask, rbd_bio_clone);
- if (!bio)
- return NULL; /* ENOMEM */
-
- bio_advance(bio, offset);
- bio->bi_iter.bi_size = len;
+ struct ceph_bvec_iter it = *bvec_pos;
- return bio;
+ ceph_bvec_iter_advance(&it, off);
+ ceph_bvec_iter_advance_step(&it, bytes, ({
+ zero_bvec(&bv);
+ }));
}
/*
- * Clone a portion of a bio chain, starting at the given byte offset
- * into the first bio in the source chain and continuing for the
- * number of bytes indicated. The result is another bio chain of
- * exactly the given length, or a null pointer on error.
+ * Zero a range in @obj_req data buffer defined by a bio (list) or
+ * (private) bio_vec array.
*
- * The bio_src and offset parameters are both in-out. On entry they
- * refer to the first source bio and the offset into that bio where
- * the start of data to be cloned is located.
- *
- * On return, bio_src is updated to refer to the bio in the source
- * chain that contains first un-cloned byte, and *offset will
- * contain the offset of that byte within that bio.
+ * @off is relative to the start of the data buffer.
*/
-static struct bio *bio_chain_clone_range(struct bio **bio_src,
- unsigned int *offset,
- unsigned int len,
- gfp_t gfpmask)
+static void rbd_obj_zero_range(struct rbd_obj_request *obj_req, u32 off,
+ u32 bytes)
{
- struct bio *bi = *bio_src;
- unsigned int off = *offset;
- struct bio *chain = NULL;
- struct bio **end;
-
- /* Build up a chain of clone bios up to the limit */
-
- if (!bi || off >= bi->bi_iter.bi_size || !len)
- return NULL; /* Nothing to clone */
-
- end = &chain;
- while (len) {
- unsigned int bi_size;
- struct bio *bio;
-
- if (!bi) {
- rbd_warn(NULL, "bio_chain exhausted with %u left", len);
- goto out_err; /* EINVAL; ran out of bio's */
- }
- bi_size = min_t(unsigned int, bi->bi_iter.bi_size - off, len);
- bio = bio_clone_range(bi, off, bi_size, gfpmask);
- if (!bio)
- goto out_err; /* ENOMEM */
-
- *end = bio;
- end = &bio->bi_next;
-
- off += bi_size;
- if (off == bi->bi_iter.bi_size) {
- bi = bi->bi_next;
- off = 0;
- }
- len -= bi_size;
- }
- *bio_src = bi;
- *offset = off;
-
- return chain;
-out_err:
- bio_chain_put(chain);
-
- return NULL;
-}
-
-/*
- * The default/initial value for all object request flags is 0. For
- * each flag, once its value is set to 1 it is never reset to 0
- * again.
- */
-static void obj_request_img_data_set(struct rbd_obj_request *obj_request)
-{
- if (test_and_set_bit(OBJ_REQ_IMG_DATA, &obj_request->flags)) {
- struct rbd_device *rbd_dev;
-
- rbd_dev = obj_request->img_request->rbd_dev;
- rbd_warn(rbd_dev, "obj_request %p already marked img_data",
- obj_request);
- }
-}
-
-static bool obj_request_img_data_test(struct rbd_obj_request *obj_request)
-{
- smp_mb();
- return test_bit(OBJ_REQ_IMG_DATA, &obj_request->flags) != 0;
-}
-
-static void obj_request_done_set(struct rbd_obj_request *obj_request)
-{
- if (test_and_set_bit(OBJ_REQ_DONE, &obj_request->flags)) {
- struct rbd_device *rbd_dev = NULL;
-
- if (obj_request_img_data_test(obj_request))
- rbd_dev = obj_request->img_request->rbd_dev;
- rbd_warn(rbd_dev, "obj_request %p already marked done",
- obj_request);
+ switch (obj_req->img_request->data_type) {
+ case OBJ_REQUEST_BIO:
+ zero_bios(&obj_req->bio_pos, off, bytes);
+ break;
+ case OBJ_REQUEST_BVECS:
+ case OBJ_REQUEST_OWN_BVECS:
+ zero_bvecs(&obj_req->bvec_pos, off, bytes);
+ break;
+ default:
+ rbd_assert(0);
}
}
-static bool obj_request_done_test(struct rbd_obj_request *obj_request)
-{
- smp_mb();
- return test_bit(OBJ_REQ_DONE, &obj_request->flags) != 0;
-}
-
-/*
- * This sets the KNOWN flag after (possibly) setting the EXISTS
- * flag. The latter is set based on the "exists" value provided.
- *
- * Note that for our purposes once an object exists it never goes
- * away again. It's possible that the response from two existence
- * checks are separated by the creation of the target object, and
- * the first ("doesn't exist") response arrives *after* the second
- * ("does exist"). In that case we ignore the second one.
- */
-static void obj_request_existence_set(struct rbd_obj_request *obj_request,
- bool exists)
-{
- if (exists)
- set_bit(OBJ_REQ_EXISTS, &obj_request->flags);
- set_bit(OBJ_REQ_KNOWN, &obj_request->flags);
- smp_mb();
-}
-
-static bool obj_request_known_test(struct rbd_obj_request *obj_request)
-{
- smp_mb();
- return test_bit(OBJ_REQ_KNOWN, &obj_request->flags) != 0;
-}
-
-static bool obj_request_exists_test(struct rbd_obj_request *obj_request)
-{
- smp_mb();
- return test_bit(OBJ_REQ_EXISTS, &obj_request->flags) != 0;
-}
-
-static bool obj_request_overlaps_parent(struct rbd_obj_request *obj_request)
-{
- struct rbd_device *rbd_dev = obj_request->img_request->rbd_dev;
-
- return obj_request->img_offset <
- round_up(rbd_dev->parent_overlap, rbd_obj_bytes(&rbd_dev->header));
-}
-
-static void rbd_obj_request_get(struct rbd_obj_request *obj_request)
-{
- dout("%s: obj %p (was %d)\n", __func__, obj_request,
- kref_read(&obj_request->kref));
- kref_get(&obj_request->kref);
-}
-
static void rbd_obj_request_destroy(struct kref *kref);
static void rbd_obj_request_put(struct rbd_obj_request *obj_request)
{
@@ -1514,18 +1315,13 @@ static void rbd_img_request_get(struct rbd_img_request *img_request)
kref_get(&img_request->kref);
}
-static bool img_request_child_test(struct rbd_img_request *img_request);
-static void rbd_parent_request_destroy(struct kref *kref);
static void rbd_img_request_destroy(struct kref *kref);
static void rbd_img_request_put(struct rbd_img_request *img_request)
{
rbd_assert(img_request != NULL);
dout("%s: img %p (was %d)\n", __func__, img_request,
kref_read(&img_request->kref));
- if (img_request_child_test(img_request))
- kref_put(&img_request->kref, rbd_parent_request_destroy);
- else
- kref_put(&img_request->kref, rbd_img_request_destroy);
+ kref_put(&img_request->kref, rbd_img_request_destroy);
}
static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request,
@@ -1535,139 +1331,37 @@ static inline void rbd_img_obj_request_add(struct rbd_img_request *img_request,
/* Image request now owns object's original reference */
obj_request->img_request = img_request;
- obj_request->which = img_request->obj_request_count;
- rbd_assert(!obj_request_img_data_test(obj_request));
- obj_request_img_data_set(obj_request);
- rbd_assert(obj_request->which != BAD_WHICH);
img_request->obj_request_count++;
- list_add_tail(&obj_request->links, &img_request->obj_requests);
- dout("%s: img %p obj %p w=%u\n", __func__, img_request, obj_request,
- obj_request->which);
+ img_request->pending_count++;
+ dout("%s: img %p obj %p\n", __func__, img_request, obj_request);
}
static inline void rbd_img_obj_request_del(struct rbd_img_request *img_request,
struct rbd_obj_request *obj_request)
{
- rbd_assert(obj_request->which != BAD_WHICH);
-
- dout("%s: img %p obj %p w=%u\n", __func__, img_request, obj_request,
- obj_request->which);
- list_del(&obj_request->links);
+ dout("%s: img %p obj %p\n", __func__, img_request, obj_request);
+ list_del(&obj_request->ex.oe_item);
rbd_assert(img_request->obj_request_count > 0);
img_request->obj_request_count--;
- rbd_assert(obj_request->which == img_request->obj_request_count);
- obj_request->which = BAD_WHICH;
- rbd_assert(obj_request_img_data_test(obj_request));
rbd_assert(obj_request->img_request == img_request);
- obj_request->img_request = NULL;
- obj_request->callback = NULL;
rbd_obj_request_put(obj_request);
}
-static bool obj_request_type_valid(enum obj_request_type type)
-{
- switch (type) {
- case OBJ_REQUEST_NODATA:
- case OBJ_REQUEST_BIO:
- case OBJ_REQUEST_PAGES:
- return true;
- default:
- return false;
- }
-}
-
-static void rbd_img_obj_callback(struct rbd_obj_request *obj_request);
-
static void rbd_obj_request_submit(struct rbd_obj_request *obj_request)
{
struct ceph_osd_request *osd_req = obj_request->osd_req;
dout("%s %p object_no %016llx %llu~%llu osd_req %p\n", __func__,
- obj_request, obj_request->object_no, obj_request->offset,
- obj_request->length, osd_req);
- if (obj_request_img_data_test(obj_request)) {
- WARN_ON(obj_request->callback != rbd_img_obj_callback);
- rbd_img_request_get(obj_request->img_request);
- }
+ obj_request, obj_request->ex.oe_objno, obj_request->ex.oe_off,
+ obj_request->ex.oe_len, osd_req);
ceph_osdc_start_request(osd_req->r_osdc, osd_req, false);
}
-static void rbd_img_request_complete(struct rbd_img_request *img_request)
-{
-
- dout("%s: img %p\n", __func__, img_request);
-
- /*
- * If no error occurred, compute the aggregate transfer
- * count for the image request. We could instead use
- * atomic64_cmpxchg() to update it as each object request
- * completes; not clear which way is better off hand.
- */
- if (!img_request->result) {
- struct rbd_obj_request *obj_request;
- u64 xferred = 0;
-
- for_each_obj_request(img_request, obj_request)
- xferred += obj_request->xferred;
- img_request->xferred = xferred;
- }
-
- if (img_request->callback)
- img_request->callback(img_request);
- else
- rbd_img_request_put(img_request);
-}
-
/*
* The default/initial value for all image request flags is 0. Each
* is conditionally set to 1 at image request initialization time
* and currently never change thereafter.
*/
-static void img_request_write_set(struct rbd_img_request *img_request)
-{
- set_bit(IMG_REQ_WRITE, &img_request->flags);
- smp_mb();
-}
-
-static bool img_request_write_test(struct rbd_img_request *img_request)
-{
- smp_mb();
- return test_bit(IMG_REQ_WRITE, &img_request->flags) != 0;
-}
-
-/*
- * Set the discard flag when the img_request is an discard request
- */
-static void img_request_discard_set(struct rbd_img_request *img_request)
-{
- set_bit(IMG_REQ_DISCARD, &img_request->flags);
- smp_mb();
-}
-
-static bool img_request_discard_test(struct rbd_img_request *img_request)
-{
- smp_mb();
- return test_bit(IMG_REQ_DISCARD, &img_request->flags) != 0;
-}
-
-static void img_request_child_set(struct rbd_img_request *img_request)
-{
- set_bit(IMG_REQ_CHILD, &img_request->flags);
- smp_mb();
-}
-
-static void img_request_child_clear(struct rbd_img_request *img_request)
-{
- clear_bit(IMG_REQ_CHILD, &img_request->flags);
- smp_mb();
-}
-
-static bool img_request_child_test(struct rbd_img_request *img_request)
-{
- smp_mb();
- return test_bit(IMG_REQ_CHILD, &img_request->flags) != 0;
-}
-
static void img_request_layered_set(struct rbd_img_request *img_request)
{
set_bit(IMG_REQ_LAYERED, &img_request->flags);
@@ -1686,209 +1380,70 @@ static bool img_request_layered_test(struct rbd_img_request *img_request)
return test_bit(IMG_REQ_LAYERED, &img_request->flags) != 0;
}
-static enum obj_operation_type
-rbd_img_request_op_type(struct rbd_img_request *img_request)
-{
- if (img_request_write_test(img_request))
- return OBJ_OP_WRITE;
- else if (img_request_discard_test(img_request))
- return OBJ_OP_DISCARD;
- else
- return OBJ_OP_READ;
-}
-
-static void
-rbd_img_obj_request_read_callback(struct rbd_obj_request *obj_request)
-{
- u64 xferred = obj_request->xferred;
- u64 length = obj_request->length;
-
- dout("%s: obj %p img %p result %d %llu/%llu\n", __func__,
- obj_request, obj_request->img_request, obj_request->result,
- xferred, length);
- /*
- * ENOENT means a hole in the image. We zero-fill the entire
- * length of the request. A short read also implies zero-fill
- * to the end of the request. An error requires the whole
- * length of the request to be reported finished with an error
- * to the block layer. In each case we update the xferred
- * count to indicate the whole request was satisfied.
- */
- rbd_assert(obj_request->type != OBJ_REQUEST_NODATA);
- if (obj_request->result == -ENOENT) {
- if (obj_request->type == OBJ_REQUEST_BIO)
- zero_bio_chain(obj_request->bio_list, 0);
- else
- zero_pages(obj_request->pages, 0, length);
- obj_request->result = 0;
- } else if (xferred < length && !obj_request->result) {
- if (obj_request->type == OBJ_REQUEST_BIO)
- zero_bio_chain(obj_request->bio_list, xferred);
- else
- zero_pages(obj_request->pages, xferred, length);
- }
- obj_request->xferred = length;
- obj_request_done_set(obj_request);
-}
-
-static void rbd_obj_request_complete(struct rbd_obj_request *obj_request)
+static bool rbd_obj_is_entire(struct rbd_obj_request *obj_req)
{
- dout("%s: obj %p cb %p\n", __func__, obj_request,
- obj_request->callback);
- obj_request->callback(obj_request);
-}
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
-static void rbd_obj_request_error(struct rbd_obj_request *obj_request, int err)
-{
- obj_request->result = err;
- obj_request->xferred = 0;
- /*
- * kludge - mirror rbd_obj_request_submit() to match a put in
- * rbd_img_obj_callback()
- */
- if (obj_request_img_data_test(obj_request)) {
- WARN_ON(obj_request->callback != rbd_img_obj_callback);
- rbd_img_request_get(obj_request->img_request);
- }
- obj_request_done_set(obj_request);
- rbd_obj_request_complete(obj_request);
+ return !obj_req->ex.oe_off &&
+ obj_req->ex.oe_len == rbd_dev->layout.object_size;
}
-static void rbd_osd_read_callback(struct rbd_obj_request *obj_request)
+static bool rbd_obj_is_tail(struct rbd_obj_request *obj_req)
{
- struct rbd_img_request *img_request = NULL;
- struct rbd_device *rbd_dev = NULL;
- bool layered = false;
-
- if (obj_request_img_data_test(obj_request)) {
- img_request = obj_request->img_request;
- layered = img_request && img_request_layered_test(img_request);
- rbd_dev = img_request->rbd_dev;
- }
-
- dout("%s: obj %p img %p result %d %llu/%llu\n", __func__,
- obj_request, img_request, obj_request->result,
- obj_request->xferred, obj_request->length);
- if (layered && obj_request->result == -ENOENT &&
- obj_request->img_offset < rbd_dev->parent_overlap)
- rbd_img_parent_read(obj_request);
- else if (img_request)
- rbd_img_obj_request_read_callback(obj_request);
- else
- obj_request_done_set(obj_request);
-}
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
-static void rbd_osd_write_callback(struct rbd_obj_request *obj_request)
-{
- dout("%s: obj %p result %d %llu\n", __func__, obj_request,
- obj_request->result, obj_request->length);
- /*
- * There is no such thing as a successful short write. Set
- * it to our originally-requested length.
- */
- obj_request->xferred = obj_request->length;
- obj_request_done_set(obj_request);
+ return obj_req->ex.oe_off + obj_req->ex.oe_len ==
+ rbd_dev->layout.object_size;
}
-static void rbd_osd_discard_callback(struct rbd_obj_request *obj_request)
+static u64 rbd_obj_img_extents_bytes(struct rbd_obj_request *obj_req)
{
- dout("%s: obj %p result %d %llu\n", __func__, obj_request,
- obj_request->result, obj_request->length);
- /*
- * There is no such thing as a successful short discard. Set
- * it to our originally-requested length.
- */
- obj_request->xferred = obj_request->length;
- /* discarding a non-existent object is not a problem */
- if (obj_request->result == -ENOENT)
- obj_request->result = 0;
- obj_request_done_set(obj_request);
+ return ceph_file_extents_bytes(obj_req->img_extents,
+ obj_req->num_img_extents);
}
-/*
- * For a simple stat call there's nothing to do. We'll do more if
- * this is part of a write sequence for a layered image.
- */
-static void rbd_osd_stat_callback(struct rbd_obj_request *obj_request)
+static bool rbd_img_is_write(struct rbd_img_request *img_req)
{
- dout("%s: obj %p\n", __func__, obj_request);
- obj_request_done_set(obj_request);
+ switch (img_req->op_type) {
+ case OBJ_OP_READ:
+ return false;
+ case OBJ_OP_WRITE:
+ case OBJ_OP_DISCARD:
+ return true;
+ default:
+ BUG();
+ }
}
-static void rbd_osd_call_callback(struct rbd_obj_request *obj_request)
-{
- dout("%s: obj %p\n", __func__, obj_request);
-
- if (obj_request_img_data_test(obj_request))
- rbd_osd_copyup_callback(obj_request);
- else
- obj_request_done_set(obj_request);
-}
+static void rbd_obj_handle_request(struct rbd_obj_request *obj_req);
static void rbd_osd_req_callback(struct ceph_osd_request *osd_req)
{
- struct rbd_obj_request *obj_request = osd_req->r_priv;
- u16 opcode;
-
- dout("%s: osd_req %p\n", __func__, osd_req);
- rbd_assert(osd_req == obj_request->osd_req);
- if (obj_request_img_data_test(obj_request)) {
- rbd_assert(obj_request->img_request);
- rbd_assert(obj_request->which != BAD_WHICH);
- } else {
- rbd_assert(obj_request->which == BAD_WHICH);
- }
-
- if (osd_req->r_result < 0)
- obj_request->result = osd_req->r_result;
+ struct rbd_obj_request *obj_req = osd_req->r_priv;
- /*
- * We support a 64-bit length, but ultimately it has to be
- * passed to the block layer, which just supports a 32-bit
- * length field.
- */
- obj_request->xferred = osd_req->r_ops[0].outdata_len;
- rbd_assert(obj_request->xferred < (u64)UINT_MAX);
+ dout("%s osd_req %p result %d for obj_req %p\n", __func__, osd_req,
+ osd_req->r_result, obj_req);
+ rbd_assert(osd_req == obj_req->osd_req);
- opcode = osd_req->r_ops[0].op;
- switch (opcode) {
- case CEPH_OSD_OP_READ:
- rbd_osd_read_callback(obj_request);
- break;
- case CEPH_OSD_OP_SETALLOCHINT:
- rbd_assert(osd_req->r_ops[1].op == CEPH_OSD_OP_WRITE ||
- osd_req->r_ops[1].op == CEPH_OSD_OP_WRITEFULL);
- /* fall through */
- case CEPH_OSD_OP_WRITE:
- case CEPH_OSD_OP_WRITEFULL:
- rbd_osd_write_callback(obj_request);
- break;
- case CEPH_OSD_OP_STAT:
- rbd_osd_stat_callback(obj_request);
- break;
- case CEPH_OSD_OP_DELETE:
- case CEPH_OSD_OP_TRUNCATE:
- case CEPH_OSD_OP_ZERO:
- rbd_osd_discard_callback(obj_request);
- break;
- case CEPH_OSD_OP_CALL:
- rbd_osd_call_callback(obj_request);
- break;
- default:
- rbd_warn(NULL, "unexpected OSD op: object_no %016llx opcode %d",
- obj_request->object_no, opcode);
- break;
- }
+ obj_req->result = osd_req->r_result < 0 ? osd_req->r_result : 0;
+ if (!obj_req->result && !rbd_img_is_write(obj_req->img_request))
+ obj_req->xferred = osd_req->r_result;
+ else
+ /*
+ * Writes aren't allowed to return a data payload. In some
+ * guarded write cases (e.g. stat + zero on an empty object)
+ * a stat response makes it through, but we don't care.
+ */
+ obj_req->xferred = 0;
- if (obj_request_done_test(obj_request))
- rbd_obj_request_complete(obj_request);
+ rbd_obj_handle_request(obj_req);
}
static void rbd_osd_req_format_read(struct rbd_obj_request *obj_request)
{
struct ceph_osd_request *osd_req = obj_request->osd_req;
- rbd_assert(obj_request_img_data_test(obj_request));
+ osd_req->r_flags = CEPH_OSD_FLAG_READ;
osd_req->r_snapid = obj_request->img_request->snap_id;
}
@@ -1896,32 +1451,33 @@ static void rbd_osd_req_format_write(struct rbd_obj_request *obj_request)
{
struct ceph_osd_request *osd_req = obj_request->osd_req;
+ osd_req->r_flags = CEPH_OSD_FLAG_WRITE;
ktime_get_real_ts(&osd_req->r_mtime);
- osd_req->r_data_offset = obj_request->offset;
+ osd_req->r_data_offset = obj_request->ex.oe_off;
}
static struct ceph_osd_request *
-__rbd_osd_req_create(struct rbd_device *rbd_dev,
- struct ceph_snap_context *snapc,
- int num_ops, unsigned int flags,
- struct rbd_obj_request *obj_request)
+rbd_osd_req_create(struct rbd_obj_request *obj_req, unsigned int num_ops)
{
+ struct rbd_img_request *img_req = obj_req->img_request;
+ struct rbd_device *rbd_dev = img_req->rbd_dev;
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
struct ceph_osd_request *req;
const char *name_format = rbd_dev->image_format == 1 ?
RBD_V1_DATA_FORMAT : RBD_V2_DATA_FORMAT;
- req = ceph_osdc_alloc_request(osdc, snapc, num_ops, false, GFP_NOIO);
+ req = ceph_osdc_alloc_request(osdc,
+ (rbd_img_is_write(img_req) ? img_req->snapc : NULL),
+ num_ops, false, GFP_NOIO);
if (!req)
return NULL;
- req->r_flags = flags;
req->r_callback = rbd_osd_req_callback;
- req->r_priv = obj_request;
+ req->r_priv = obj_req;
req->r_base_oloc.pool = rbd_dev->layout.pool_id;
if (ceph_oid_aprintf(&req->r_base_oid, GFP_NOIO, name_format,
- rbd_dev->header.object_prefix, obj_request->object_no))
+ rbd_dev->header.object_prefix, obj_req->ex.oe_objno))
goto err_req;
if (ceph_osdc_alloc_messages(req, GFP_NOIO))
@@ -1934,83 +1490,20 @@ err_req:
return NULL;
}
-/*
- * Create an osd request. A read request has one osd op (read).
- * A write request has either one (watch) or two (hint+write) osd ops.
- * (All rbd data writes are prefixed with an allocation hint op, but
- * technically osd watch is a write request, hence this distinction.)
- */
-static struct ceph_osd_request *rbd_osd_req_create(
- struct rbd_device *rbd_dev,
- enum obj_operation_type op_type,
- unsigned int num_ops,
- struct rbd_obj_request *obj_request)
-{
- struct ceph_snap_context *snapc = NULL;
-
- if (obj_request_img_data_test(obj_request) &&
- (op_type == OBJ_OP_DISCARD || op_type == OBJ_OP_WRITE)) {
- struct rbd_img_request *img_request = obj_request->img_request;
- if (op_type == OBJ_OP_WRITE) {
- rbd_assert(img_request_write_test(img_request));
- } else {
- rbd_assert(img_request_discard_test(img_request));
- }
- snapc = img_request->snapc;
- }
-
- rbd_assert(num_ops == 1 || ((op_type == OBJ_OP_WRITE) && num_ops == 2));
-
- return __rbd_osd_req_create(rbd_dev, snapc, num_ops,
- (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD) ?
- CEPH_OSD_FLAG_WRITE : CEPH_OSD_FLAG_READ, obj_request);
-}
-
-/*
- * Create a copyup osd request based on the information in the object
- * request supplied. A copyup request has two or three osd ops, a
- * copyup method call, potentially a hint op, and a write or truncate
- * or zero op.
- */
-static struct ceph_osd_request *
-rbd_osd_req_create_copyup(struct rbd_obj_request *obj_request)
-{
- struct rbd_img_request *img_request;
- int num_osd_ops = 3;
-
- rbd_assert(obj_request_img_data_test(obj_request));
- img_request = obj_request->img_request;
- rbd_assert(img_request);
- rbd_assert(img_request_write_test(img_request) ||
- img_request_discard_test(img_request));
-
- if (img_request_discard_test(img_request))
- num_osd_ops = 2;
-
- return __rbd_osd_req_create(img_request->rbd_dev,
- img_request->snapc, num_osd_ops,
- CEPH_OSD_FLAG_WRITE, obj_request);
-}
-
static void rbd_osd_req_destroy(struct ceph_osd_request *osd_req)
{
ceph_osdc_put_request(osd_req);
}
-static struct rbd_obj_request *
-rbd_obj_request_create(enum obj_request_type type)
+static struct rbd_obj_request *rbd_obj_request_create(void)
{
struct rbd_obj_request *obj_request;
- rbd_assert(obj_request_type_valid(type));
-
obj_request = kmem_cache_zalloc(rbd_obj_request_cache, GFP_NOIO);
if (!obj_request)
return NULL;
- obj_request->which = BAD_WHICH;
- obj_request->type = type;
- INIT_LIST_HEAD(&obj_request->links);
+ ceph_object_extent_init(&obj_request->ex);
kref_init(&obj_request->kref);
dout("%s %p\n", __func__, obj_request);
@@ -2020,32 +1513,34 @@ rbd_obj_request_create(enum obj_request_type type)
static void rbd_obj_request_destroy(struct kref *kref)
{
struct rbd_obj_request *obj_request;
+ u32 i;
obj_request = container_of(kref, struct rbd_obj_request, kref);
dout("%s: obj %p\n", __func__, obj_request);
- rbd_assert(obj_request->img_request == NULL);
- rbd_assert(obj_request->which == BAD_WHICH);
-
if (obj_request->osd_req)
rbd_osd_req_destroy(obj_request->osd_req);
- rbd_assert(obj_request_type_valid(obj_request->type));
- switch (obj_request->type) {
+ switch (obj_request->img_request->data_type) {
case OBJ_REQUEST_NODATA:
- break; /* Nothing to do */
case OBJ_REQUEST_BIO:
- if (obj_request->bio_list)
- bio_chain_put(obj_request->bio_list);
- break;
- case OBJ_REQUEST_PAGES:
- /* img_data requests don't own their page array */
- if (obj_request->pages &&
- !obj_request_img_data_test(obj_request))
- ceph_release_page_vector(obj_request->pages,
- obj_request->page_count);
+ case OBJ_REQUEST_BVECS:
+ break; /* Nothing to do */
+ case OBJ_REQUEST_OWN_BVECS:
+ kfree(obj_request->bvec_pos.bvecs);
break;
+ default:
+ rbd_assert(0);
+ }
+
+ kfree(obj_request->img_extents);
+ if (obj_request->copyup_bvecs) {
+ for (i = 0; i < obj_request->copyup_bvec_count; i++) {
+ if (obj_request->copyup_bvecs[i].bv_page)
+ __free_page(obj_request->copyup_bvecs[i].bv_page);
+ }
+ kfree(obj_request->copyup_bvecs);
}
kmem_cache_free(rbd_obj_request_cache, obj_request);
@@ -2120,7 +1615,6 @@ static bool rbd_dev_parent_get(struct rbd_device *rbd_dev)
*/
static struct rbd_img_request *rbd_img_request_create(
struct rbd_device *rbd_dev,
- u64 offset, u64 length,
enum obj_operation_type op_type,
struct ceph_snap_context *snapc)
{
@@ -2131,27 +1625,21 @@ static struct rbd_img_request *rbd_img_request_create(
return NULL;
img_request->rbd_dev = rbd_dev;
- img_request->offset = offset;
- img_request->length = length;
- if (op_type == OBJ_OP_DISCARD) {
- img_request_discard_set(img_request);
- img_request->snapc = snapc;
- } else if (op_type == OBJ_OP_WRITE) {
- img_request_write_set(img_request);
- img_request->snapc = snapc;
- } else {
+ img_request->op_type = op_type;
+ if (!rbd_img_is_write(img_request))
img_request->snap_id = rbd_dev->spec->snap_id;
- }
+ else
+ img_request->snapc = snapc;
+
if (rbd_dev_parent_get(rbd_dev))
img_request_layered_set(img_request);
spin_lock_init(&img_request->completion_lock);
- INIT_LIST_HEAD(&img_request->obj_requests);
+ INIT_LIST_HEAD(&img_request->object_extents);
kref_init(&img_request->kref);
- dout("%s: rbd_dev %p %s %llu/%llu -> img %p\n", __func__, rbd_dev,
- obj_op_name(op_type), offset, length, img_request);
-
+ dout("%s: rbd_dev %p %s -> img %p\n", __func__, rbd_dev,
+ obj_op_name(op_type), img_request);
return img_request;
}
@@ -2174,829 +1662,934 @@ static void rbd_img_request_destroy(struct kref *kref)
rbd_dev_parent_put(img_request->rbd_dev);
}
- if (img_request_write_test(img_request) ||
- img_request_discard_test(img_request))
+ if (rbd_img_is_write(img_request))
ceph_put_snap_context(img_request->snapc);
kmem_cache_free(rbd_img_request_cache, img_request);
}
-static struct rbd_img_request *rbd_parent_request_create(
- struct rbd_obj_request *obj_request,
- u64 img_offset, u64 length)
+static void prune_extents(struct ceph_file_extent *img_extents,
+ u32 *num_img_extents, u64 overlap)
{
- struct rbd_img_request *parent_request;
- struct rbd_device *rbd_dev;
+ u32 cnt = *num_img_extents;
- rbd_assert(obj_request->img_request);
- rbd_dev = obj_request->img_request->rbd_dev;
+ /* drop extents completely beyond the overlap */
+ while (cnt && img_extents[cnt - 1].fe_off >= overlap)
+ cnt--;
- parent_request = rbd_img_request_create(rbd_dev->parent, img_offset,
- length, OBJ_OP_READ, NULL);
- if (!parent_request)
- return NULL;
+ if (cnt) {
+ struct ceph_file_extent *ex = &img_extents[cnt - 1];
- img_request_child_set(parent_request);
- rbd_obj_request_get(obj_request);
- parent_request->obj_request = obj_request;
+ /* trim final overlapping extent */
+ if (ex->fe_off + ex->fe_len > overlap)
+ ex->fe_len = overlap - ex->fe_off;
+ }
- return parent_request;
+ *num_img_extents = cnt;
}
-static void rbd_parent_request_destroy(struct kref *kref)
+/*
+ * Determine the byte range(s) covered by either just the object extent
+ * or the entire object in the parent image.
+ */
+static int rbd_obj_calc_img_extents(struct rbd_obj_request *obj_req,
+ bool entire)
{
- struct rbd_img_request *parent_request;
- struct rbd_obj_request *orig_request;
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
+ int ret;
- parent_request = container_of(kref, struct rbd_img_request, kref);
- orig_request = parent_request->obj_request;
+ if (!rbd_dev->parent_overlap)
+ return 0;
- parent_request->obj_request = NULL;
- rbd_obj_request_put(orig_request);
- img_request_child_clear(parent_request);
+ ret = ceph_extent_to_file(&rbd_dev->layout, obj_req->ex.oe_objno,
+ entire ? 0 : obj_req->ex.oe_off,
+ entire ? rbd_dev->layout.object_size :
+ obj_req->ex.oe_len,
+ &obj_req->img_extents,
+ &obj_req->num_img_extents);
+ if (ret)
+ return ret;
- rbd_img_request_destroy(kref);
+ prune_extents(obj_req->img_extents, &obj_req->num_img_extents,
+ rbd_dev->parent_overlap);
+ return 0;
}
-static bool rbd_img_obj_end_request(struct rbd_obj_request *obj_request)
+static void rbd_osd_req_setup_data(struct rbd_obj_request *obj_req, u32 which)
{
- struct rbd_img_request *img_request;
- unsigned int xferred;
- int result;
- bool more;
-
- rbd_assert(obj_request_img_data_test(obj_request));
- img_request = obj_request->img_request;
-
- rbd_assert(obj_request->xferred <= (u64)UINT_MAX);
- xferred = (unsigned int)obj_request->xferred;
- result = obj_request->result;
- if (result) {
- struct rbd_device *rbd_dev = img_request->rbd_dev;
- enum obj_operation_type op_type;
-
- if (img_request_discard_test(img_request))
- op_type = OBJ_OP_DISCARD;
- else if (img_request_write_test(img_request))
- op_type = OBJ_OP_WRITE;
- else
- op_type = OBJ_OP_READ;
-
- rbd_warn(rbd_dev, "%s %llx at %llx (%llx)",
- obj_op_name(op_type), obj_request->length,
- obj_request->img_offset, obj_request->offset);
- rbd_warn(rbd_dev, " result %d xferred %x",
- result, xferred);
- if (!img_request->result)
- img_request->result = result;
- /*
- * Need to end I/O on the entire obj_request worth of
- * bytes in case of error.
- */
- xferred = obj_request->length;
+ switch (obj_req->img_request->data_type) {
+ case OBJ_REQUEST_BIO:
+ osd_req_op_extent_osd_data_bio(obj_req->osd_req, which,
+ &obj_req->bio_pos,
+ obj_req->ex.oe_len);
+ break;
+ case OBJ_REQUEST_BVECS:
+ case OBJ_REQUEST_OWN_BVECS:
+ rbd_assert(obj_req->bvec_pos.iter.bi_size ==
+ obj_req->ex.oe_len);
+ rbd_assert(obj_req->bvec_idx == obj_req->bvec_count);
+ osd_req_op_extent_osd_data_bvec_pos(obj_req->osd_req, which,
+ &obj_req->bvec_pos);
+ break;
+ default:
+ rbd_assert(0);
}
+}
- if (img_request_child_test(img_request)) {
- rbd_assert(img_request->obj_request != NULL);
- more = obj_request->which < img_request->obj_request_count - 1;
- } else {
- blk_status_t status = errno_to_blk_status(result);
+static int rbd_obj_setup_read(struct rbd_obj_request *obj_req)
+{
+ obj_req->osd_req = rbd_osd_req_create(obj_req, 1);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
- rbd_assert(img_request->rq != NULL);
+ osd_req_op_extent_init(obj_req->osd_req, 0, CEPH_OSD_OP_READ,
+ obj_req->ex.oe_off, obj_req->ex.oe_len, 0, 0);
+ rbd_osd_req_setup_data(obj_req, 0);
- more = blk_update_request(img_request->rq, status, xferred);
- if (!more)
- __blk_mq_end_request(img_request->rq, status);
- }
+ rbd_osd_req_format_read(obj_req);
+ return 0;
+}
+
+static int __rbd_obj_setup_stat(struct rbd_obj_request *obj_req,
+ unsigned int which)
+{
+ struct page **pages;
- return more;
+ /*
+ * The response data for a STAT call consists of:
+ * le64 length;
+ * struct {
+ * le32 tv_sec;
+ * le32 tv_nsec;
+ * } mtime;
+ */
+ pages = ceph_alloc_page_vector(1, GFP_NOIO);
+ if (IS_ERR(pages))
+ return PTR_ERR(pages);
+
+ osd_req_op_init(obj_req->osd_req, which, CEPH_OSD_OP_STAT, 0);
+ osd_req_op_raw_data_in_pages(obj_req->osd_req, which, pages,
+ 8 + sizeof(struct ceph_timespec),
+ 0, false, true);
+ return 0;
}
-static void rbd_img_obj_callback(struct rbd_obj_request *obj_request)
+static void __rbd_obj_setup_write(struct rbd_obj_request *obj_req,
+ unsigned int which)
{
- struct rbd_img_request *img_request;
- u32 which = obj_request->which;
- bool more = true;
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
+ u16 opcode;
- rbd_assert(obj_request_img_data_test(obj_request));
- img_request = obj_request->img_request;
+ osd_req_op_alloc_hint_init(obj_req->osd_req, which++,
+ rbd_dev->layout.object_size,
+ rbd_dev->layout.object_size);
- dout("%s: img %p obj %p\n", __func__, img_request, obj_request);
- rbd_assert(img_request != NULL);
- rbd_assert(img_request->obj_request_count > 0);
- rbd_assert(which != BAD_WHICH);
- rbd_assert(which < img_request->obj_request_count);
+ if (rbd_obj_is_entire(obj_req))
+ opcode = CEPH_OSD_OP_WRITEFULL;
+ else
+ opcode = CEPH_OSD_OP_WRITE;
- spin_lock_irq(&img_request->completion_lock);
- if (which != img_request->next_completion)
- goto out;
+ osd_req_op_extent_init(obj_req->osd_req, which, opcode,
+ obj_req->ex.oe_off, obj_req->ex.oe_len, 0, 0);
+ rbd_osd_req_setup_data(obj_req, which++);
- for_each_obj_request_from(img_request, obj_request) {
- rbd_assert(more);
- rbd_assert(which < img_request->obj_request_count);
+ rbd_assert(which == obj_req->osd_req->r_num_ops);
+ rbd_osd_req_format_write(obj_req);
+}
- if (!obj_request_done_test(obj_request))
- break;
- more = rbd_img_obj_end_request(obj_request);
- which++;
+static int rbd_obj_setup_write(struct rbd_obj_request *obj_req)
+{
+ unsigned int num_osd_ops, which = 0;
+ int ret;
+
+ /* reverse map the entire object onto the parent */
+ ret = rbd_obj_calc_img_extents(obj_req, true);
+ if (ret)
+ return ret;
+
+ if (obj_req->num_img_extents) {
+ obj_req->write_state = RBD_OBJ_WRITE_GUARD;
+ num_osd_ops = 3; /* stat + setallochint + write/writefull */
+ } else {
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
+ num_osd_ops = 2; /* setallochint + write/writefull */
}
- rbd_assert(more ^ (which == img_request->obj_request_count));
- img_request->next_completion = which;
-out:
- spin_unlock_irq(&img_request->completion_lock);
- rbd_img_request_put(img_request);
+ obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
- if (!more)
- rbd_img_request_complete(img_request);
+ if (obj_req->num_img_extents) {
+ ret = __rbd_obj_setup_stat(obj_req, which++);
+ if (ret)
+ return ret;
+ }
+
+ __rbd_obj_setup_write(obj_req, which);
+ return 0;
}
-/*
- * Add individual osd ops to the given ceph_osd_request and prepare
- * them for submission. num_ops is the current number of
- * osd operations already to the object request.
- */
-static void rbd_img_obj_request_fill(struct rbd_obj_request *obj_request,
- struct ceph_osd_request *osd_request,
- enum obj_operation_type op_type,
- unsigned int num_ops)
-{
- struct rbd_img_request *img_request = obj_request->img_request;
- struct rbd_device *rbd_dev = img_request->rbd_dev;
- u64 object_size = rbd_obj_bytes(&rbd_dev->header);
- u64 offset = obj_request->offset;
- u64 length = obj_request->length;
- u64 img_end;
+static void __rbd_obj_setup_discard(struct rbd_obj_request *obj_req,
+ unsigned int which)
+{
u16 opcode;
- if (op_type == OBJ_OP_DISCARD) {
- if (!offset && length == object_size &&
- (!img_request_layered_test(img_request) ||
- !obj_request_overlaps_parent(obj_request))) {
- opcode = CEPH_OSD_OP_DELETE;
- } else if ((offset + length == object_size)) {
+ if (rbd_obj_is_entire(obj_req)) {
+ if (obj_req->num_img_extents) {
+ osd_req_op_init(obj_req->osd_req, which++,
+ CEPH_OSD_OP_CREATE, 0);
opcode = CEPH_OSD_OP_TRUNCATE;
} else {
- down_read(&rbd_dev->header_rwsem);
- img_end = rbd_dev->header.image_size;
- up_read(&rbd_dev->header_rwsem);
-
- if (obj_request->img_offset + length == img_end)
- opcode = CEPH_OSD_OP_TRUNCATE;
- else
- opcode = CEPH_OSD_OP_ZERO;
+ osd_req_op_init(obj_req->osd_req, which++,
+ CEPH_OSD_OP_DELETE, 0);
+ opcode = 0;
}
- } else if (op_type == OBJ_OP_WRITE) {
- if (!offset && length == object_size)
- opcode = CEPH_OSD_OP_WRITEFULL;
- else
- opcode = CEPH_OSD_OP_WRITE;
- osd_req_op_alloc_hint_init(osd_request, num_ops,
- object_size, object_size);
- num_ops++;
+ } else if (rbd_obj_is_tail(obj_req)) {
+ opcode = CEPH_OSD_OP_TRUNCATE;
} else {
- opcode = CEPH_OSD_OP_READ;
+ opcode = CEPH_OSD_OP_ZERO;
}
- if (opcode == CEPH_OSD_OP_DELETE)
- osd_req_op_init(osd_request, num_ops, opcode, 0);
- else
- osd_req_op_extent_init(osd_request, num_ops, opcode,
- offset, length, 0, 0);
-
- if (obj_request->type == OBJ_REQUEST_BIO)
- osd_req_op_extent_osd_data_bio(osd_request, num_ops,
- obj_request->bio_list, length);
- else if (obj_request->type == OBJ_REQUEST_PAGES)
- osd_req_op_extent_osd_data_pages(osd_request, num_ops,
- obj_request->pages, length,
- offset & ~PAGE_MASK, false, false);
-
- /* Discards are also writes */
- if (op_type == OBJ_OP_WRITE || op_type == OBJ_OP_DISCARD)
- rbd_osd_req_format_write(obj_request);
- else
- rbd_osd_req_format_read(obj_request);
+ if (opcode)
+ osd_req_op_extent_init(obj_req->osd_req, which++, opcode,
+ obj_req->ex.oe_off, obj_req->ex.oe_len,
+ 0, 0);
+
+ rbd_assert(which == obj_req->osd_req->r_num_ops);
+ rbd_osd_req_format_write(obj_req);
}
-/*
- * Split up an image request into one or more object requests, each
- * to a different object. The "type" parameter indicates whether
- * "data_desc" is the pointer to the head of a list of bio
- * structures, or the base of a page array. In either case this
- * function assumes data_desc describes memory sufficient to hold
- * all data described by the image request.
- */
-static int rbd_img_request_fill(struct rbd_img_request *img_request,
- enum obj_request_type type,
- void *data_desc)
+static int rbd_obj_setup_discard(struct rbd_obj_request *obj_req)
{
- struct rbd_device *rbd_dev = img_request->rbd_dev;
- struct rbd_obj_request *obj_request = NULL;
- struct rbd_obj_request *next_obj_request;
- struct bio *bio_list = NULL;
- unsigned int bio_offset = 0;
- struct page **pages = NULL;
- enum obj_operation_type op_type;
- u64 img_offset;
- u64 resid;
-
- dout("%s: img %p type %d data_desc %p\n", __func__, img_request,
- (int)type, data_desc);
+ unsigned int num_osd_ops, which = 0;
+ int ret;
- img_offset = img_request->offset;
- resid = img_request->length;
- rbd_assert(resid > 0);
- op_type = rbd_img_request_op_type(img_request);
+ /* reverse map the entire object onto the parent */
+ ret = rbd_obj_calc_img_extents(obj_req, true);
+ if (ret)
+ return ret;
- if (type == OBJ_REQUEST_BIO) {
- bio_list = data_desc;
- rbd_assert(img_offset ==
- bio_list->bi_iter.bi_sector << SECTOR_SHIFT);
- } else if (type == OBJ_REQUEST_PAGES) {
- pages = data_desc;
+ if (rbd_obj_is_entire(obj_req)) {
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
+ if (obj_req->num_img_extents)
+ num_osd_ops = 2; /* create + truncate */
+ else
+ num_osd_ops = 1; /* delete */
+ } else {
+ if (obj_req->num_img_extents) {
+ obj_req->write_state = RBD_OBJ_WRITE_GUARD;
+ num_osd_ops = 2; /* stat + truncate/zero */
+ } else {
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
+ num_osd_ops = 1; /* truncate/zero */
+ }
}
- while (resid) {
- struct ceph_osd_request *osd_req;
- u64 object_no = img_offset >> rbd_dev->header.obj_order;
- u64 offset = rbd_segment_offset(rbd_dev, img_offset);
- u64 length = rbd_segment_length(rbd_dev, img_offset, resid);
-
- obj_request = rbd_obj_request_create(type);
- if (!obj_request)
- goto out_unwind;
-
- obj_request->object_no = object_no;
- obj_request->offset = offset;
- obj_request->length = length;
-
- /*
- * set obj_request->img_request before creating the
- * osd_request so that it gets the right snapc
- */
- rbd_img_obj_request_add(img_request, obj_request);
-
- if (type == OBJ_REQUEST_BIO) {
- unsigned int clone_size;
-
- rbd_assert(length <= (u64)UINT_MAX);
- clone_size = (unsigned int)length;
- obj_request->bio_list =
- bio_chain_clone_range(&bio_list,
- &bio_offset,
- clone_size,
- GFP_NOIO);
- if (!obj_request->bio_list)
- goto out_unwind;
- } else if (type == OBJ_REQUEST_PAGES) {
- unsigned int page_count;
-
- obj_request->pages = pages;
- page_count = (u32)calc_pages_for(offset, length);
- obj_request->page_count = page_count;
- if ((offset + length) & ~PAGE_MASK)
- page_count--; /* more on last page */
- pages += page_count;
- }
+ obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
- osd_req = rbd_osd_req_create(rbd_dev, op_type,
- (op_type == OBJ_OP_WRITE) ? 2 : 1,
- obj_request);
- if (!osd_req)
- goto out_unwind;
+ if (!rbd_obj_is_entire(obj_req) && obj_req->num_img_extents) {
+ ret = __rbd_obj_setup_stat(obj_req, which++);
+ if (ret)
+ return ret;
+ }
- obj_request->osd_req = osd_req;
- obj_request->callback = rbd_img_obj_callback;
- obj_request->img_offset = img_offset;
+ __rbd_obj_setup_discard(obj_req, which);
+ return 0;
+}
- rbd_img_obj_request_fill(obj_request, osd_req, op_type, 0);
+/*
+ * For each object request in @img_req, allocate an OSD request, add
+ * individual OSD ops and prepare them for submission. The number of
+ * OSD ops depends on op_type and the overlap point (if any).
+ */
+static int __rbd_img_fill_request(struct rbd_img_request *img_req)
+{
+ struct rbd_obj_request *obj_req;
+ int ret;
- img_offset += length;
- resid -= length;
+ for_each_obj_request(img_req, obj_req) {
+ switch (img_req->op_type) {
+ case OBJ_OP_READ:
+ ret = rbd_obj_setup_read(obj_req);
+ break;
+ case OBJ_OP_WRITE:
+ ret = rbd_obj_setup_write(obj_req);
+ break;
+ case OBJ_OP_DISCARD:
+ ret = rbd_obj_setup_discard(obj_req);
+ break;
+ default:
+ rbd_assert(0);
+ }
+ if (ret)
+ return ret;
}
return 0;
+}
-out_unwind:
- for_each_obj_request_safe(img_request, obj_request, next_obj_request)
- rbd_img_obj_request_del(img_request, obj_request);
+union rbd_img_fill_iter {
+ struct ceph_bio_iter bio_iter;
+ struct ceph_bvec_iter bvec_iter;
+};
- return -ENOMEM;
-}
+struct rbd_img_fill_ctx {
+ enum obj_request_type pos_type;
+ union rbd_img_fill_iter *pos;
+ union rbd_img_fill_iter iter;
+ ceph_object_extent_fn_t set_pos_fn;
+ ceph_object_extent_fn_t count_fn;
+ ceph_object_extent_fn_t copy_fn;
+};
-static void
-rbd_osd_copyup_callback(struct rbd_obj_request *obj_request)
+static struct ceph_object_extent *alloc_object_extent(void *arg)
{
- struct rbd_img_request *img_request;
- struct rbd_device *rbd_dev;
- struct page **pages;
- u32 page_count;
+ struct rbd_img_request *img_req = arg;
+ struct rbd_obj_request *obj_req;
- dout("%s: obj %p\n", __func__, obj_request);
+ obj_req = rbd_obj_request_create();
+ if (!obj_req)
+ return NULL;
- rbd_assert(obj_request->type == OBJ_REQUEST_BIO ||
- obj_request->type == OBJ_REQUEST_NODATA);
- rbd_assert(obj_request_img_data_test(obj_request));
- img_request = obj_request->img_request;
- rbd_assert(img_request);
+ rbd_img_obj_request_add(img_req, obj_req);
+ return &obj_req->ex;
+}
- rbd_dev = img_request->rbd_dev;
- rbd_assert(rbd_dev);
+/*
+ * While su != os && sc == 1 is technically not fancy (it's the same
+ * layout as su == os && sc == 1), we can't use the nocopy path for it
+ * because ->set_pos_fn() should be called only once per object.
+ * ceph_file_to_extents() invokes action_fn once per stripe unit, so
+ * treat su != os && sc == 1 as fancy.
+ */
+static bool rbd_layout_is_fancy(struct ceph_file_layout *l)
+{
+ return l->stripe_unit != l->object_size;
+}
+
+static int rbd_img_fill_request_nocopy(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct rbd_img_fill_ctx *fctx)
+{
+ u32 i;
+ int ret;
- pages = obj_request->copyup_pages;
- rbd_assert(pages != NULL);
- obj_request->copyup_pages = NULL;
- page_count = obj_request->copyup_page_count;
- rbd_assert(page_count);
- obj_request->copyup_page_count = 0;
- ceph_release_page_vector(pages, page_count);
+ img_req->data_type = fctx->pos_type;
/*
- * We want the transfer count to reflect the size of the
- * original write request. There is no such thing as a
- * successful short write, so if the request was successful
- * we can just set it to the originally-requested length.
+ * Create object requests and set each object request's starting
+ * position in the provided bio (list) or bio_vec array.
*/
- if (!obj_request->result)
- obj_request->xferred = obj_request->length;
+ fctx->iter = *fctx->pos;
+ for (i = 0; i < num_img_extents; i++) {
+ ret = ceph_file_to_extents(&img_req->rbd_dev->layout,
+ img_extents[i].fe_off,
+ img_extents[i].fe_len,
+ &img_req->object_extents,
+ alloc_object_extent, img_req,
+ fctx->set_pos_fn, &fctx->iter);
+ if (ret)
+ return ret;
+ }
- obj_request_done_set(obj_request);
+ return __rbd_img_fill_request(img_req);
}
-static void
-rbd_img_obj_parent_read_full_callback(struct rbd_img_request *img_request)
+/*
+ * Map a list of image extents to a list of object extents, create the
+ * corresponding object requests (normally each to a different object,
+ * but not always) and add them to @img_req. For each object request,
+ * set up its data descriptor to point to the corresponding chunk(s) of
+ * @fctx->pos data buffer.
+ *
+ * Because ceph_file_to_extents() will merge adjacent object extents
+ * together, each object request's data descriptor may point to multiple
+ * different chunks of @fctx->pos data buffer.
+ *
+ * @fctx->pos data buffer is assumed to be large enough.
+ */
+static int rbd_img_fill_request(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct rbd_img_fill_ctx *fctx)
{
- struct rbd_obj_request *orig_request;
- struct ceph_osd_request *osd_req;
- struct rbd_device *rbd_dev;
- struct page **pages;
- enum obj_operation_type op_type;
- u32 page_count;
- int img_result;
- u64 parent_length;
-
- rbd_assert(img_request_child_test(img_request));
-
- /* First get what we need from the image request */
-
- pages = img_request->copyup_pages;
- rbd_assert(pages != NULL);
- img_request->copyup_pages = NULL;
- page_count = img_request->copyup_page_count;
- rbd_assert(page_count);
- img_request->copyup_page_count = 0;
-
- orig_request = img_request->obj_request;
- rbd_assert(orig_request != NULL);
- rbd_assert(obj_request_type_valid(orig_request->type));
- img_result = img_request->result;
- parent_length = img_request->length;
- rbd_assert(img_result || parent_length == img_request->xferred);
- rbd_img_request_put(img_request);
+ struct rbd_device *rbd_dev = img_req->rbd_dev;
+ struct rbd_obj_request *obj_req;
+ u32 i;
+ int ret;
- rbd_assert(orig_request->img_request);
- rbd_dev = orig_request->img_request->rbd_dev;
- rbd_assert(rbd_dev);
+ if (fctx->pos_type == OBJ_REQUEST_NODATA ||
+ !rbd_layout_is_fancy(&rbd_dev->layout))
+ return rbd_img_fill_request_nocopy(img_req, img_extents,
+ num_img_extents, fctx);
+
+ img_req->data_type = OBJ_REQUEST_OWN_BVECS;
/*
- * If the overlap has become 0 (most likely because the
- * image has been flattened) we need to free the pages
- * and re-submit the original write request.
+ * Create object requests and determine ->bvec_count for each object
+ * request. Note that ->bvec_count sum over all object requests may
+ * be greater than the number of bio_vecs in the provided bio (list)
+ * or bio_vec array because when mapped, those bio_vecs can straddle
+ * stripe unit boundaries.
*/
- if (!rbd_dev->parent_overlap) {
- ceph_release_page_vector(pages, page_count);
- rbd_obj_request_submit(orig_request);
- return;
+ fctx->iter = *fctx->pos;
+ for (i = 0; i < num_img_extents; i++) {
+ ret = ceph_file_to_extents(&rbd_dev->layout,
+ img_extents[i].fe_off,
+ img_extents[i].fe_len,
+ &img_req->object_extents,
+ alloc_object_extent, img_req,
+ fctx->count_fn, &fctx->iter);
+ if (ret)
+ return ret;
}
- if (img_result)
- goto out_err;
+ for_each_obj_request(img_req, obj_req) {
+ obj_req->bvec_pos.bvecs = kmalloc_array(obj_req->bvec_count,
+ sizeof(*obj_req->bvec_pos.bvecs),
+ GFP_NOIO);
+ if (!obj_req->bvec_pos.bvecs)
+ return -ENOMEM;
+ }
/*
- * The original osd request is of no use to use any more.
- * We need a new one that can hold the three ops in a copyup
- * request. Allocate the new copyup osd request for the
- * original request, and release the old one.
+ * Fill in each object request's private bio_vec array, splitting and
+ * rearranging the provided bio_vecs in stripe unit chunks as needed.
*/
- img_result = -ENOMEM;
- osd_req = rbd_osd_req_create_copyup(orig_request);
- if (!osd_req)
- goto out_err;
- rbd_osd_req_destroy(orig_request->osd_req);
- orig_request->osd_req = osd_req;
- orig_request->copyup_pages = pages;
- orig_request->copyup_page_count = page_count;
+ fctx->iter = *fctx->pos;
+ for (i = 0; i < num_img_extents; i++) {
+ ret = ceph_iterate_extents(&rbd_dev->layout,
+ img_extents[i].fe_off,
+ img_extents[i].fe_len,
+ &img_req->object_extents,
+ fctx->copy_fn, &fctx->iter);
+ if (ret)
+ return ret;
+ }
- /* Initialize the copyup op */
+ return __rbd_img_fill_request(img_req);
+}
- osd_req_op_cls_init(osd_req, 0, CEPH_OSD_OP_CALL, "rbd", "copyup");
- osd_req_op_cls_request_data_pages(osd_req, 0, pages, parent_length, 0,
- false, false);
+static int rbd_img_fill_nodata(struct rbd_img_request *img_req,
+ u64 off, u64 len)
+{
+ struct ceph_file_extent ex = { off, len };
+ union rbd_img_fill_iter dummy;
+ struct rbd_img_fill_ctx fctx = {
+ .pos_type = OBJ_REQUEST_NODATA,
+ .pos = &dummy,
+ };
- /* Add the other op(s) */
+ return rbd_img_fill_request(img_req, &ex, 1, &fctx);
+}
- op_type = rbd_img_request_op_type(orig_request->img_request);
- rbd_img_obj_request_fill(orig_request, osd_req, op_type, 1);
+static void set_bio_pos(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bio_iter *it = arg;
- /* All set, send it off. */
+ dout("%s objno %llu bytes %u\n", __func__, ex->oe_objno, bytes);
+ obj_req->bio_pos = *it;
+ ceph_bio_iter_advance(it, bytes);
+}
- rbd_obj_request_submit(orig_request);
- return;
+static void count_bio_bvecs(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bio_iter *it = arg;
+
+ dout("%s objno %llu bytes %u\n", __func__, ex->oe_objno, bytes);
+ ceph_bio_iter_advance_step(it, bytes, ({
+ obj_req->bvec_count++;
+ }));
-out_err:
- ceph_release_page_vector(pages, page_count);
- rbd_obj_request_error(orig_request, img_result);
}
-/*
- * Read from the parent image the range of data that covers the
- * entire target of the given object request. This is used for
- * satisfying a layered image write request when the target of an
- * object request from the image request does not exist.
- *
- * A page array big enough to hold the returned data is allocated
- * and supplied to rbd_img_request_fill() as the "data descriptor."
- * When the read completes, this page array will be transferred to
- * the original object request for the copyup operation.
- *
- * If an error occurs, it is recorded as the result of the original
- * object request in rbd_img_obj_exists_callback().
- */
-static int rbd_img_obj_parent_read_full(struct rbd_obj_request *obj_request)
-{
- struct rbd_device *rbd_dev = obj_request->img_request->rbd_dev;
- struct rbd_img_request *parent_request = NULL;
- u64 img_offset;
- u64 length;
- struct page **pages = NULL;
- u32 page_count;
- int result;
+static void copy_bio_bvecs(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bio_iter *it = arg;
- rbd_assert(rbd_dev->parent != NULL);
+ dout("%s objno %llu bytes %u\n", __func__, ex->oe_objno, bytes);
+ ceph_bio_iter_advance_step(it, bytes, ({
+ obj_req->bvec_pos.bvecs[obj_req->bvec_idx++] = bv;
+ obj_req->bvec_pos.iter.bi_size += bv.bv_len;
+ }));
+}
- /*
- * Determine the byte range covered by the object in the
- * child image to which the original request was to be sent.
- */
- img_offset = obj_request->img_offset - obj_request->offset;
- length = rbd_obj_bytes(&rbd_dev->header);
+static int __rbd_img_fill_from_bio(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct ceph_bio_iter *bio_pos)
+{
+ struct rbd_img_fill_ctx fctx = {
+ .pos_type = OBJ_REQUEST_BIO,
+ .pos = (union rbd_img_fill_iter *)bio_pos,
+ .set_pos_fn = set_bio_pos,
+ .count_fn = count_bio_bvecs,
+ .copy_fn = copy_bio_bvecs,
+ };
- /*
- * There is no defined parent data beyond the parent
- * overlap, so limit what we read at that boundary if
- * necessary.
- */
- if (img_offset + length > rbd_dev->parent_overlap) {
- rbd_assert(img_offset < rbd_dev->parent_overlap);
- length = rbd_dev->parent_overlap - img_offset;
- }
+ return rbd_img_fill_request(img_req, img_extents, num_img_extents,
+ &fctx);
+}
- /*
- * Allocate a page array big enough to receive the data read
- * from the parent.
- */
- page_count = (u32)calc_pages_for(0, length);
- pages = ceph_alloc_page_vector(page_count, GFP_NOIO);
- if (IS_ERR(pages)) {
- result = PTR_ERR(pages);
- pages = NULL;
- goto out_err;
- }
+static int rbd_img_fill_from_bio(struct rbd_img_request *img_req,
+ u64 off, u64 len, struct bio *bio)
+{
+ struct ceph_file_extent ex = { off, len };
+ struct ceph_bio_iter it = { .bio = bio, .iter = bio->bi_iter };
- result = -ENOMEM;
- parent_request = rbd_parent_request_create(obj_request,
- img_offset, length);
- if (!parent_request)
- goto out_err;
+ return __rbd_img_fill_from_bio(img_req, &ex, 1, &it);
+}
- result = rbd_img_request_fill(parent_request, OBJ_REQUEST_PAGES, pages);
- if (result)
- goto out_err;
+static void set_bvec_pos(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bvec_iter *it = arg;
- parent_request->copyup_pages = pages;
- parent_request->copyup_page_count = page_count;
- parent_request->callback = rbd_img_obj_parent_read_full_callback;
+ obj_req->bvec_pos = *it;
+ ceph_bvec_iter_shorten(&obj_req->bvec_pos, bytes);
+ ceph_bvec_iter_advance(it, bytes);
+}
- result = rbd_img_request_submit(parent_request);
- if (!result)
- return 0;
+static void count_bvecs(struct ceph_object_extent *ex, u32 bytes, void *arg)
+{
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bvec_iter *it = arg;
- parent_request->copyup_pages = NULL;
- parent_request->copyup_page_count = 0;
-out_err:
- if (pages)
- ceph_release_page_vector(pages, page_count);
- if (parent_request)
- rbd_img_request_put(parent_request);
- return result;
+ ceph_bvec_iter_advance_step(it, bytes, ({
+ obj_req->bvec_count++;
+ }));
}
-static void rbd_img_obj_exists_callback(struct rbd_obj_request *obj_request)
+static void copy_bvecs(struct ceph_object_extent *ex, u32 bytes, void *arg)
{
- struct rbd_obj_request *orig_request;
- struct rbd_device *rbd_dev;
- int result;
+ struct rbd_obj_request *obj_req =
+ container_of(ex, struct rbd_obj_request, ex);
+ struct ceph_bvec_iter *it = arg;
- rbd_assert(!obj_request_img_data_test(obj_request));
+ ceph_bvec_iter_advance_step(it, bytes, ({
+ obj_req->bvec_pos.bvecs[obj_req->bvec_idx++] = bv;
+ obj_req->bvec_pos.iter.bi_size += bv.bv_len;
+ }));
+}
- /*
- * All we need from the object request is the original
- * request and the result of the STAT op. Grab those, then
- * we're done with the request.
- */
- orig_request = obj_request->obj_request;
- obj_request->obj_request = NULL;
- rbd_obj_request_put(orig_request);
- rbd_assert(orig_request);
- rbd_assert(orig_request->img_request);
-
- result = obj_request->result;
- obj_request->result = 0;
-
- dout("%s: obj %p for obj %p result %d %llu/%llu\n", __func__,
- obj_request, orig_request, result,
- obj_request->xferred, obj_request->length);
- rbd_obj_request_put(obj_request);
+static int __rbd_img_fill_from_bvecs(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct ceph_bvec_iter *bvec_pos)
+{
+ struct rbd_img_fill_ctx fctx = {
+ .pos_type = OBJ_REQUEST_BVECS,
+ .pos = (union rbd_img_fill_iter *)bvec_pos,
+ .set_pos_fn = set_bvec_pos,
+ .count_fn = count_bvecs,
+ .copy_fn = copy_bvecs,
+ };
- /*
- * If the overlap has become 0 (most likely because the
- * image has been flattened) we need to re-submit the
- * original request.
- */
- rbd_dev = orig_request->img_request->rbd_dev;
- if (!rbd_dev->parent_overlap) {
- rbd_obj_request_submit(orig_request);
- return;
- }
+ return rbd_img_fill_request(img_req, img_extents, num_img_extents,
+ &fctx);
+}
- /*
- * Our only purpose here is to determine whether the object
- * exists, and we don't want to treat the non-existence as
- * an error. If something else comes back, transfer the
- * error to the original request and complete it now.
- */
- if (!result) {
- obj_request_existence_set(orig_request, true);
- } else if (result == -ENOENT) {
- obj_request_existence_set(orig_request, false);
- } else {
- goto fail_orig_request;
- }
+static int rbd_img_fill_from_bvecs(struct rbd_img_request *img_req,
+ struct ceph_file_extent *img_extents,
+ u32 num_img_extents,
+ struct bio_vec *bvecs)
+{
+ struct ceph_bvec_iter it = {
+ .bvecs = bvecs,
+ .iter = { .bi_size = ceph_file_extents_bytes(img_extents,
+ num_img_extents) },
+ };
- /*
- * Resubmit the original request now that we have recorded
- * whether the target object exists.
- */
- result = rbd_img_obj_request_submit(orig_request);
- if (result)
- goto fail_orig_request;
+ return __rbd_img_fill_from_bvecs(img_req, img_extents, num_img_extents,
+ &it);
+}
- return;
+static void rbd_img_request_submit(struct rbd_img_request *img_request)
+{
+ struct rbd_obj_request *obj_request;
+
+ dout("%s: img %p\n", __func__, img_request);
+
+ rbd_img_request_get(img_request);
+ for_each_obj_request(img_request, obj_request)
+ rbd_obj_request_submit(obj_request);
-fail_orig_request:
- rbd_obj_request_error(orig_request, result);
+ rbd_img_request_put(img_request);
}
-static int rbd_img_obj_exists_submit(struct rbd_obj_request *obj_request)
+static int rbd_obj_read_from_parent(struct rbd_obj_request *obj_req)
{
- struct rbd_device *rbd_dev = obj_request->img_request->rbd_dev;
- struct rbd_obj_request *stat_request;
- struct page **pages;
- u32 page_count;
- size_t size;
+ struct rbd_img_request *img_req = obj_req->img_request;
+ struct rbd_img_request *child_img_req;
int ret;
- stat_request = rbd_obj_request_create(OBJ_REQUEST_PAGES);
- if (!stat_request)
+ child_img_req = rbd_img_request_create(img_req->rbd_dev->parent,
+ OBJ_OP_READ, NULL);
+ if (!child_img_req)
return -ENOMEM;
- stat_request->object_no = obj_request->object_no;
+ __set_bit(IMG_REQ_CHILD, &child_img_req->flags);
+ child_img_req->obj_request = obj_req;
- stat_request->osd_req = rbd_osd_req_create(rbd_dev, OBJ_OP_READ, 1,
- stat_request);
- if (!stat_request->osd_req) {
- ret = -ENOMEM;
- goto fail_stat_request;
+ if (!rbd_img_is_write(img_req)) {
+ switch (img_req->data_type) {
+ case OBJ_REQUEST_BIO:
+ ret = __rbd_img_fill_from_bio(child_img_req,
+ obj_req->img_extents,
+ obj_req->num_img_extents,
+ &obj_req->bio_pos);
+ break;
+ case OBJ_REQUEST_BVECS:
+ case OBJ_REQUEST_OWN_BVECS:
+ ret = __rbd_img_fill_from_bvecs(child_img_req,
+ obj_req->img_extents,
+ obj_req->num_img_extents,
+ &obj_req->bvec_pos);
+ break;
+ default:
+ rbd_assert(0);
+ }
+ } else {
+ ret = rbd_img_fill_from_bvecs(child_img_req,
+ obj_req->img_extents,
+ obj_req->num_img_extents,
+ obj_req->copyup_bvecs);
+ }
+ if (ret) {
+ rbd_img_request_put(child_img_req);
+ return ret;
+ }
+
+ rbd_img_request_submit(child_img_req);
+ return 0;
+}
+
+static bool rbd_obj_handle_read(struct rbd_obj_request *obj_req)
+{
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
+ int ret;
+
+ if (obj_req->result == -ENOENT &&
+ rbd_dev->parent_overlap && !obj_req->tried_parent) {
+ /* reverse map this object extent onto the parent */
+ ret = rbd_obj_calc_img_extents(obj_req, false);
+ if (ret) {
+ obj_req->result = ret;
+ return true;
+ }
+
+ if (obj_req->num_img_extents) {
+ obj_req->tried_parent = true;
+ ret = rbd_obj_read_from_parent(obj_req);
+ if (ret) {
+ obj_req->result = ret;
+ return true;
+ }
+ return false;
+ }
}
/*
- * The response data for a STAT call consists of:
- * le64 length;
- * struct {
- * le32 tv_sec;
- * le32 tv_nsec;
- * } mtime;
+ * -ENOENT means a hole in the image -- zero-fill the entire
+ * length of the request. A short read also implies zero-fill
+ * to the end of the request. In both cases we update xferred
+ * count to indicate the whole request was satisfied.
*/
- size = sizeof (__le64) + sizeof (__le32) + sizeof (__le32);
- page_count = (u32)calc_pages_for(0, size);
- pages = ceph_alloc_page_vector(page_count, GFP_NOIO);
- if (IS_ERR(pages)) {
- ret = PTR_ERR(pages);
- goto fail_stat_request;
+ if (obj_req->result == -ENOENT ||
+ (!obj_req->result && obj_req->xferred < obj_req->ex.oe_len)) {
+ rbd_assert(!obj_req->xferred || !obj_req->result);
+ rbd_obj_zero_range(obj_req, obj_req->xferred,
+ obj_req->ex.oe_len - obj_req->xferred);
+ obj_req->result = 0;
+ obj_req->xferred = obj_req->ex.oe_len;
}
- osd_req_op_init(stat_request->osd_req, 0, CEPH_OSD_OP_STAT, 0);
- osd_req_op_raw_data_in_pages(stat_request->osd_req, 0, pages, size, 0,
- false, false);
-
- rbd_obj_request_get(obj_request);
- stat_request->obj_request = obj_request;
- stat_request->pages = pages;
- stat_request->page_count = page_count;
- stat_request->callback = rbd_img_obj_exists_callback;
+ return true;
+}
- rbd_obj_request_submit(stat_request);
- return 0;
+/*
+ * copyup_bvecs pages are never highmem pages
+ */
+static bool is_zero_bvecs(struct bio_vec *bvecs, u32 bytes)
+{
+ struct ceph_bvec_iter it = {
+ .bvecs = bvecs,
+ .iter = { .bi_size = bytes },
+ };
-fail_stat_request:
- rbd_obj_request_put(stat_request);
- return ret;
+ ceph_bvec_iter_advance_step(&it, bytes, ({
+ if (memchr_inv(page_address(bv.bv_page) + bv.bv_offset, 0,
+ bv.bv_len))
+ return false;
+ }));
+ return true;
}
-static bool img_obj_request_simple(struct rbd_obj_request *obj_request)
+static int rbd_obj_issue_copyup(struct rbd_obj_request *obj_req, u32 bytes)
{
- struct rbd_img_request *img_request = obj_request->img_request;
- struct rbd_device *rbd_dev = img_request->rbd_dev;
+ unsigned int num_osd_ops = obj_req->osd_req->r_num_ops;
- /* Reads */
- if (!img_request_write_test(img_request) &&
- !img_request_discard_test(img_request))
- return true;
-
- /* Non-layered writes */
- if (!img_request_layered_test(img_request))
- return true;
+ dout("%s obj_req %p bytes %u\n", __func__, obj_req, bytes);
+ rbd_assert(obj_req->osd_req->r_ops[0].op == CEPH_OSD_OP_STAT);
+ rbd_osd_req_destroy(obj_req->osd_req);
/*
- * Layered writes outside of the parent overlap range don't
- * share any data with the parent.
+ * Create a copyup request with the same number of OSD ops as
+ * the original request. The original request was stat + op(s),
+ * the new copyup request will be copyup + the same op(s).
*/
- if (!obj_request_overlaps_parent(obj_request))
- return true;
+ obj_req->osd_req = rbd_osd_req_create(obj_req, num_osd_ops);
+ if (!obj_req->osd_req)
+ return -ENOMEM;
/*
- * Entire-object layered writes - we will overwrite whatever
- * parent data there is anyway.
+ * Only send non-zero copyup data to save some I/O and network
+ * bandwidth -- zero copyup data is equivalent to the object not
+ * existing.
*/
- if (!obj_request->offset &&
- obj_request->length == rbd_obj_bytes(&rbd_dev->header))
- return true;
+ if (is_zero_bvecs(obj_req->copyup_bvecs, bytes)) {
+ dout("%s obj_req %p detected zeroes\n", __func__, obj_req);
+ bytes = 0;
+ }
- /*
- * If the object is known to already exist, its parent data has
- * already been copied.
- */
- if (obj_request_known_test(obj_request) &&
- obj_request_exists_test(obj_request))
- return true;
+ osd_req_op_cls_init(obj_req->osd_req, 0, CEPH_OSD_OP_CALL, "rbd",
+ "copyup");
+ osd_req_op_cls_request_data_bvecs(obj_req->osd_req, 0,
+ obj_req->copyup_bvecs, bytes);
+
+ switch (obj_req->img_request->op_type) {
+ case OBJ_OP_WRITE:
+ __rbd_obj_setup_write(obj_req, 1);
+ break;
+ case OBJ_OP_DISCARD:
+ rbd_assert(!rbd_obj_is_entire(obj_req));
+ __rbd_obj_setup_discard(obj_req, 1);
+ break;
+ default:
+ rbd_assert(0);
+ }
- return false;
+ rbd_obj_request_submit(obj_req);
+ return 0;
}
-static int rbd_img_obj_request_submit(struct rbd_obj_request *obj_request)
+static int setup_copyup_bvecs(struct rbd_obj_request *obj_req, u64 obj_overlap)
{
- rbd_assert(obj_request_img_data_test(obj_request));
- rbd_assert(obj_request_type_valid(obj_request->type));
- rbd_assert(obj_request->img_request);
+ u32 i;
- if (img_obj_request_simple(obj_request)) {
- rbd_obj_request_submit(obj_request);
- return 0;
- }
+ rbd_assert(!obj_req->copyup_bvecs);
+ obj_req->copyup_bvec_count = calc_pages_for(0, obj_overlap);
+ obj_req->copyup_bvecs = kcalloc(obj_req->copyup_bvec_count,
+ sizeof(*obj_req->copyup_bvecs),
+ GFP_NOIO);
+ if (!obj_req->copyup_bvecs)
+ return -ENOMEM;
- /*
- * It's a layered write. The target object might exist but
- * we may not know that yet. If we know it doesn't exist,
- * start by reading the data for the full target object from
- * the parent so we can use it for a copyup to the target.
- */
- if (obj_request_known_test(obj_request))
- return rbd_img_obj_parent_read_full(obj_request);
+ for (i = 0; i < obj_req->copyup_bvec_count; i++) {
+ unsigned int len = min(obj_overlap, (u64)PAGE_SIZE);
+
+ obj_req->copyup_bvecs[i].bv_page = alloc_page(GFP_NOIO);
+ if (!obj_req->copyup_bvecs[i].bv_page)
+ return -ENOMEM;
- /* We don't know whether the target exists. Go find out. */
+ obj_req->copyup_bvecs[i].bv_offset = 0;
+ obj_req->copyup_bvecs[i].bv_len = len;
+ obj_overlap -= len;
+ }
- return rbd_img_obj_exists_submit(obj_request);
+ rbd_assert(!obj_overlap);
+ return 0;
}
-static int rbd_img_request_submit(struct rbd_img_request *img_request)
+static int rbd_obj_handle_write_guard(struct rbd_obj_request *obj_req)
{
- struct rbd_obj_request *obj_request;
- struct rbd_obj_request *next_obj_request;
- int ret = 0;
-
- dout("%s: img %p\n", __func__, img_request);
+ struct rbd_device *rbd_dev = obj_req->img_request->rbd_dev;
+ int ret;
- rbd_img_request_get(img_request);
- for_each_obj_request_safe(img_request, obj_request, next_obj_request) {
- ret = rbd_img_obj_request_submit(obj_request);
- if (ret)
- goto out_put_ireq;
+ rbd_assert(obj_req->num_img_extents);
+ prune_extents(obj_req->img_extents, &obj_req->num_img_extents,
+ rbd_dev->parent_overlap);
+ if (!obj_req->num_img_extents) {
+ /*
+ * The overlap has become 0 (most likely because the
+ * image has been flattened). Use rbd_obj_issue_copyup()
+ * to re-submit the original write request -- the copyup
+ * operation itself will be a no-op, since someone must
+ * have populated the child object while we weren't
+ * looking. Move to WRITE_FLAT state as we'll be done
+ * with the operation once the null copyup completes.
+ */
+ obj_req->write_state = RBD_OBJ_WRITE_FLAT;
+ return rbd_obj_issue_copyup(obj_req, 0);
}
-out_put_ireq:
- rbd_img_request_put(img_request);
- return ret;
+ ret = setup_copyup_bvecs(obj_req, rbd_obj_img_extents_bytes(obj_req));
+ if (ret)
+ return ret;
+
+ obj_req->write_state = RBD_OBJ_WRITE_COPYUP;
+ return rbd_obj_read_from_parent(obj_req);
}
-static void rbd_img_parent_read_callback(struct rbd_img_request *img_request)
+static bool rbd_obj_handle_write(struct rbd_obj_request *obj_req)
{
- struct rbd_obj_request *obj_request;
- struct rbd_device *rbd_dev;
- u64 obj_end;
- u64 img_xferred;
- int img_result;
+ int ret;
- rbd_assert(img_request_child_test(img_request));
+again:
+ switch (obj_req->write_state) {
+ case RBD_OBJ_WRITE_GUARD:
+ rbd_assert(!obj_req->xferred);
+ if (obj_req->result == -ENOENT) {
+ /*
+ * The target object doesn't exist. Read the data for
+ * the entire target object up to the overlap point (if
+ * any) from the parent, so we can use it for a copyup.
+ */
+ ret = rbd_obj_handle_write_guard(obj_req);
+ if (ret) {
+ obj_req->result = ret;
+ return true;
+ }
+ return false;
+ }
+ /* fall through */
+ case RBD_OBJ_WRITE_FLAT:
+ if (!obj_req->result)
+ /*
+ * There is no such thing as a successful short
+ * write -- indicate the whole request was satisfied.
+ */
+ obj_req->xferred = obj_req->ex.oe_len;
+ return true;
+ case RBD_OBJ_WRITE_COPYUP:
+ obj_req->write_state = RBD_OBJ_WRITE_GUARD;
+ if (obj_req->result)
+ goto again;
- /* First get what we need from the image request and release it */
+ rbd_assert(obj_req->xferred);
+ ret = rbd_obj_issue_copyup(obj_req, obj_req->xferred);
+ if (ret) {
+ obj_req->result = ret;
+ return true;
+ }
+ return false;
+ default:
+ BUG();
+ }
+}
- obj_request = img_request->obj_request;
- img_xferred = img_request->xferred;
- img_result = img_request->result;
- rbd_img_request_put(img_request);
+/*
+ * Returns true if @obj_req is completed, or false otherwise.
+ */
+static bool __rbd_obj_handle_request(struct rbd_obj_request *obj_req)
+{
+ switch (obj_req->img_request->op_type) {
+ case OBJ_OP_READ:
+ return rbd_obj_handle_read(obj_req);
+ case OBJ_OP_WRITE:
+ return rbd_obj_handle_write(obj_req);
+ case OBJ_OP_DISCARD:
+ if (rbd_obj_handle_write(obj_req)) {
+ /*
+ * Hide -ENOENT from delete/truncate/zero -- discarding
+ * a non-existent object is not a problem.
+ */
+ if (obj_req->result == -ENOENT) {
+ obj_req->result = 0;
+ obj_req->xferred = obj_req->ex.oe_len;
+ }
+ return true;
+ }
+ return false;
+ default:
+ BUG();
+ }
+}
- /*
- * If the overlap has become 0 (most likely because the
- * image has been flattened) we need to re-submit the
- * original request.
- */
- rbd_assert(obj_request);
- rbd_assert(obj_request->img_request);
- rbd_dev = obj_request->img_request->rbd_dev;
- if (!rbd_dev->parent_overlap) {
- rbd_obj_request_submit(obj_request);
+static void rbd_obj_end_request(struct rbd_obj_request *obj_req)
+{
+ struct rbd_img_request *img_req = obj_req->img_request;
+
+ rbd_assert((!obj_req->result &&
+ obj_req->xferred == obj_req->ex.oe_len) ||
+ (obj_req->result < 0 && !obj_req->xferred));
+ if (!obj_req->result) {
+ img_req->xferred += obj_req->xferred;
return;
}
- obj_request->result = img_result;
- if (obj_request->result)
- goto out;
+ rbd_warn(img_req->rbd_dev,
+ "%s at objno %llu %llu~%llu result %d xferred %llu",
+ obj_op_name(img_req->op_type), obj_req->ex.oe_objno,
+ obj_req->ex.oe_off, obj_req->ex.oe_len, obj_req->result,
+ obj_req->xferred);
+ if (!img_req->result) {
+ img_req->result = obj_req->result;
+ img_req->xferred = 0;
+ }
+}
- /*
- * We need to zero anything beyond the parent overlap
- * boundary. Since rbd_img_obj_request_read_callback()
- * will zero anything beyond the end of a short read, an
- * easy way to do this is to pretend the data from the
- * parent came up short--ending at the overlap boundary.
- */
- rbd_assert(obj_request->img_offset < U64_MAX - obj_request->length);
- obj_end = obj_request->img_offset + obj_request->length;
- if (obj_end > rbd_dev->parent_overlap) {
- u64 xferred = 0;
+static void rbd_img_end_child_request(struct rbd_img_request *img_req)
+{
+ struct rbd_obj_request *obj_req = img_req->obj_request;
- if (obj_request->img_offset < rbd_dev->parent_overlap)
- xferred = rbd_dev->parent_overlap -
- obj_request->img_offset;
+ rbd_assert(test_bit(IMG_REQ_CHILD, &img_req->flags));
+ rbd_assert((!img_req->result &&
+ img_req->xferred == rbd_obj_img_extents_bytes(obj_req)) ||
+ (img_req->result < 0 && !img_req->xferred));
- obj_request->xferred = min(img_xferred, xferred);
- } else {
- obj_request->xferred = img_xferred;
- }
-out:
- rbd_img_obj_request_read_callback(obj_request);
- rbd_obj_request_complete(obj_request);
+ obj_req->result = img_req->result;
+ obj_req->xferred = img_req->xferred;
+ rbd_img_request_put(img_req);
}
-static void rbd_img_parent_read(struct rbd_obj_request *obj_request)
+static void rbd_img_end_request(struct rbd_img_request *img_req)
{
- struct rbd_img_request *img_request;
- int result;
+ rbd_assert(!test_bit(IMG_REQ_CHILD, &img_req->flags));
+ rbd_assert((!img_req->result &&
+ img_req->xferred == blk_rq_bytes(img_req->rq)) ||
+ (img_req->result < 0 && !img_req->xferred));
- rbd_assert(obj_request_img_data_test(obj_request));
- rbd_assert(obj_request->img_request != NULL);
- rbd_assert(obj_request->result == (s32) -ENOENT);
- rbd_assert(obj_request_type_valid(obj_request->type));
+ blk_mq_end_request(img_req->rq,
+ errno_to_blk_status(img_req->result));
+ rbd_img_request_put(img_req);
+}
- /* rbd_read_finish(obj_request, obj_request->length); */
- img_request = rbd_parent_request_create(obj_request,
- obj_request->img_offset,
- obj_request->length);
- result = -ENOMEM;
- if (!img_request)
- goto out_err;
+static void rbd_obj_handle_request(struct rbd_obj_request *obj_req)
+{
+ struct rbd_img_request *img_req;
- if (obj_request->type == OBJ_REQUEST_BIO)
- result = rbd_img_request_fill(img_request, OBJ_REQUEST_BIO,
- obj_request->bio_list);
- else
- result = rbd_img_request_fill(img_request, OBJ_REQUEST_PAGES,
- obj_request->pages);
- if (result)
- goto out_err;
+again:
+ if (!__rbd_obj_handle_request(obj_req))
+ return;
- img_request->callback = rbd_img_parent_read_callback;
- result = rbd_img_request_submit(img_request);
- if (result)
- goto out_err;
+ img_req = obj_req->img_request;
+ spin_lock(&img_req->completion_lock);
+ rbd_obj_end_request(obj_req);
+ rbd_assert(img_req->pending_count);
+ if (--img_req->pending_count) {
+ spin_unlock(&img_req->completion_lock);
+ return;
+ }
- return;
-out_err:
- if (img_request)
- rbd_img_request_put(img_request);
- obj_request->result = result;
- obj_request->xferred = 0;
- obj_request_done_set(obj_request);
+ spin_unlock(&img_req->completion_lock);
+ if (test_bit(IMG_REQ_CHILD, &img_req->flags)) {
+ obj_req = img_req->obj_request;
+ rbd_img_end_child_request(img_req);
+ goto again;
+ }
+ rbd_img_end_request(img_req);
}
static const struct rbd_client_id rbd_empty_cid;
@@ -3100,8 +2693,8 @@ static int __rbd_notify_op_lock(struct rbd_device *rbd_dev,
{
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
struct rbd_client_id cid = rbd_get_cid(rbd_dev);
- int buf_size = 4 + 8 + 8 + CEPH_ENCODING_START_BLK_LEN;
- char buf[buf_size];
+ char buf[4 + 8 + 8 + CEPH_ENCODING_START_BLK_LEN];
+ int buf_size = sizeof(buf);
void *p = buf;
dout("%s rbd_dev %p notify_op %d\n", __func__, rbd_dev, notify_op);
@@ -3619,8 +3212,8 @@ static void __rbd_acknowledge_notify(struct rbd_device *rbd_dev,
u64 notify_id, u64 cookie, s32 *result)
{
struct ceph_osd_client *osdc = &rbd_dev->rbd_client->client->osdc;
- int buf_size = 4 + CEPH_ENCODING_START_BLK_LEN;
- char buf[buf_size];
+ char buf[4 + CEPH_ENCODING_START_BLK_LEN];
+ int buf_size = sizeof(buf);
int ret;
if (result) {
@@ -3896,7 +3489,7 @@ static void rbd_reregister_watch(struct work_struct *work)
ret = rbd_dev_refresh(rbd_dev);
if (ret)
- rbd_warn(rbd_dev, "reregisteration refresh failed: %d", ret);
+ rbd_warn(rbd_dev, "reregistration refresh failed: %d", ret);
}
/*
@@ -3959,9 +3552,22 @@ static int rbd_obj_method_sync(struct rbd_device *rbd_dev,
/*
* lock_rwsem must be held for read
*/
-static void rbd_wait_state_locked(struct rbd_device *rbd_dev)
+static int rbd_wait_state_locked(struct rbd_device *rbd_dev, bool may_acquire)
{
DEFINE_WAIT(wait);
+ unsigned long timeout;
+ int ret = 0;
+
+ if (test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags))
+ return -EBLACKLISTED;
+
+ if (rbd_dev->lock_state == RBD_LOCK_STATE_LOCKED)
+ return 0;
+
+ if (!may_acquire) {
+ rbd_warn(rbd_dev, "exclusive lock required");
+ return -EROFS;
+ }
do {
/*
@@ -3973,12 +3579,22 @@ static void rbd_wait_state_locked(struct rbd_device *rbd_dev)
prepare_to_wait_exclusive(&rbd_dev->lock_waitq, &wait,
TASK_UNINTERRUPTIBLE);
up_read(&rbd_dev->lock_rwsem);
- schedule();
+ timeout = schedule_timeout(ceph_timeout_jiffies(
+ rbd_dev->opts->lock_timeout));
down_read(&rbd_dev->lock_rwsem);
- } while (rbd_dev->lock_state != RBD_LOCK_STATE_LOCKED &&
- !test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags));
+ if (test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags)) {
+ ret = -EBLACKLISTED;
+ break;
+ }
+ if (!timeout) {
+ rbd_warn(rbd_dev, "timed out waiting for lock");
+ ret = -ETIMEDOUT;
+ break;
+ }
+ } while (rbd_dev->lock_state != RBD_LOCK_STATE_LOCKED);
finish_wait(&rbd_dev->lock_waitq, &wait);
+ return ret;
}
static void rbd_queue_workfn(struct work_struct *work)
@@ -4064,23 +3680,13 @@ static void rbd_queue_workfn(struct work_struct *work)
(op_type != OBJ_OP_READ || rbd_dev->opts->lock_on_read);
if (must_be_locked) {
down_read(&rbd_dev->lock_rwsem);
- if (rbd_dev->lock_state != RBD_LOCK_STATE_LOCKED &&
- !test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags)) {
- if (rbd_dev->opts->exclusive) {
- rbd_warn(rbd_dev, "exclusive lock required");
- result = -EROFS;
- goto err_unlock;
- }
- rbd_wait_state_locked(rbd_dev);
- }
- if (test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags)) {
- result = -EBLACKLISTED;
+ result = rbd_wait_state_locked(rbd_dev,
+ !rbd_dev->opts->exclusive);
+ if (result)
goto err_unlock;
- }
}
- img_request = rbd_img_request_create(rbd_dev, offset, length, op_type,
- snapc);
+ img_request = rbd_img_request_create(rbd_dev, op_type, snapc);
if (!img_request) {
result = -ENOMEM;
goto err_unlock;
@@ -4089,18 +3695,14 @@ static void rbd_queue_workfn(struct work_struct *work)
snapc = NULL; /* img_request consumes a ref */
if (op_type == OBJ_OP_DISCARD)
- result = rbd_img_request_fill(img_request, OBJ_REQUEST_NODATA,
- NULL);
+ result = rbd_img_fill_nodata(img_request, offset, length);
else
- result = rbd_img_request_fill(img_request, OBJ_REQUEST_BIO,
- rq->bio);
- if (result)
- goto err_img_request;
-
- result = rbd_img_request_submit(img_request);
+ result = rbd_img_fill_from_bio(img_request, offset, length,
+ rq->bio);
if (result)
goto err_img_request;
+ rbd_img_request_submit(img_request);
if (must_be_locked)
up_read(&rbd_dev->lock_rwsem);
return;
@@ -4333,7 +3935,8 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
{
struct gendisk *disk;
struct request_queue *q;
- u64 segment_size;
+ unsigned int objset_bytes =
+ rbd_dev->layout.object_size * rbd_dev->layout.stripe_count;
int err;
/* create gendisk info */
@@ -4370,23 +3973,22 @@ static int rbd_init_disk(struct rbd_device *rbd_dev)
goto out_tag_set;
}
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
/* QUEUE_FLAG_ADD_RANDOM is off by default for blk-mq */
- /* set io sizes to object size */
- segment_size = rbd_obj_bytes(&rbd_dev->header);
- blk_queue_max_hw_sectors(q, segment_size / SECTOR_SIZE);
+ blk_queue_max_hw_sectors(q, objset_bytes >> SECTOR_SHIFT);
q->limits.max_sectors = queue_max_hw_sectors(q);
blk_queue_max_segments(q, USHRT_MAX);
- blk_queue_max_segment_size(q, segment_size);
- blk_queue_io_min(q, segment_size);
- blk_queue_io_opt(q, segment_size);
+ blk_queue_max_segment_size(q, UINT_MAX);
+ blk_queue_io_min(q, objset_bytes);
+ blk_queue_io_opt(q, objset_bytes);
- /* enable the discard support */
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
- q->limits.discard_granularity = segment_size;
- blk_queue_max_discard_sectors(q, segment_size / SECTOR_SIZE);
- blk_queue_max_write_zeroes_sectors(q, segment_size / SECTOR_SIZE);
+ if (rbd_dev->opts->trim) {
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, q);
+ q->limits.discard_granularity = objset_bytes;
+ blk_queue_max_discard_sectors(q, objset_bytes >> SECTOR_SHIFT);
+ blk_queue_max_write_zeroes_sectors(q, objset_bytes >> SECTOR_SHIFT);
+ }
if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC))
q->backing_dev_info->capabilities |= BDI_CAP_STABLE_WRITES;
@@ -5066,9 +4668,6 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev)
} __attribute__ ((packed)) striping_info_buf = { 0 };
size_t size = sizeof (striping_info_buf);
void *p;
- u64 obj_size;
- u64 stripe_unit;
- u64 stripe_count;
int ret;
ret = rbd_obj_method_sync(rbd_dev, &rbd_dev->header_oid,
@@ -5080,31 +4679,9 @@ static int rbd_dev_v2_striping_info(struct rbd_device *rbd_dev)
if (ret < size)
return -ERANGE;
- /*
- * We don't actually support the "fancy striping" feature
- * (STRIPINGV2) yet, but if the striping sizes are the
- * defaults the behavior is the same as before. So find
- * out, and only fail if the image has non-default values.
- */
- ret = -EINVAL;
- obj_size = rbd_obj_bytes(&rbd_dev->header);
p = &striping_info_buf;
- stripe_unit = ceph_decode_64(&p);
- if (stripe_unit != obj_size) {
- rbd_warn(rbd_dev, "unsupported stripe unit "
- "(got %llu want %llu)",
- stripe_unit, obj_size);
- return -EINVAL;
- }
- stripe_count = ceph_decode_64(&p);
- if (stripe_count != 1) {
- rbd_warn(rbd_dev, "unsupported stripe count "
- "(got %llu want 1)", stripe_count);
- return -EINVAL;
- }
- rbd_dev->header.stripe_unit = stripe_unit;
- rbd_dev->header.stripe_count = stripe_count;
-
+ rbd_dev->header.stripe_unit = ceph_decode_64(&p);
+ rbd_dev->header.stripe_count = ceph_decode_64(&p);
return 0;
}
@@ -5635,8 +5212,10 @@ static int rbd_add_parse_args(const char *buf,
rbd_opts->read_only = RBD_READ_ONLY_DEFAULT;
rbd_opts->queue_depth = RBD_QUEUE_DEPTH_DEFAULT;
+ rbd_opts->lock_timeout = RBD_LOCK_TIMEOUT_DEFAULT;
rbd_opts->lock_on_read = RBD_LOCK_ON_READ_DEFAULT;
rbd_opts->exclusive = RBD_EXCLUSIVE_DEFAULT;
+ rbd_opts->trim = RBD_TRIM_DEFAULT;
copts = ceph_parse_options(options, mon_addrs,
mon_addrs + mon_addrs_size - 1,
@@ -5662,39 +5241,6 @@ out_err:
return ret;
}
-/*
- * Return pool id (>= 0) or a negative error code.
- */
-static int rbd_add_get_pool_id(struct rbd_client *rbdc, const char *pool_name)
-{
- struct ceph_options *opts = rbdc->client->options;
- u64 newest_epoch;
- int tries = 0;
- int ret;
-
-again:
- ret = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, pool_name);
- if (ret == -ENOENT && tries++ < 1) {
- ret = ceph_monc_get_version(&rbdc->client->monc, "osdmap",
- &newest_epoch);
- if (ret < 0)
- return ret;
-
- if (rbdc->client->osdc.osdmap->epoch < newest_epoch) {
- ceph_osdc_maybe_request_map(&rbdc->client->osdc);
- (void) ceph_monc_wait_osdmap(&rbdc->client->monc,
- newest_epoch,
- opts->mount_timeout);
- goto again;
- } else {
- /* the osdmap we have is new enough */
- return -ENOENT;
- }
- }
-
- return ret;
-}
-
static void rbd_dev_image_unlock(struct rbd_device *rbd_dev)
{
down_write(&rbd_dev->lock_rwsem);
@@ -5705,6 +5251,8 @@ static void rbd_dev_image_unlock(struct rbd_device *rbd_dev)
static int rbd_add_acquire_lock(struct rbd_device *rbd_dev)
{
+ int ret;
+
if (!(rbd_dev->header.features & RBD_FEATURE_EXCLUSIVE_LOCK)) {
rbd_warn(rbd_dev, "exclusive-lock feature is not enabled");
return -EINVAL;
@@ -5712,9 +5260,9 @@ static int rbd_add_acquire_lock(struct rbd_device *rbd_dev)
/* FIXME: "rbd map --exclusive" should be in interruptible */
down_read(&rbd_dev->lock_rwsem);
- rbd_wait_state_locked(rbd_dev);
+ ret = rbd_wait_state_locked(rbd_dev, true);
up_read(&rbd_dev->lock_rwsem);
- if (test_bit(RBD_DEV_FLAG_BLACKLISTED, &rbd_dev->flags)) {
+ if (ret) {
rbd_warn(rbd_dev, "failed to acquire exclusive lock");
return -EROFS;
}
@@ -6123,7 +5671,7 @@ static ssize_t do_rbd_add(struct bus_type *bus,
}
/* pick the pool */
- rc = rbd_add_get_pool_id(rbdc, spec->pool_name);
+ rc = ceph_pg_poolid_by_name(rbdc->client->osdc.osdmap, spec->pool_name);
if (rc < 0) {
if (rc == -ENOENT)
pr_info("pool %s does not exist\n", spec->pool_name);
@@ -6375,16 +5923,8 @@ static int rbd_slab_init(void)
if (!rbd_obj_request_cache)
goto out_err;
- rbd_assert(!rbd_bio_clone);
- rbd_bio_clone = bioset_create(BIO_POOL_SIZE, 0, 0);
- if (!rbd_bio_clone)
- goto out_err_clone;
-
return 0;
-out_err_clone:
- kmem_cache_destroy(rbd_obj_request_cache);
- rbd_obj_request_cache = NULL;
out_err:
kmem_cache_destroy(rbd_img_request_cache);
rbd_img_request_cache = NULL;
@@ -6400,10 +5940,6 @@ static void rbd_slab_exit(void)
rbd_assert(rbd_img_request_cache);
kmem_cache_destroy(rbd_img_request_cache);
rbd_img_request_cache = NULL;
-
- rbd_assert(rbd_bio_clone);
- bioset_free(rbd_bio_clone);
- rbd_bio_clone = NULL;
}
static int __init rbd_init(void)
diff --git a/drivers/block/rsxx/dev.c b/drivers/block/rsxx/dev.c
index e397d3ee7308..dddb3f2490b6 100644
--- a/drivers/block/rsxx/dev.c
+++ b/drivers/block/rsxx/dev.c
@@ -287,10 +287,10 @@ int rsxx_setup_dev(struct rsxx_cardinfo *card)
blk_queue_max_hw_sectors(card->queue, blkdev_max_hw_sectors);
blk_queue_physical_block_size(card->queue, RSXX_HW_BLK_SIZE);
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, card->queue);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, card->queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, card->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, card->queue);
if (rsxx_discard_supported(card)) {
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, card->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, card->queue);
blk_queue_max_discard_sectors(card->queue,
RSXX_HW_BLK_SIZE >> 9);
card->queue->limits.discard_granularity = RSXX_HW_BLK_SIZE;
diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c
index e41935ab41ef..bc7aea6d7b7c 100644
--- a/drivers/block/skd_main.c
+++ b/drivers/block/skd_main.c
@@ -2858,8 +2858,8 @@ static int skd_cons_disk(struct skd_device *skdev)
/* set optimal I/O size to 8KB */
blk_queue_io_opt(q, 8192);
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, q);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, q);
blk_queue_rq_timeout(q, 8 * HZ);
diff --git a/drivers/block/swim.c b/drivers/block/swim.c
index 64e066eba72e..0e31884a9519 100644
--- a/drivers/block/swim.c
+++ b/drivers/block/swim.c
@@ -110,7 +110,7 @@ struct iwm {
/* Select values for swim_select and swim_readbit */
#define READ_DATA_0 0x074
-#define TWOMEG_DRIVE 0x075
+#define ONEMEG_DRIVE 0x075
#define SINGLE_SIDED 0x076
#define DRIVE_PRESENT 0x077
#define DISK_IN 0x170
@@ -118,9 +118,9 @@ struct iwm {
#define TRACK_ZERO 0x172
#define TACHO 0x173
#define READ_DATA_1 0x174
-#define MFM_MODE 0x175
+#define GCR_MODE 0x175
#define SEEK_COMPLETE 0x176
-#define ONEMEG_MEDIA 0x177
+#define TWOMEG_MEDIA 0x177
/* Bits in handshake register */
@@ -612,7 +612,6 @@ static void setup_medium(struct floppy_state *fs)
struct floppy_struct *g;
fs->disk_in = 1;
fs->write_protected = swim_readbit(base, WRITE_PROT);
- fs->type = swim_readbit(base, ONEMEG_MEDIA);
if (swim_track00(base))
printk(KERN_ERR
@@ -620,6 +619,9 @@ static void setup_medium(struct floppy_state *fs)
swim_track00(base);
+ fs->type = swim_readbit(base, TWOMEG_MEDIA) ?
+ HD_MEDIA : DD_MEDIA;
+ fs->head_number = swim_readbit(base, SINGLE_SIDED) ? 1 : 2;
get_floppy_geometry(fs, 0, &g);
fs->total_secs = g->size;
fs->secpercyl = g->head * g->sect;
@@ -646,7 +648,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
swim_write(base, setup, S_IBM_DRIVE | S_FCLK_DIV2);
udelay(10);
- swim_drive(base, INTERNAL_DRIVE);
+ swim_drive(base, fs->location);
swim_motor(base, ON);
swim_action(base, SETMFM);
if (fs->ejected)
@@ -656,6 +658,8 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
goto out;
}
+ set_capacity(fs->disk, fs->total_secs);
+
if (mode & FMODE_NDELAY)
return 0;
@@ -727,14 +731,9 @@ static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
if (copy_to_user((void __user *) param, (void *) &floppy_type,
sizeof(struct floppy_struct)))
return -EFAULT;
- break;
-
- default:
- printk(KERN_DEBUG "SWIM floppy_ioctl: unknown cmd %d\n",
- cmd);
- return -ENOSYS;
+ return 0;
}
- return 0;
+ return -ENOTTY;
}
static int floppy_getgeo(struct block_device *bdev, struct hd_geometry *geo)
@@ -795,7 +794,7 @@ static struct kobject *floppy_find(dev_t dev, int *part, void *data)
struct swim_priv *swd = data;
int drive = (*part & 3);
- if (drive > swd->floppy_count)
+ if (drive >= swd->floppy_count)
return NULL;
*part = 0;
@@ -813,10 +812,9 @@ static int swim_add_floppy(struct swim_priv *swd, enum drive_location location)
swim_motor(base, OFF);
- if (swim_readbit(base, SINGLE_SIDED))
- fs->head_number = 1;
- else
- fs->head_number = 2;
+ fs->type = HD_MEDIA;
+ fs->head_number = 2;
+
fs->ref_count = 0;
fs->ejected = 1;
@@ -834,10 +832,12 @@ static int swim_floppy_init(struct swim_priv *swd)
/* scan floppy drives */
swim_drive(base, INTERNAL_DRIVE);
- if (swim_readbit(base, DRIVE_PRESENT))
+ if (swim_readbit(base, DRIVE_PRESENT) &&
+ !swim_readbit(base, ONEMEG_DRIVE))
swim_add_floppy(swd, INTERNAL_DRIVE);
swim_drive(base, EXTERNAL_DRIVE);
- if (swim_readbit(base, DRIVE_PRESENT))
+ if (swim_readbit(base, DRIVE_PRESENT) &&
+ !swim_readbit(base, ONEMEG_DRIVE))
swim_add_floppy(swd, EXTERNAL_DRIVE);
/* register floppy drives */
@@ -861,7 +861,6 @@ static int swim_floppy_init(struct swim_priv *swd)
&swd->lock);
if (!swd->unit[drive].disk->queue) {
err = -ENOMEM;
- put_disk(swd->unit[drive].disk);
goto exit_put_disks;
}
blk_queue_bounce_limit(swd->unit[drive].disk->queue,
@@ -911,7 +910,7 @@ static int swim_probe(struct platform_device *dev)
goto out;
}
- swim_base = ioremap(res->start, resource_size(res));
+ swim_base = (struct swim __iomem *)res->start;
if (!swim_base) {
ret = -ENOMEM;
goto out_release_io;
@@ -923,7 +922,7 @@ static int swim_probe(struct platform_device *dev)
if (!get_swim_mode(swim_base)) {
printk(KERN_INFO "SWIM device not found !\n");
ret = -ENODEV;
- goto out_iounmap;
+ goto out_release_io;
}
/* set platform driver data */
@@ -931,7 +930,7 @@ static int swim_probe(struct platform_device *dev)
swd = kzalloc(sizeof(struct swim_priv), GFP_KERNEL);
if (!swd) {
ret = -ENOMEM;
- goto out_iounmap;
+ goto out_release_io;
}
platform_set_drvdata(dev, swd);
@@ -945,8 +944,6 @@ static int swim_probe(struct platform_device *dev)
out_kfree:
kfree(swd);
-out_iounmap:
- iounmap(swim_base);
out_release_io:
release_mem_region(res->start, resource_size(res));
out:
@@ -974,8 +971,6 @@ static int swim_remove(struct platform_device *dev)
for (drive = 0; drive < swd->floppy_count; drive++)
floppy_eject(&swd->unit[drive]);
- iounmap(swd->base);
-
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (res)
release_mem_region(res->start, resource_size(res));
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index af51015d056e..469541c1e51e 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -148,7 +148,7 @@ struct swim3 {
#define MOTOR_ON 2
#define RELAX 3 /* also eject in progress */
#define READ_DATA_0 4
-#define TWOMEG_DRIVE 5
+#define ONEMEG_DRIVE 5
#define SINGLE_SIDED 6 /* drive or diskette is 4MB type? */
#define DRIVE_PRESENT 7
#define DISK_IN 8
@@ -156,9 +156,9 @@ struct swim3 {
#define TRACK_ZERO 10
#define TACHO 11
#define READ_DATA_1 12
-#define MFM_MODE 13
+#define GCR_MODE 13
#define SEEK_COMPLETE 14
-#define ONEMEG_MEDIA 15
+#define TWOMEG_MEDIA 15
/* Definitions of values used in writing and formatting */
#define DATA_ESCAPE 0x99
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index 8077123678ad..5c7fb8cc4149 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -888,13 +888,14 @@ static int mm_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
card->Active = -1; /* no page is active */
card->bio = NULL;
card->biotail = &card->bio;
+ spin_lock_init(&card->lock);
- card->queue = blk_alloc_queue(GFP_KERNEL);
+ card->queue = blk_alloc_queue_node(GFP_KERNEL, NUMA_NO_NODE,
+ &card->lock);
if (!card->queue)
goto failed_alloc;
blk_queue_make_request(card->queue, mm_make_request);
- card->queue->queue_lock = &card->lock;
card->queue->queuedata = card;
tasklet_init(&card->tasklet, process_page, (unsigned long)card);
@@ -968,8 +969,6 @@ static int mm_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
dev_printk(KERN_INFO, &card->dev->dev,
"Window size %d bytes, IRQ %d\n", data, dev->irq);
- spin_lock_init(&card->lock);
-
pci_set_drvdata(dev, card);
if (pci_write_cmd != 0x0F) /* If not Memory Write & Invalidate */
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 92ec1bbece51..2a8e7813bd1a 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -932,15 +932,15 @@ static void blkif_set_queue_limits(struct blkfront_info *info)
unsigned int segments = info->max_indirect_segments ? :
BLKIF_MAX_SEGMENTS_PER_REQUEST;
- queue_flag_set_unlocked(QUEUE_FLAG_VIRT, rq);
+ blk_queue_flag_set(QUEUE_FLAG_VIRT, rq);
if (info->feature_discard) {
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, rq);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, rq);
blk_queue_max_discard_sectors(rq, get_capacity(gd));
rq->limits.discard_granularity = info->discard_granularity;
rq->limits.discard_alignment = info->discard_alignment;
if (info->feature_secdiscard)
- queue_flag_set_unlocked(QUEUE_FLAG_SECERASE, rq);
+ blk_queue_flag_set(QUEUE_FLAG_SECERASE, rq);
}
/* Hard sector size and max sectors impersonate the equiv. hardware. */
@@ -1611,8 +1611,8 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id)
blkif_req(req)->error = BLK_STS_NOTSUPP;
info->feature_discard = 0;
info->feature_secdiscard = 0;
- queue_flag_clear(QUEUE_FLAG_DISCARD, rq);
- queue_flag_clear(QUEUE_FLAG_SECERASE, rq);
+ blk_queue_flag_clear(QUEUE_FLAG_DISCARD, rq);
+ blk_queue_flag_clear(QUEUE_FLAG_SECERASE, rq);
}
break;
case BLKIF_OP_FLUSH_DISKCACHE:
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 0afa6c8c3857..0f3fadd71230 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -44,6 +44,11 @@ static const char *default_compressor = "lzo";
/* Module params (documentation at end) */
static unsigned int num_devices = 1;
+/*
+ * Pages that compress to sizes equals or greater than this are stored
+ * uncompressed in memory.
+ */
+static size_t huge_class_size;
static void zram_free_page(struct zram *zram, size_t index);
@@ -786,6 +791,8 @@ static bool zram_meta_alloc(struct zram *zram, u64 disksize)
return false;
}
+ if (!huge_class_size)
+ huge_class_size = zs_huge_class_size(zram->mem_pool);
return true;
}
@@ -965,7 +972,7 @@ compress_again:
return ret;
}
- if (unlikely(comp_len > max_zpage_size)) {
+ if (unlikely(comp_len >= huge_class_size)) {
if (zram_wb_enabled(zram) && allow_wb) {
zcomp_stream_put(zram->comp);
ret = write_to_bdev(zram, bvec, index, bio, &element);
@@ -1530,8 +1537,8 @@ static int zram_add(void)
/* Actual capacity set using syfs (/sys/block/zram<id>/disksize */
set_capacity(zram->disk, 0);
/* zram devices sort of resembles non-rotational disks */
- queue_flag_set_unlocked(QUEUE_FLAG_NONROT, zram->disk->queue);
- queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue);
+ blk_queue_flag_set(QUEUE_FLAG_NONROT, zram->disk->queue);
+ blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue);
/*
* To ensure that we always get PAGE_SIZE aligned
@@ -1544,7 +1551,7 @@ static int zram_add(void)
blk_queue_io_opt(zram->disk->queue, PAGE_SIZE);
zram->disk->queue->limits.discard_granularity = PAGE_SIZE;
blk_queue_max_discard_sectors(zram->disk->queue, UINT_MAX);
- queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, zram->disk->queue);
+ blk_queue_flag_set(QUEUE_FLAG_DISCARD, zram->disk->queue);
/*
* zram_bio_discard() will clear all logical blocks if logical block
@@ -1620,8 +1627,8 @@ static int zram_remove(struct zram *zram)
pr_info("Removed device: %s\n", zram->disk->disk_name);
- blk_cleanup_queue(zram->disk->queue);
del_gendisk(zram->disk);
+ blk_cleanup_queue(zram->disk->queue);
put_disk(zram->disk);
kfree(zram);
return 0;
diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h
index 31762db861e3..008861220723 100644
--- a/drivers/block/zram/zram_drv.h
+++ b/drivers/block/zram/zram_drv.h
@@ -21,23 +21,6 @@
#include "zcomp.h"
-/*-- Configurable parameters */
-
-/*
- * Pages that compress to size greater than this are stored
- * uncompressed in memory.
- */
-static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
-
-/*
- * NOTE: max_zpage_size must be less than or equal to:
- * ZS_MAX_ALLOC_SIZE. Otherwise, zs_malloc() would
- * always return failure.
- */
-
-/*-- End of configurable params */
-
-#define SECTOR_SHIFT 9
#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT)
#define ZRAM_LOGICAL_BLOCK_SHIFT 12