/* The partition capacity referenced by the block device */
if (part)
- return part->nr_sects;
+ return (part->nr_sects << 9);
/* Otherwise assume the full device capacity */
- return get_capacity(bdev->bd_disk);
+ return (get_capacity(bdev->bd_disk) << 9);
}
static void
return (error);
}
+/*
+ * Expanding a whole disk vdev involves invoking BLKRRPART on the
+ * whole disk device. This poses a problem, because BLKRRPART will
+ * return EBUSY if one of the disk's partitions is open. That's why
+ * we have to do it here, just before opening the data partition.
+ * Unfortunately, BLKRRPART works by dropping all partitions and
+ * recreating them, which means that for a short time window, all
+ * /dev/sdxN device files disappear (until udev recreates them).
+ * This means two things:
+ * - When we open the data partition just after a BLKRRPART, we
+ * can't do it using the normal device file path because of the
+ * obvious race condition with udev. Instead, we use reliable
+ * kernel APIs to get a handle to the new partition device from
+ * the whole disk device.
+ * - Because vdev_disk_open() initially needs to find the device
+ * using its path, multiple vdev_disk_open() invocations in
+ * short succession on the same disk with BLKRRPARTs in the
+ * middle have a high probability of failure (because of the
+ * race condition with udev). A typical situation where this
+ * might happen is when the zpool userspace tool does a
+ * TRYIMPORT immediately followed by an IMPORT. For this
+ * reason, we only invoke BLKRRPART in the module when strictly
+ * necessary (zpool online -e case), and rely on userspace to
+ * do it when possible.
+ */
+static struct block_device *
+vdev_disk_rrpart(const char *path, int mode, vdev_disk_t *vd)
+{
+#if defined(HAVE_3ARG_BLKDEV_GET) && defined(HAVE_GET_GENDISK)
+ struct block_device *bdev, *result = ERR_PTR(-ENXIO);
+ struct gendisk *disk;
+ int error, partno;
+
+ bdev = vdev_bdev_open(path, vdev_bdev_mode(mode), vd);
+ if (IS_ERR(bdev))
+ return bdev;
+
+ disk = get_gendisk(bdev->bd_dev, &partno);
+ vdev_bdev_close(bdev, vdev_bdev_mode(mode));
+
+ if (disk) {
+ bdev = bdget(disk_devt(disk));
+ if (bdev) {
+ error = blkdev_get(bdev, vdev_bdev_mode(mode), vd);
+ if (error == 0)
+ error = ioctl_by_bdev(bdev, BLKRRPART, 0);
+ vdev_bdev_close(bdev, vdev_bdev_mode(mode));
+ }
+
+ bdev = bdget_disk(disk, partno);
+ if (bdev) {
+ error = blkdev_get(bdev,
+ vdev_bdev_mode(mode) | FMODE_EXCL, vd);
+ if (error == 0)
+ result = bdev;
+ }
+ put_disk(disk);
+ }
+
+ return result;
+#else
+ return ERR_PTR(-EOPNOTSUPP);
+#endif /* defined(HAVE_3ARG_BLKDEV_GET) && defined(HAVE_GET_GENDISK) */
+}
+
static int
-vdev_disk_open(vdev_t *v, uint64_t *psize, uint64_t *ashift)
+vdev_disk_open(vdev_t *v, uint64_t *psize, uint64_t *max_psize,
+ uint64_t *ashift)
{
- struct block_device *bdev;
+ struct block_device *bdev = ERR_PTR(-ENXIO);
vdev_disk_t *vd;
int mode, block_size;
return EINVAL;
}
- vd = kmem_zalloc(sizeof(vdev_disk_t), KM_SLEEP);
+ vd = kmem_zalloc(sizeof(vdev_disk_t), KM_PUSHPAGE);
if (vd == NULL)
return ENOMEM;
* level vdev validation.
*/
mode = spa_mode(v->vdev_spa);
- bdev = vdev_bdev_open(v->vdev_path, vdev_bdev_mode(mode), vd);
+ if (v->vdev_wholedisk && v->vdev_expanding)
+ bdev = vdev_disk_rrpart(v->vdev_path, mode, vd);
+ if (IS_ERR(bdev))
+ bdev = vdev_bdev_open(v->vdev_path, vdev_bdev_mode(mode), vd);
if (IS_ERR(bdev)) {
kmem_free(vd, sizeof(vdev_disk_t));
return -PTR_ERR(bdev);
v->vdev_nowritecache = B_FALSE;
/* Physical volume size in bytes */
- *psize = bdev_capacity(bdev) * block_size;
+ *psize = bdev_capacity(bdev);
+
+ /* TODO: report possible expansion size */
+ *max_psize = *psize;
/* Based on the minimum sector size set the block size */
*ashift = highbit(MAX(block_size, SPA_MINBLOCKSIZE)) - 1;
int i;
dr = kmem_zalloc(sizeof(dio_request_t) +
- sizeof(struct bio *) * bio_count, KM_SLEEP);
+ sizeof(struct bio *) * bio_count, KM_PUSHPAGE);
if (dr) {
init_completion(&dr->dr_comp);
atomic_set(&dr->dr_ref, 0);
caddr_t bio_ptr;
uint64_t bio_offset;
int bio_size, bio_count = 16;
- int i = 0, error = 0, block_size;
+ int i = 0, error = 0;
ASSERT3U(kbuf_offset + kbuf_size, <=, bdev->bd_inode->i_size);
dr->dr_zio = zio;
dr->dr_rw = flags;
- block_size = vdev_bdev_block_size(bdev);
/*
* When the IO size exceeds the maximum bio size for the request
vdev_disk_dio_get(dr);
dr->dr_bio[i]->bi_bdev = bdev;
- dr->dr_bio[i]->bi_sector = bio_offset / block_size;
+ dr->dr_bio[i]->bi_sector = bio_offset >> 9;
dr->dr_bio[i]->bi_rw = dr->dr_rw;
dr->dr_bio[i]->bi_end_io = vdev_disk_physio_completion;
dr->dr_bio[i]->bi_private = dr;
bio->bi_private = zio;
bio->bi_bdev = bdev;
zio->io_delay = jiffies_64;
- submit_bio(WRITE_BARRIER, bio);
+ submit_bio(VDEV_WRITE_FLUSH_FUA, bio);
return 0;
}
if (IS_ERR(bdev))
return -PTR_ERR(bdev);
- s = bdev_capacity(bdev) * vdev_bdev_block_size(bdev);
+ s = bdev_capacity(bdev);
if (s == 0) {
vdev_bdev_close(bdev, vdev_bdev_mode(FREAD));
return EIO;
}
size = P2ALIGN_TYPED(s, sizeof(vdev_label_t), uint64_t);
- label = vmem_alloc(sizeof(vdev_label_t), KM_SLEEP);
+ label = vmem_alloc(sizeof(vdev_label_t), KM_PUSHPAGE);
for (i = 0; i < VDEV_LABELS; i++) {
uint64_t offset, state, txg = 0;