10
10
11
11
#include "blk.h"
12
12
13
- /* Keeps track of all outstanding copy IO */
14
- struct blkdev_copy_io {
15
- atomic_t refcount ;
16
- ssize_t copied ;
17
- int status ;
18
- struct task_struct * waiter ;
19
- void (* endio )(void * private , int status , ssize_t copied );
20
- void * private ;
21
- };
22
-
23
- /* Keeps track of single outstanding copy offload IO */
24
- struct blkdev_copy_offload_io {
25
- struct blkdev_copy_io * cio ;
26
- loff_t offset ;
27
- };
28
-
29
13
static sector_t bio_discard_limit (struct block_device * bdev , sector_t sector )
30
14
{
31
15
unsigned int discard_granularity = bdev_discard_granularity (bdev );
@@ -180,14 +164,17 @@ static void blkdev_copy_offload_src_endio(struct bio *bio)
180
164
cio -> status = blk_status_to_errno (bio -> bi_status );
181
165
}
182
166
bio_put (bio );
167
+ if (offload_io -> dst_bio )
168
+ bio_put (offload_io -> dst_bio );
169
+
183
170
kfree (offload_io );
184
171
185
172
if (atomic_dec_and_test (& cio -> refcount ))
186
173
blkdev_copy_endio (cio );
187
174
}
188
175
189
176
/*
190
- * @bdev: block device
177
+ * @bdev: source block device
191
178
* @pos_in: source offset
192
179
* @pos_out: destination offset
193
180
* @len: length in bytes to be copied
@@ -196,6 +183,7 @@ static void blkdev_copy_offload_src_endio(struct bio *bio)
196
183
* @private: endio function will be called with this private data,
197
184
* for synchronous operation this should be NULL
198
185
* @gfp_mask: memory allocation flags (for bio_alloc)
186
+ * @bdev_out: destination block device
199
187
*
200
188
* For synchronous operation returns the length of bytes copied or error
201
189
* For asynchronous operation returns -EIOCBQUEUED or error
@@ -216,20 +204,38 @@ static void blkdev_copy_offload_src_endio(struct bio *bio)
216
204
ssize_t blkdev_copy_offload (struct block_device * bdev , loff_t pos_in ,
217
205
loff_t pos_out , size_t len ,
218
206
void (* endio )(void * , int , ssize_t ),
219
- void * private , gfp_t gfp )
207
+ void * private , gfp_t gfp , struct block_device * bdev_out )
220
208
{
221
209
struct blkdev_copy_io * cio ;
222
210
struct blkdev_copy_offload_io * offload_io ;
223
211
struct bio * src_bio , * dst_bio ;
224
212
size_t rem , chunk ;
225
- size_t max_copy_bytes = bdev_max_copy_sectors (bdev ) << SECTOR_SHIFT ;
226
213
ssize_t ret ;
227
214
struct blk_plug plug ;
215
+ int is_mq = 0 ;
216
+ size_t max_copy_bytes = min (bdev_max_copy_sectors (bdev ) << SECTOR_SHIFT ,
217
+ bdev_max_copy_sectors (bdev_out ) << SECTOR_SHIFT );
228
218
229
219
if (!max_copy_bytes )
230
220
return - EOPNOTSUPP ;
231
221
232
- ret = blkdev_copy_sanity_check (bdev , pos_in , bdev , pos_out , len );
222
+ if (queue_is_mq (bdev -> bd_queue )) {
223
+ if (bdev -> bd_queue -> mq_ops != bdev_out -> bd_queue -> mq_ops )
224
+ return - EOPNOTSUPP ;
225
+ is_mq = 1 ;
226
+ } else if (!bdev -> bd_disk -> fops -> submit_bio ||
227
+ bdev -> bd_disk -> fops -> submit_bio != bdev_out -> bd_disk -> fops -> submit_bio ) {
228
+ return - EOPNOTSUPP ;
229
+ }
230
+
231
+ /*
232
+ * Cross device copy only supported for zvols
233
+ */
234
+ if (strncmp (bdev_out -> bd_disk -> disk_name , "zd" , 2 ))
235
+ return - EOPNOTSUPP ;
236
+
237
+ ret = blkdev_copy_sanity_check (bdev , pos_in , bdev_out , pos_out , len );
238
+
233
239
if (ret )
234
240
return ret ;
235
241
@@ -258,25 +264,33 @@ ssize_t blkdev_copy_offload(struct block_device *bdev, loff_t pos_in,
258
264
* successful copy length
259
265
*/
260
266
offload_io -> offset = len - rem ;
267
+ if (bdev_out )
268
+ offload_io -> driver_private = bdev_out -> bd_queue -> queuedata ;
261
269
262
270
dst_bio = bio_alloc (bdev , 0 , REQ_OP_COPY_DST , gfp );
263
271
if (!dst_bio )
264
272
goto err_free_offload_io ;
265
273
dst_bio -> bi_iter .bi_size = chunk ;
266
274
dst_bio -> bi_iter .bi_sector = pos_out >> SECTOR_SHIFT ;
267
275
268
- blk_start_plug (& plug );
269
- src_bio = blk_next_bio (dst_bio , bdev , 0 , REQ_OP_COPY_SRC , gfp );
276
+ if (is_mq ) {
277
+ blk_start_plug (& plug );
278
+ src_bio = blk_next_bio (dst_bio , bdev , 0 , REQ_OP_COPY_SRC , gfp );
279
+ } else {
280
+ src_bio = bio_alloc (bdev , 0 , REQ_OP_COPY_SRC , gfp );
281
+ }
270
282
if (!src_bio )
271
283
goto err_free_dst_bio ;
272
284
src_bio -> bi_iter .bi_size = chunk ;
273
285
src_bio -> bi_iter .bi_sector = pos_in >> SECTOR_SHIFT ;
274
286
src_bio -> bi_end_io = blkdev_copy_offload_src_endio ;
275
287
src_bio -> bi_private = offload_io ;
288
+ offload_io -> dst_bio = (is_mq ) ? NULL : dst_bio ;
276
289
277
290
atomic_inc (& cio -> refcount );
278
291
submit_bio (src_bio );
279
- blk_finish_plug (& plug );
292
+ if (is_mq )
293
+ blk_finish_plug (& plug );
280
294
pos_in += chunk ;
281
295
pos_out += chunk ;
282
296
}
@@ -289,6 +303,8 @@ ssize_t blkdev_copy_offload(struct block_device *bdev, loff_t pos_in,
289
303
return blkdev_copy_wait_for_completion_io (cio );
290
304
291
305
err_free_dst_bio :
306
+ if (is_mq )
307
+ blk_finish_plug (& plug );
292
308
bio_put (dst_bio );
293
309
err_free_offload_io :
294
310
kfree (offload_io );
0 commit comments