Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 1 | /* |
| 2 | * linux/fs/nfs/blocklayout/blocklayoutdev.c |
| 3 | * |
| 4 | * Device operations for the pnfs nfs4 file layout driver. |
| 5 | * |
| 6 | * Copyright (c) 2006 The Regents of the University of Michigan. |
| 7 | * All rights reserved. |
| 8 | * |
| 9 | * Andy Adamson <andros@citi.umich.edu> |
| 10 | * Fred Isaman <iisaman@umich.edu> |
| 11 | * |
| 12 | * permission is granted to use, copy, create derivative works and |
| 13 | * redistribute this software and such derivative works for any purpose, |
| 14 | * so long as the name of the university of michigan is not used in |
| 15 | * any advertising or publicity pertaining to the use or distribution |
| 16 | * of this software without specific, written prior authorization. if |
| 17 | * the above copyright notice or any other identification of the |
| 18 | * university of michigan is included in any copy of any portion of |
| 19 | * this software, then the disclaimer below must also be included. |
| 20 | * |
| 21 | * this software is provided as is, without representation from the |
| 22 | * university of michigan as to its fitness for any purpose, and without |
| 23 | * warranty by the university of michigan of any kind, either express |
| 24 | * or implied, including without limitation the implied warranties of |
| 25 | * merchantability and fitness for a particular purpose. the regents |
| 26 | * of the university of michigan shall not be liable for any damages, |
| 27 | * including special, indirect, incidental, or consequential damages, |
| 28 | * with respect to any claim arising out or in connection with the use |
| 29 | * of the software, even if it has been or is hereafter advised of the |
| 30 | * possibility of such damages. |
| 31 | */ |
| 32 | #include <linux/module.h> |
| 33 | #include <linux/buffer_head.h> /* __bread */ |
| 34 | |
| 35 | #include <linux/genhd.h> |
| 36 | #include <linux/blkdev.h> |
| 37 | #include <linux/hash.h> |
| 38 | |
| 39 | #include "blocklayout.h" |
| 40 | |
| 41 | #define NFSDBG_FACILITY NFSDBG_PNFS_LD |
| 42 | |
Fred Isaman | e9437cc | 2011-07-30 20:52:47 -0400 | [diff] [blame] | 43 | static int decode_sector_number(__be32 **rp, sector_t *sp) |
| 44 | { |
| 45 | uint64_t s; |
| 46 | |
| 47 | *rp = xdr_decode_hyper(*rp, &s); |
| 48 | if (s & 0x1ff) { |
Weston Andros Adamson | a030889 | 2012-01-26 13:32:23 -0500 | [diff] [blame] | 49 | printk(KERN_WARNING "NFS: %s: sector not aligned\n", __func__); |
Fred Isaman | e9437cc | 2011-07-30 20:52:47 -0400 | [diff] [blame] | 50 | return -1; |
| 51 | } |
| 52 | *sp = s >> SECTOR_SHIFT; |
| 53 | return 0; |
| 54 | } |
| 55 | |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 56 | /* Open a block_device by device number. */ |
| 57 | struct block_device *nfs4_blkdev_get(dev_t dev) |
| 58 | { |
| 59 | struct block_device *bd; |
| 60 | |
| 61 | dprintk("%s enter\n", __func__); |
| 62 | bd = blkdev_get_by_dev(dev, FMODE_READ, NULL); |
| 63 | if (IS_ERR(bd)) |
| 64 | goto fail; |
| 65 | return bd; |
| 66 | fail: |
| 67 | dprintk("%s failed to open device : %ld\n", |
| 68 | __func__, PTR_ERR(bd)); |
| 69 | return NULL; |
| 70 | } |
| 71 | |
| 72 | /* |
| 73 | * Release the block device |
| 74 | */ |
| 75 | int nfs4_blkdev_put(struct block_device *bdev) |
| 76 | { |
| 77 | dprintk("%s for device %d:%d\n", __func__, MAJOR(bdev->bd_dev), |
| 78 | MINOR(bdev->bd_dev)); |
| 79 | return blkdev_put(bdev, FMODE_READ); |
| 80 | } |
| 81 | |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 82 | ssize_t bl_pipe_downcall(struct file *filp, const char __user *src, |
| 83 | size_t mlen) |
| 84 | { |
Stanislav Kinsbursky | cb9c1c4 | 2012-03-11 18:20:23 +0400 | [diff] [blame] | 85 | struct nfs_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info, |
| 86 | nfs_net_id); |
| 87 | |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 88 | if (mlen != sizeof (struct bl_dev_msg)) |
| 89 | return -EINVAL; |
| 90 | |
Stanislav Kinsbursky | cb9c1c4 | 2012-03-11 18:20:23 +0400 | [diff] [blame] | 91 | if (copy_from_user(&nn->bl_mount_reply, src, mlen) != 0) |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 92 | return -EFAULT; |
| 93 | |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 94 | wake_up(&nn->bl_wq); |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 95 | |
| 96 | return mlen; |
| 97 | } |
| 98 | |
| 99 | void bl_pipe_destroy_msg(struct rpc_pipe_msg *msg) |
| 100 | { |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 101 | struct bl_pipe_msg *bl_pipe_msg = container_of(msg, struct bl_pipe_msg, msg); |
| 102 | |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 103 | if (msg->errno >= 0) |
| 104 | return; |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 105 | wake_up(bl_pipe_msg->bl_wq); |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 106 | } |
| 107 | |
| 108 | /* |
| 109 | * Decodes pnfs_block_deviceaddr4 which is XDR encoded in dev->dev_addr_buf. |
| 110 | */ |
| 111 | struct pnfs_block_dev * |
| 112 | nfs4_blk_decode_device(struct nfs_server *server, |
Fred Isaman | 2f9fd18 | 2011-07-30 20:52:46 -0400 | [diff] [blame] | 113 | struct pnfs_device *dev) |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 114 | { |
Jim Rees | 516f2e2 | 2011-09-22 21:50:08 -0400 | [diff] [blame] | 115 | struct pnfs_block_dev *rv; |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 116 | struct block_device *bd = NULL; |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 117 | struct bl_pipe_msg bl_pipe_msg; |
| 118 | struct rpc_pipe_msg *msg = &bl_pipe_msg.msg; |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 119 | struct bl_msg_hdr bl_msg = { |
| 120 | .type = BL_DEVICE_MOUNT, |
| 121 | .totallen = dev->mincount, |
| 122 | }; |
| 123 | uint8_t *dataptr; |
| 124 | DECLARE_WAITQUEUE(wq, current); |
Jim Rees | 516f2e2 | 2011-09-22 21:50:08 -0400 | [diff] [blame] | 125 | int offset, len, i, rc; |
Chuck Lever | 73ea666 | 2012-05-21 22:44:50 -0400 | [diff] [blame] | 126 | struct net *net = server->nfs_client->cl_net; |
Stanislav Kinsbursky | 9e2e74d | 2012-01-10 17:04:24 +0400 | [diff] [blame] | 127 | struct nfs_net *nn = net_generic(net, nfs_net_id); |
Stanislav Kinsbursky | cb9c1c4 | 2012-03-11 18:20:23 +0400 | [diff] [blame] | 128 | struct bl_dev_msg *reply = &nn->bl_mount_reply; |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 129 | |
| 130 | dprintk("%s CREATING PIPEFS MESSAGE\n", __func__); |
| 131 | dprintk("%s: deviceid: %s, mincount: %d\n", __func__, dev->dev_id.data, |
| 132 | dev->mincount); |
| 133 | |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 134 | bl_pipe_msg.bl_wq = &nn->bl_wq; |
| 135 | memset(msg, 0, sizeof(*msg)); |
| 136 | msg->data = kzalloc(sizeof(bl_msg) + dev->mincount, GFP_NOFS); |
| 137 | if (!msg->data) { |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 138 | rv = ERR_PTR(-ENOMEM); |
| 139 | goto out; |
| 140 | } |
| 141 | |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 142 | memcpy(msg->data, &bl_msg, sizeof(bl_msg)); |
| 143 | dataptr = (uint8_t *) msg->data; |
Fred Isaman | 2f9fd18 | 2011-07-30 20:52:46 -0400 | [diff] [blame] | 144 | len = dev->mincount; |
| 145 | offset = sizeof(bl_msg); |
| 146 | for (i = 0; len > 0; i++) { |
| 147 | memcpy(&dataptr[offset], page_address(dev->pages[i]), |
| 148 | len < PAGE_CACHE_SIZE ? len : PAGE_CACHE_SIZE); |
| 149 | len -= PAGE_CACHE_SIZE; |
| 150 | offset += PAGE_CACHE_SIZE; |
| 151 | } |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 152 | msg->len = sizeof(bl_msg) + dev->mincount; |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 153 | |
| 154 | dprintk("%s CALLING USERSPACE DAEMON\n", __func__); |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 155 | add_wait_queue(&nn->bl_wq, &wq); |
| 156 | rc = rpc_queue_upcall(nn->bl_device_pipe, msg); |
Jim Rees | 516f2e2 | 2011-09-22 21:50:08 -0400 | [diff] [blame] | 157 | if (rc < 0) { |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 158 | remove_wait_queue(&nn->bl_wq, &wq); |
Jim Rees | 516f2e2 | 2011-09-22 21:50:08 -0400 | [diff] [blame] | 159 | rv = ERR_PTR(rc); |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 160 | goto out; |
| 161 | } |
| 162 | |
| 163 | set_current_state(TASK_UNINTERRUPTIBLE); |
| 164 | schedule(); |
| 165 | __set_current_state(TASK_RUNNING); |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 166 | remove_wait_queue(&nn->bl_wq, &wq); |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 167 | |
| 168 | if (reply->status != BL_DEVICE_REQUEST_PROC) { |
| 169 | dprintk("%s failed to open device: %d\n", |
| 170 | __func__, reply->status); |
| 171 | rv = ERR_PTR(-EINVAL); |
| 172 | goto out; |
| 173 | } |
| 174 | |
| 175 | bd = nfs4_blkdev_get(MKDEV(reply->major, reply->minor)); |
| 176 | if (IS_ERR(bd)) { |
Jim Rees | 516f2e2 | 2011-09-22 21:50:08 -0400 | [diff] [blame] | 177 | rc = PTR_ERR(bd); |
| 178 | dprintk("%s failed to open device : %d\n", __func__, rc); |
| 179 | rv = ERR_PTR(rc); |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 180 | goto out; |
| 181 | } |
| 182 | |
| 183 | rv = kzalloc(sizeof(*rv), GFP_NOFS); |
| 184 | if (!rv) { |
| 185 | rv = ERR_PTR(-ENOMEM); |
| 186 | goto out; |
| 187 | } |
| 188 | |
| 189 | rv->bm_mdev = bd; |
| 190 | memcpy(&rv->bm_mdevid, &dev->dev_id, sizeof(struct nfs4_deviceid)); |
Stanislav Kinsbursky | 9e2e74d | 2012-01-10 17:04:24 +0400 | [diff] [blame] | 191 | rv->net = net; |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 192 | dprintk("%s Created device %s with bd_block_size %u\n", |
| 193 | __func__, |
| 194 | bd->bd_disk->disk_name, |
| 195 | bd->bd_block_size); |
| 196 | |
| 197 | out: |
Stanislav Kinsbursky | 5ffaf85 | 2012-03-11 18:20:31 +0400 | [diff] [blame] | 198 | kfree(msg->data); |
Jim Rees | fe0a9b7 | 2011-07-30 20:52:42 -0400 | [diff] [blame] | 199 | return rv; |
| 200 | } |
Fred Isaman | a60d2eb | 2011-07-30 20:52:44 -0400 | [diff] [blame] | 201 | |
Fred Isaman | e9437cc | 2011-07-30 20:52:47 -0400 | [diff] [blame] | 202 | /* Map deviceid returned by the server to constructed block_device */ |
| 203 | static struct block_device *translate_devid(struct pnfs_layout_hdr *lo, |
| 204 | struct nfs4_deviceid *id) |
| 205 | { |
| 206 | struct block_device *rv = NULL; |
| 207 | struct block_mount_id *mid; |
| 208 | struct pnfs_block_dev *dev; |
| 209 | |
| 210 | dprintk("%s enter, lo=%p, id=%p\n", __func__, lo, id); |
| 211 | mid = BLK_ID(lo); |
| 212 | spin_lock(&mid->bm_lock); |
| 213 | list_for_each_entry(dev, &mid->bm_devlist, bm_node) { |
| 214 | if (memcmp(id->data, dev->bm_mdevid.data, |
| 215 | NFS4_DEVICEID4_SIZE) == 0) { |
| 216 | rv = dev->bm_mdev; |
| 217 | goto out; |
| 218 | } |
| 219 | } |
| 220 | out: |
| 221 | spin_unlock(&mid->bm_lock); |
| 222 | dprintk("%s returning %p\n", __func__, rv); |
| 223 | return rv; |
| 224 | } |
| 225 | |
| 226 | /* Tracks info needed to ensure extents in layout obey constraints of spec */ |
| 227 | struct layout_verification { |
| 228 | u32 mode; /* R or RW */ |
| 229 | u64 start; /* Expected start of next non-COW extent */ |
| 230 | u64 inval; /* Start of INVAL coverage */ |
| 231 | u64 cowread; /* End of COW read coverage */ |
| 232 | }; |
| 233 | |
| 234 | /* Verify the extent meets the layout requirements of the pnfs-block draft, |
| 235 | * section 2.3.1. |
| 236 | */ |
| 237 | static int verify_extent(struct pnfs_block_extent *be, |
| 238 | struct layout_verification *lv) |
| 239 | { |
| 240 | if (lv->mode == IOMODE_READ) { |
| 241 | if (be->be_state == PNFS_BLOCK_READWRITE_DATA || |
| 242 | be->be_state == PNFS_BLOCK_INVALID_DATA) |
| 243 | return -EIO; |
| 244 | if (be->be_f_offset != lv->start) |
| 245 | return -EIO; |
| 246 | lv->start += be->be_length; |
| 247 | return 0; |
| 248 | } |
| 249 | /* lv->mode == IOMODE_RW */ |
| 250 | if (be->be_state == PNFS_BLOCK_READWRITE_DATA) { |
| 251 | if (be->be_f_offset != lv->start) |
| 252 | return -EIO; |
| 253 | if (lv->cowread > lv->start) |
| 254 | return -EIO; |
| 255 | lv->start += be->be_length; |
| 256 | lv->inval = lv->start; |
| 257 | return 0; |
| 258 | } else if (be->be_state == PNFS_BLOCK_INVALID_DATA) { |
| 259 | if (be->be_f_offset != lv->start) |
| 260 | return -EIO; |
| 261 | lv->start += be->be_length; |
| 262 | return 0; |
| 263 | } else if (be->be_state == PNFS_BLOCK_READ_DATA) { |
| 264 | if (be->be_f_offset > lv->start) |
| 265 | return -EIO; |
| 266 | if (be->be_f_offset < lv->inval) |
| 267 | return -EIO; |
| 268 | if (be->be_f_offset < lv->cowread) |
| 269 | return -EIO; |
| 270 | /* It looks like you might want to min this with lv->start, |
| 271 | * but you really don't. |
| 272 | */ |
| 273 | lv->inval = lv->inval + be->be_length; |
| 274 | lv->cowread = be->be_f_offset + be->be_length; |
| 275 | return 0; |
| 276 | } else |
| 277 | return -EIO; |
| 278 | } |
| 279 | |
| 280 | /* XDR decode pnfs_block_layout4 structure */ |
Fred Isaman | a60d2eb | 2011-07-30 20:52:44 -0400 | [diff] [blame] | 281 | int |
| 282 | nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo, |
| 283 | struct nfs4_layoutget_res *lgr, gfp_t gfp_flags) |
| 284 | { |
Fred Isaman | e9437cc | 2011-07-30 20:52:47 -0400 | [diff] [blame] | 285 | struct pnfs_block_layout *bl = BLK_LO2EXT(lo); |
| 286 | int i, status = -EIO; |
| 287 | uint32_t count; |
| 288 | struct pnfs_block_extent *be = NULL, *save; |
| 289 | struct xdr_stream stream; |
| 290 | struct xdr_buf buf; |
| 291 | struct page *scratch; |
| 292 | __be32 *p; |
| 293 | struct layout_verification lv = { |
| 294 | .mode = lgr->range.iomode, |
| 295 | .start = lgr->range.offset >> SECTOR_SHIFT, |
| 296 | .inval = lgr->range.offset >> SECTOR_SHIFT, |
| 297 | .cowread = lgr->range.offset >> SECTOR_SHIFT, |
| 298 | }; |
| 299 | LIST_HEAD(extents); |
| 300 | |
| 301 | dprintk("---> %s\n", __func__); |
| 302 | |
| 303 | scratch = alloc_page(gfp_flags); |
| 304 | if (!scratch) |
| 305 | return -ENOMEM; |
| 306 | |
| 307 | xdr_init_decode_pages(&stream, &buf, lgr->layoutp->pages, lgr->layoutp->len); |
| 308 | xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE); |
| 309 | |
| 310 | p = xdr_inline_decode(&stream, 4); |
| 311 | if (unlikely(!p)) |
| 312 | goto out_err; |
| 313 | |
| 314 | count = be32_to_cpup(p++); |
| 315 | |
| 316 | dprintk("%s enter, number of extents %i\n", __func__, count); |
| 317 | p = xdr_inline_decode(&stream, (28 + NFS4_DEVICEID4_SIZE) * count); |
| 318 | if (unlikely(!p)) |
| 319 | goto out_err; |
| 320 | |
| 321 | /* Decode individual extents, putting them in temporary |
| 322 | * staging area until whole layout is decoded to make error |
| 323 | * recovery easier. |
| 324 | */ |
| 325 | for (i = 0; i < count; i++) { |
| 326 | be = bl_alloc_extent(); |
| 327 | if (!be) { |
| 328 | status = -ENOMEM; |
| 329 | goto out_err; |
| 330 | } |
| 331 | memcpy(&be->be_devid, p, NFS4_DEVICEID4_SIZE); |
| 332 | p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); |
| 333 | be->be_mdev = translate_devid(lo, &be->be_devid); |
| 334 | if (!be->be_mdev) |
| 335 | goto out_err; |
| 336 | |
| 337 | /* The next three values are read in as bytes, |
| 338 | * but stored as 512-byte sector lengths |
| 339 | */ |
| 340 | if (decode_sector_number(&p, &be->be_f_offset) < 0) |
| 341 | goto out_err; |
| 342 | if (decode_sector_number(&p, &be->be_length) < 0) |
| 343 | goto out_err; |
| 344 | if (decode_sector_number(&p, &be->be_v_offset) < 0) |
| 345 | goto out_err; |
| 346 | be->be_state = be32_to_cpup(p++); |
| 347 | if (be->be_state == PNFS_BLOCK_INVALID_DATA) |
| 348 | be->be_inval = &bl->bl_inval; |
| 349 | if (verify_extent(be, &lv)) { |
| 350 | dprintk("%s verify failed\n", __func__); |
| 351 | goto out_err; |
| 352 | } |
| 353 | list_add_tail(&be->be_node, &extents); |
| 354 | } |
| 355 | if (lgr->range.offset + lgr->range.length != |
| 356 | lv.start << SECTOR_SHIFT) { |
| 357 | dprintk("%s Final length mismatch\n", __func__); |
| 358 | be = NULL; |
| 359 | goto out_err; |
| 360 | } |
| 361 | if (lv.start < lv.cowread) { |
| 362 | dprintk("%s Final uncovered COW extent\n", __func__); |
| 363 | be = NULL; |
| 364 | goto out_err; |
| 365 | } |
| 366 | /* Extents decoded properly, now try to merge them in to |
| 367 | * existing layout extents. |
| 368 | */ |
| 369 | spin_lock(&bl->bl_ext_lock); |
| 370 | list_for_each_entry_safe(be, save, &extents, be_node) { |
| 371 | list_del(&be->be_node); |
| 372 | status = bl_add_merge_extent(bl, be); |
| 373 | if (status) { |
| 374 | spin_unlock(&bl->bl_ext_lock); |
| 375 | /* This is a fairly catastrophic error, as the |
| 376 | * entire layout extent lists are now corrupted. |
| 377 | * We should have some way to distinguish this. |
| 378 | */ |
| 379 | be = NULL; |
| 380 | goto out_err; |
| 381 | } |
| 382 | } |
| 383 | spin_unlock(&bl->bl_ext_lock); |
| 384 | status = 0; |
| 385 | out: |
| 386 | __free_page(scratch); |
| 387 | dprintk("%s returns %i\n", __func__, status); |
| 388 | return status; |
| 389 | |
| 390 | out_err: |
| 391 | bl_put_extent(be); |
| 392 | while (!list_empty(&extents)) { |
| 393 | be = list_first_entry(&extents, struct pnfs_block_extent, |
| 394 | be_node); |
| 395 | list_del(&be->be_node); |
| 396 | bl_put_extent(be); |
| 397 | } |
| 398 | goto out; |
Fred Isaman | a60d2eb | 2011-07-30 20:52:44 -0400 | [diff] [blame] | 399 | } |