diff options
| -rw-r--r-- | fs/nfs/Makefile | 2 | ||||
| -rw-r--r-- | fs/nfs/client.c | 1 | ||||
| -rw-r--r-- | fs/nfs/nfs4filelayout.c | 204 | ||||
| -rw-r--r-- | fs/nfs/nfs4filelayout.h | 94 | ||||
| -rw-r--r-- | fs/nfs/nfs4filelayoutdev.c | 448 |
5 files changed, 747 insertions, 2 deletions
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 08a8889c5149..4776ff9e3814 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile | |||
| @@ -20,4 +20,4 @@ nfs-$(CONFIG_SYSCTL) += sysctl.o | |||
| 20 | nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o | 20 | nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o |
| 21 | 21 | ||
| 22 | obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o | 22 | obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o |
| 23 | nfs_layout_nfsv41_files-y := nfs4filelayout.o | 23 | nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o |
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index e55ad211dbed..8934b2e339df 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c | |||
| @@ -255,6 +255,7 @@ void nfs_put_client(struct nfs_client *clp) | |||
| 255 | nfs_free_client(clp); | 255 | nfs_free_client(clp); |
| 256 | } | 256 | } |
| 257 | } | 257 | } |
| 258 | EXPORT_SYMBOL_GPL(nfs_put_client); | ||
| 258 | 259 | ||
| 259 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 260 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
| 260 | /* | 261 | /* |
diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 696e283c2632..9e82b51cb515 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c | |||
| @@ -30,7 +30,9 @@ | |||
| 30 | */ | 30 | */ |
| 31 | 31 | ||
| 32 | #include <linux/nfs_fs.h> | 32 | #include <linux/nfs_fs.h> |
| 33 | #include "pnfs.h" | 33 | |
| 34 | #include "internal.h" | ||
| 35 | #include "nfs4filelayout.h" | ||
| 34 | 36 | ||
| 35 | #define NFSDBG_FACILITY NFSDBG_PNFS_LD | 37 | #define NFSDBG_FACILITY NFSDBG_PNFS_LD |
| 36 | 38 | ||
| @@ -41,23 +43,223 @@ MODULE_DESCRIPTION("The NFSv4 file layout driver"); | |||
| 41 | int | 43 | int |
| 42 | filelayout_initialize_mountpoint(struct nfs_server *nfss) | 44 | filelayout_initialize_mountpoint(struct nfs_server *nfss) |
| 43 | { | 45 | { |
| 46 | int status = pnfs_alloc_init_deviceid_cache(nfss->nfs_client, | ||
| 47 | nfs4_fl_free_deviceid_callback); | ||
| 48 | if (status) { | ||
| 49 | printk(KERN_WARNING "%s: deviceid cache could not be " | ||
| 50 | "initialized\n", __func__); | ||
| 51 | return status; | ||
| 52 | } | ||
| 53 | dprintk("%s: deviceid cache has been initialized successfully\n", | ||
| 54 | __func__); | ||
| 44 | return 0; | 55 | return 0; |
| 45 | } | 56 | } |
| 46 | 57 | ||
| 58 | /* Uninitialize a mountpoint by destroying its device list */ | ||
| 47 | int | 59 | int |
| 48 | filelayout_uninitialize_mountpoint(struct nfs_server *nfss) | 60 | filelayout_uninitialize_mountpoint(struct nfs_server *nfss) |
| 49 | { | 61 | { |
| 50 | dprintk("--> %s\n", __func__); | 62 | dprintk("--> %s\n", __func__); |
| 51 | 63 | ||
| 64 | if (nfss->nfs_client->cl_devid_cache) | ||
| 65 | pnfs_put_deviceid_cache(nfss->nfs_client); | ||
| 52 | return 0; | 66 | return 0; |
| 53 | } | 67 | } |
| 54 | 68 | ||
| 69 | /* | ||
| 70 | * filelayout_check_layout() | ||
| 71 | * | ||
| 72 | * Make sure layout segment parameters are sane WRT the device. | ||
| 73 | * At this point no generic layer initialization of the lseg has occurred, | ||
| 74 | * and nothing has been added to the layout_hdr cache. | ||
| 75 | * | ||
| 76 | */ | ||
| 77 | static int | ||
| 78 | filelayout_check_layout(struct pnfs_layout_hdr *lo, | ||
| 79 | struct nfs4_filelayout_segment *fl, | ||
| 80 | struct nfs4_layoutget_res *lgr, | ||
| 81 | struct nfs4_deviceid *id) | ||
| 82 | { | ||
| 83 | struct nfs4_file_layout_dsaddr *dsaddr; | ||
| 84 | int status = -EINVAL; | ||
| 85 | struct nfs_server *nfss = NFS_SERVER(lo->inode); | ||
| 86 | |||
| 87 | dprintk("--> %s\n", __func__); | ||
| 88 | |||
| 89 | if (fl->pattern_offset > lgr->range.offset) { | ||
| 90 | dprintk("%s pattern_offset %lld to large\n", | ||
| 91 | __func__, fl->pattern_offset); | ||
| 92 | goto out; | ||
| 93 | } | ||
| 94 | |||
| 95 | if (fl->stripe_unit % PAGE_SIZE) { | ||
| 96 | dprintk("%s Stripe unit (%u) not page aligned\n", | ||
| 97 | __func__, fl->stripe_unit); | ||
| 98 | goto out; | ||
| 99 | } | ||
| 100 | |||
| 101 | /* find and reference the deviceid */ | ||
| 102 | dsaddr = nfs4_fl_find_get_deviceid(nfss->nfs_client, id); | ||
| 103 | if (dsaddr == NULL) { | ||
| 104 | dsaddr = get_device_info(lo->inode, id); | ||
| 105 | if (dsaddr == NULL) | ||
| 106 | goto out; | ||
| 107 | } | ||
| 108 | fl->dsaddr = dsaddr; | ||
| 109 | |||
| 110 | if (fl->first_stripe_index < 0 || | ||
| 111 | fl->first_stripe_index >= dsaddr->stripe_count) { | ||
| 112 | dprintk("%s Bad first_stripe_index %d\n", | ||
| 113 | __func__, fl->first_stripe_index); | ||
| 114 | goto out_put; | ||
| 115 | } | ||
| 116 | |||
| 117 | if ((fl->stripe_type == STRIPE_SPARSE && | ||
| 118 | fl->num_fh > 1 && fl->num_fh != dsaddr->ds_num) || | ||
| 119 | (fl->stripe_type == STRIPE_DENSE && | ||
| 120 | fl->num_fh != dsaddr->stripe_count)) { | ||
| 121 | dprintk("%s num_fh %u not valid for given packing\n", | ||
| 122 | __func__, fl->num_fh); | ||
| 123 | goto out_put; | ||
| 124 | } | ||
| 125 | |||
| 126 | if (fl->stripe_unit % nfss->rsize || fl->stripe_unit % nfss->wsize) { | ||
| 127 | dprintk("%s Stripe unit (%u) not aligned with rsize %u " | ||
| 128 | "wsize %u\n", __func__, fl->stripe_unit, nfss->rsize, | ||
| 129 | nfss->wsize); | ||
| 130 | } | ||
| 131 | |||
| 132 | status = 0; | ||
| 133 | out: | ||
| 134 | dprintk("--> %s returns %d\n", __func__, status); | ||
| 135 | return status; | ||
| 136 | out_put: | ||
| 137 | pnfs_put_deviceid(nfss->nfs_client->cl_devid_cache, &dsaddr->deviceid); | ||
| 138 | goto out; | ||
| 139 | } | ||
| 140 | |||
| 141 | static void filelayout_free_fh_array(struct nfs4_filelayout_segment *fl) | ||
| 142 | { | ||
| 143 | int i; | ||
| 144 | |||
| 145 | for (i = 0; i < fl->num_fh; i++) { | ||
| 146 | if (!fl->fh_array[i]) | ||
| 147 | break; | ||
| 148 | kfree(fl->fh_array[i]); | ||
| 149 | } | ||
| 150 | kfree(fl->fh_array); | ||
| 151 | fl->fh_array = NULL; | ||
| 152 | } | ||
| 153 | |||
| 154 | static void | ||
| 155 | _filelayout_free_lseg(struct nfs4_filelayout_segment *fl) | ||
| 156 | { | ||
| 157 | filelayout_free_fh_array(fl); | ||
| 158 | kfree(fl); | ||
| 159 | } | ||
| 160 | |||
| 161 | static int | ||
| 162 | filelayout_decode_layout(struct pnfs_layout_hdr *flo, | ||
| 163 | struct nfs4_filelayout_segment *fl, | ||
| 164 | struct nfs4_layoutget_res *lgr, | ||
| 165 | struct nfs4_deviceid *id) | ||
| 166 | { | ||
| 167 | uint32_t *p = (uint32_t *)lgr->layout.buf; | ||
| 168 | uint32_t nfl_util; | ||
| 169 | int i; | ||
| 170 | |||
| 171 | dprintk("%s: set_layout_map Begin\n", __func__); | ||
| 172 | |||
| 173 | memcpy(id, p, sizeof(*id)); | ||
| 174 | p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE); | ||
| 175 | print_deviceid(id); | ||
| 176 | |||
| 177 | nfl_util = be32_to_cpup(p++); | ||
| 178 | if (nfl_util & NFL4_UFLG_COMMIT_THRU_MDS) | ||
| 179 | fl->commit_through_mds = 1; | ||
| 180 | if (nfl_util & NFL4_UFLG_DENSE) | ||
| 181 | fl->stripe_type = STRIPE_DENSE; | ||
| 182 | else | ||
| 183 | fl->stripe_type = STRIPE_SPARSE; | ||
| 184 | fl->stripe_unit = nfl_util & ~NFL4_UFLG_MASK; | ||
| 185 | |||
| 186 | fl->first_stripe_index = be32_to_cpup(p++); | ||
| 187 | p = xdr_decode_hyper(p, &fl->pattern_offset); | ||
| 188 | fl->num_fh = be32_to_cpup(p++); | ||
| 189 | |||
| 190 | dprintk("%s: nfl_util 0x%X num_fh %u fsi %u po %llu\n", | ||
| 191 | __func__, nfl_util, fl->num_fh, fl->first_stripe_index, | ||
| 192 | fl->pattern_offset); | ||
| 193 | |||
| 194 | fl->fh_array = kzalloc(fl->num_fh * sizeof(struct nfs_fh *), | ||
| 195 | GFP_KERNEL); | ||
| 196 | if (!fl->fh_array) | ||
| 197 | return -ENOMEM; | ||
| 198 | |||
| 199 | for (i = 0; i < fl->num_fh; i++) { | ||
| 200 | /* Do we want to use a mempool here? */ | ||
| 201 | fl->fh_array[i] = kmalloc(sizeof(struct nfs_fh), GFP_KERNEL); | ||
| 202 | if (!fl->fh_array[i]) { | ||
| 203 | filelayout_free_fh_array(fl); | ||
| 204 | return -ENOMEM; | ||
| 205 | } | ||
| 206 | fl->fh_array[i]->size = be32_to_cpup(p++); | ||
| 207 | if (sizeof(struct nfs_fh) < fl->fh_array[i]->size) { | ||
| 208 | printk(KERN_ERR "Too big fh %d received %d\n", | ||
| 209 | i, fl->fh_array[i]->size); | ||
| 210 | filelayout_free_fh_array(fl); | ||
| 211 | return -EIO; | ||
| 212 | } | ||
| 213 | memcpy(fl->fh_array[i]->data, p, fl->fh_array[i]->size); | ||
| 214 | p += XDR_QUADLEN(fl->fh_array[i]->size); | ||
| 215 | dprintk("DEBUG: %s: fh len %d\n", __func__, | ||
| 216 | fl->fh_array[i]->size); | ||
| 217 | } | ||
| 218 | |||
| 219 | return 0; | ||
| 220 | } | ||
| 221 | |||
| 222 | static struct pnfs_layout_segment * | ||
| 223 | filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid, | ||
| 224 | struct nfs4_layoutget_res *lgr) | ||
| 225 | { | ||
| 226 | struct nfs4_filelayout_segment *fl; | ||
| 227 | int rc; | ||
| 228 | struct nfs4_deviceid id; | ||
| 229 | |||
| 230 | dprintk("--> %s\n", __func__); | ||
| 231 | fl = kzalloc(sizeof(*fl), GFP_KERNEL); | ||
| 232 | if (!fl) | ||
| 233 | return NULL; | ||
| 234 | |||
| 235 | rc = filelayout_decode_layout(layoutid, fl, lgr, &id); | ||
| 236 | if (rc != 0 || filelayout_check_layout(layoutid, fl, lgr, &id)) { | ||
| 237 | _filelayout_free_lseg(fl); | ||
| 238 | return NULL; | ||
| 239 | } | ||
| 240 | return &fl->generic_hdr; | ||
| 241 | } | ||
| 242 | |||
| 243 | static void | ||
| 244 | filelayout_free_lseg(struct pnfs_layout_segment *lseg) | ||
| 245 | { | ||
| 246 | struct nfs_server *nfss = NFS_SERVER(lseg->layout->inode); | ||
| 247 | struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg); | ||
| 248 | |||
| 249 | dprintk("--> %s\n", __func__); | ||
| 250 | pnfs_put_deviceid(nfss->nfs_client->cl_devid_cache, | ||
| 251 | &fl->dsaddr->deviceid); | ||
| 252 | _filelayout_free_lseg(fl); | ||
| 253 | } | ||
| 254 | |||
| 55 | static struct pnfs_layoutdriver_type filelayout_type = { | 255 | static struct pnfs_layoutdriver_type filelayout_type = { |
| 56 | .id = LAYOUT_NFSV4_1_FILES, | 256 | .id = LAYOUT_NFSV4_1_FILES, |
| 57 | .name = "LAYOUT_NFSV4_1_FILES", | 257 | .name = "LAYOUT_NFSV4_1_FILES", |
| 58 | .owner = THIS_MODULE, | 258 | .owner = THIS_MODULE, |
| 59 | .initialize_mountpoint = filelayout_initialize_mountpoint, | 259 | .initialize_mountpoint = filelayout_initialize_mountpoint, |
| 60 | .uninitialize_mountpoint = filelayout_uninitialize_mountpoint, | 260 | .uninitialize_mountpoint = filelayout_uninitialize_mountpoint, |
| 261 | .alloc_lseg = filelayout_alloc_lseg, | ||
| 262 | .free_lseg = filelayout_free_lseg, | ||
| 61 | }; | 263 | }; |
| 62 | 264 | ||
| 63 | static int __init nfs4filelayout_init(void) | 265 | static int __init nfs4filelayout_init(void) |
diff --git a/fs/nfs/nfs4filelayout.h b/fs/nfs/nfs4filelayout.h new file mode 100644 index 000000000000..bbf60dd2ab9d --- /dev/null +++ b/fs/nfs/nfs4filelayout.h | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | /* | ||
| 2 | * NFSv4 file layout driver data structures. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002 | ||
| 5 | * The Regents of the University of Michigan | ||
| 6 | * All Rights Reserved | ||
| 7 | * | ||
| 8 | * Dean Hildebrand <dhildebz@umich.edu> | ||
| 9 | * | ||
| 10 | * Permission is granted to use, copy, create derivative works, and | ||
| 11 | * redistribute this software and such derivative works for any purpose, | ||
| 12 | * so long as the name of the University of Michigan is not used in | ||
| 13 | * any advertising or publicity pertaining to the use or distribution | ||
| 14 | * of this software without specific, written prior authorization. If | ||
| 15 | * the above copyright notice or any other identification of the | ||
| 16 | * University of Michigan is included in any copy of any portion of | ||
| 17 | * this software, then the disclaimer below must also be included. | ||
| 18 | * | ||
| 19 | * This software is provided as is, without representation or warranty | ||
| 20 | * of any kind either express or implied, including without limitation | ||
| 21 | * the implied warranties of merchantability, fitness for a particular | ||
| 22 | * purpose, or noninfringement. The Regents of the University of | ||
| 23 | * Michigan shall not be liable for any damages, including special, | ||
| 24 | * indirect, incidental, or consequential damages, with respect to any | ||
| 25 | * claim arising out of or in connection with the use of the software, | ||
| 26 | * even if it has been or is hereafter advised of the possibility of | ||
| 27 | * such damages. | ||
| 28 | */ | ||
| 29 | |||
| 30 | #ifndef FS_NFS_NFS4FILELAYOUT_H | ||
| 31 | #define FS_NFS_NFS4FILELAYOUT_H | ||
| 32 | |||
| 33 | #include "pnfs.h" | ||
| 34 | |||
| 35 | /* | ||
| 36 | * Field testing shows we need to support upto 4096 stripe indices. | ||
| 37 | * We store each index as a u8 (u32 on the wire) to keep the memory footprint | ||
| 38 | * reasonable. This in turn means we support a maximum of 256 | ||
| 39 | * RFC 5661 multipath_list4 structures. | ||
| 40 | */ | ||
| 41 | #define NFS4_PNFS_MAX_STRIPE_CNT 4096 | ||
| 42 | #define NFS4_PNFS_MAX_MULTI_CNT 256 /* 256 fit into a u8 stripe_index */ | ||
| 43 | |||
| 44 | enum stripetype4 { | ||
| 45 | STRIPE_SPARSE = 1, | ||
| 46 | STRIPE_DENSE = 2 | ||
| 47 | }; | ||
| 48 | |||
| 49 | /* Individual ip address */ | ||
| 50 | struct nfs4_pnfs_ds { | ||
| 51 | struct list_head ds_node; /* nfs4_pnfs_dev_hlist dev_dslist */ | ||
| 52 | u32 ds_ip_addr; | ||
| 53 | u32 ds_port; | ||
| 54 | struct nfs_client *ds_clp; | ||
| 55 | atomic_t ds_count; | ||
| 56 | }; | ||
| 57 | |||
| 58 | struct nfs4_file_layout_dsaddr { | ||
| 59 | struct pnfs_deviceid_node deviceid; | ||
| 60 | u32 stripe_count; | ||
| 61 | u8 *stripe_indices; | ||
| 62 | u32 ds_num; | ||
| 63 | struct nfs4_pnfs_ds *ds_list[1]; | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct nfs4_filelayout_segment { | ||
| 67 | struct pnfs_layout_segment generic_hdr; | ||
| 68 | u32 stripe_type; | ||
| 69 | u32 commit_through_mds; | ||
| 70 | u32 stripe_unit; | ||
| 71 | u32 first_stripe_index; | ||
| 72 | u64 pattern_offset; | ||
| 73 | struct nfs4_file_layout_dsaddr *dsaddr; /* Point to GETDEVINFO data */ | ||
| 74 | unsigned int num_fh; | ||
| 75 | struct nfs_fh **fh_array; | ||
| 76 | }; | ||
| 77 | |||
| 78 | static inline struct nfs4_filelayout_segment * | ||
| 79 | FILELAYOUT_LSEG(struct pnfs_layout_segment *lseg) | ||
| 80 | { | ||
| 81 | return container_of(lseg, | ||
| 82 | struct nfs4_filelayout_segment, | ||
| 83 | generic_hdr); | ||
| 84 | } | ||
| 85 | |||
| 86 | extern void nfs4_fl_free_deviceid_callback(struct pnfs_deviceid_node *); | ||
| 87 | extern void print_ds(struct nfs4_pnfs_ds *ds); | ||
| 88 | extern void print_deviceid(struct nfs4_deviceid *dev_id); | ||
| 89 | extern struct nfs4_file_layout_dsaddr * | ||
| 90 | nfs4_fl_find_get_deviceid(struct nfs_client *, struct nfs4_deviceid *dev_id); | ||
| 91 | struct nfs4_file_layout_dsaddr * | ||
| 92 | get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id); | ||
| 93 | |||
| 94 | #endif /* FS_NFS_NFS4FILELAYOUT_H */ | ||
diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c new file mode 100644 index 000000000000..51fe64ace55a --- /dev/null +++ b/fs/nfs/nfs4filelayoutdev.c | |||
| @@ -0,0 +1,448 @@ | |||
| 1 | /* | ||
| 2 | * Device operations for the pnfs nfs4 file layout driver. | ||
| 3 | * | ||
| 4 | * Copyright (c) 2002 | ||
| 5 | * The Regents of the University of Michigan | ||
| 6 | * All Rights Reserved | ||
| 7 | * | ||
| 8 | * Dean Hildebrand <dhildebz@umich.edu> | ||
| 9 | * Garth Goodson <Garth.Goodson@netapp.com> | ||
| 10 | * | ||
| 11 | * Permission is granted to use, copy, create derivative works, and | ||
| 12 | * redistribute this software and such derivative works for any purpose, | ||
| 13 | * so long as the name of the University of Michigan is not used in | ||
| 14 | * any advertising or publicity pertaining to the use or distribution | ||
| 15 | * of this software without specific, written prior authorization. If | ||
| 16 | * the above copyright notice or any other identification of the | ||
| 17 | * University of Michigan is included in any copy of any portion of | ||
| 18 | * this software, then the disclaimer below must also be included. | ||
| 19 | * | ||
| 20 | * This software is provided as is, without representation or warranty | ||
| 21 | * of any kind either express or implied, including without limitation | ||
| 22 | * the implied warranties of merchantability, fitness for a particular | ||
| 23 | * purpose, or noninfringement. The Regents of the University of | ||
| 24 | * Michigan shall not be liable for any damages, including special, | ||
| 25 | * indirect, incidental, or consequential damages, with respect to any | ||
| 26 | * claim arising out of or in connection with the use of the software, | ||
| 27 | * even if it has been or is hereafter advised of the possibility of | ||
| 28 | * such damages. | ||
| 29 | */ | ||
| 30 | |||
| 31 | #include <linux/nfs_fs.h> | ||
| 32 | #include <linux/vmalloc.h> | ||
| 33 | |||
| 34 | #include "internal.h" | ||
| 35 | #include "nfs4filelayout.h" | ||
| 36 | |||
| 37 | #define NFSDBG_FACILITY NFSDBG_PNFS_LD | ||
| 38 | |||
| 39 | /* | ||
| 40 | * Data server cache | ||
| 41 | * | ||
| 42 | * Data servers can be mapped to different device ids. | ||
| 43 | * nfs4_pnfs_ds reference counting | ||
| 44 | * - set to 1 on allocation | ||
| 45 | * - incremented when a device id maps a data server already in the cache. | ||
| 46 | * - decremented when deviceid is removed from the cache. | ||
| 47 | */ | ||
| 48 | DEFINE_SPINLOCK(nfs4_ds_cache_lock); | ||
| 49 | static LIST_HEAD(nfs4_data_server_cache); | ||
| 50 | |||
| 51 | /* Debug routines */ | ||
| 52 | void | ||
| 53 | print_ds(struct nfs4_pnfs_ds *ds) | ||
| 54 | { | ||
| 55 | if (ds == NULL) { | ||
| 56 | printk("%s NULL device\n", __func__); | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | printk(" ip_addr %x port %hu\n" | ||
| 60 | " ref count %d\n" | ||
| 61 | " client %p\n" | ||
| 62 | " cl_exchange_flags %x\n", | ||
| 63 | ntohl(ds->ds_ip_addr), ntohs(ds->ds_port), | ||
| 64 | atomic_read(&ds->ds_count), ds->ds_clp, | ||
| 65 | ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0); | ||
| 66 | } | ||
| 67 | |||
| 68 | void | ||
| 69 | print_ds_list(struct nfs4_file_layout_dsaddr *dsaddr) | ||
| 70 | { | ||
| 71 | int i; | ||
| 72 | |||
| 73 | ifdebug(FACILITY) { | ||
| 74 | printk("%s dsaddr->ds_num %d\n", __func__, | ||
| 75 | dsaddr->ds_num); | ||
| 76 | for (i = 0; i < dsaddr->ds_num; i++) | ||
| 77 | print_ds(dsaddr->ds_list[i]); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | void print_deviceid(struct nfs4_deviceid *id) | ||
| 82 | { | ||
| 83 | u32 *p = (u32 *)id; | ||
| 84 | |||
| 85 | dprintk("%s: device id= [%x%x%x%x]\n", __func__, | ||
| 86 | p[0], p[1], p[2], p[3]); | ||
| 87 | } | ||
| 88 | |||
| 89 | /* nfs4_ds_cache_lock is held */ | ||
| 90 | static struct nfs4_pnfs_ds * | ||
| 91 | _data_server_lookup_locked(u32 ip_addr, u32 port) | ||
| 92 | { | ||
| 93 | struct nfs4_pnfs_ds *ds; | ||
| 94 | |||
| 95 | dprintk("_data_server_lookup: ip_addr=%x port=%hu\n", | ||
| 96 | ntohl(ip_addr), ntohs(port)); | ||
| 97 | |||
| 98 | list_for_each_entry(ds, &nfs4_data_server_cache, ds_node) { | ||
| 99 | if (ds->ds_ip_addr == ip_addr && | ||
| 100 | ds->ds_port == port) { | ||
| 101 | return ds; | ||
| 102 | } | ||
| 103 | } | ||
| 104 | return NULL; | ||
| 105 | } | ||
| 106 | |||
| 107 | static void | ||
| 108 | destroy_ds(struct nfs4_pnfs_ds *ds) | ||
| 109 | { | ||
| 110 | dprintk("--> %s\n", __func__); | ||
| 111 | ifdebug(FACILITY) | ||
| 112 | print_ds(ds); | ||
| 113 | |||
| 114 | if (ds->ds_clp) | ||
| 115 | nfs_put_client(ds->ds_clp); | ||
| 116 | kfree(ds); | ||
| 117 | } | ||
| 118 | |||
| 119 | static void | ||
| 120 | nfs4_fl_free_deviceid(struct nfs4_file_layout_dsaddr *dsaddr) | ||
| 121 | { | ||
| 122 | struct nfs4_pnfs_ds *ds; | ||
| 123 | int i; | ||
| 124 | |||
| 125 | print_deviceid(&dsaddr->deviceid.de_id); | ||
| 126 | |||
| 127 | for (i = 0; i < dsaddr->ds_num; i++) { | ||
| 128 | ds = dsaddr->ds_list[i]; | ||
| 129 | if (ds != NULL) { | ||
| 130 | if (atomic_dec_and_lock(&ds->ds_count, | ||
| 131 | &nfs4_ds_cache_lock)) { | ||
| 132 | list_del_init(&ds->ds_node); | ||
| 133 | spin_unlock(&nfs4_ds_cache_lock); | ||
| 134 | destroy_ds(ds); | ||
| 135 | } | ||
| 136 | } | ||
| 137 | } | ||
| 138 | kfree(dsaddr->stripe_indices); | ||
| 139 | kfree(dsaddr); | ||
| 140 | } | ||
| 141 | |||
| 142 | void | ||
| 143 | nfs4_fl_free_deviceid_callback(struct pnfs_deviceid_node *device) | ||
| 144 | { | ||
| 145 | struct nfs4_file_layout_dsaddr *dsaddr = | ||
| 146 | container_of(device, struct nfs4_file_layout_dsaddr, deviceid); | ||
| 147 | |||
| 148 | nfs4_fl_free_deviceid(dsaddr); | ||
| 149 | } | ||
| 150 | |||
| 151 | static struct nfs4_pnfs_ds * | ||
| 152 | nfs4_pnfs_ds_add(struct inode *inode, u32 ip_addr, u32 port) | ||
| 153 | { | ||
| 154 | struct nfs4_pnfs_ds *tmp_ds, *ds; | ||
| 155 | |||
| 156 | ds = kzalloc(sizeof(*tmp_ds), GFP_KERNEL); | ||
| 157 | if (!ds) | ||
| 158 | goto out; | ||
| 159 | |||
| 160 | spin_lock(&nfs4_ds_cache_lock); | ||
| 161 | tmp_ds = _data_server_lookup_locked(ip_addr, port); | ||
| 162 | if (tmp_ds == NULL) { | ||
| 163 | ds->ds_ip_addr = ip_addr; | ||
| 164 | ds->ds_port = port; | ||
| 165 | atomic_set(&ds->ds_count, 1); | ||
| 166 | INIT_LIST_HEAD(&ds->ds_node); | ||
| 167 | ds->ds_clp = NULL; | ||
| 168 | list_add(&ds->ds_node, &nfs4_data_server_cache); | ||
| 169 | dprintk("%s add new data server ip 0x%x\n", __func__, | ||
| 170 | ds->ds_ip_addr); | ||
| 171 | } else { | ||
| 172 | kfree(ds); | ||
| 173 | atomic_inc(&tmp_ds->ds_count); | ||
| 174 | dprintk("%s data server found ip 0x%x, inc'ed ds_count to %d\n", | ||
| 175 | __func__, tmp_ds->ds_ip_addr, | ||
| 176 | atomic_read(&tmp_ds->ds_count)); | ||
| 177 | ds = tmp_ds; | ||
| 178 | } | ||
| 179 | spin_unlock(&nfs4_ds_cache_lock); | ||
| 180 | out: | ||
| 181 | return ds; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* | ||
| 185 | * Currently only support ipv4, and one multi-path address. | ||
| 186 | */ | ||
| 187 | static struct nfs4_pnfs_ds * | ||
| 188 | decode_and_add_ds(__be32 **pp, struct inode *inode) | ||
| 189 | { | ||
| 190 | struct nfs4_pnfs_ds *ds = NULL; | ||
| 191 | char *buf; | ||
| 192 | const char *ipend, *pstr; | ||
| 193 | u32 ip_addr, port; | ||
| 194 | int nlen, rlen, i; | ||
| 195 | int tmp[2]; | ||
| 196 | __be32 *r_netid, *r_addr, *p = *pp; | ||
| 197 | |||
| 198 | /* r_netid */ | ||
| 199 | nlen = be32_to_cpup(p++); | ||
| 200 | r_netid = p; | ||
| 201 | p += XDR_QUADLEN(nlen); | ||
| 202 | |||
| 203 | /* r_addr */ | ||
| 204 | rlen = be32_to_cpup(p++); | ||
| 205 | r_addr = p; | ||
| 206 | p += XDR_QUADLEN(rlen); | ||
| 207 | *pp = p; | ||
| 208 | |||
| 209 | /* Check that netid is "tcp" */ | ||
| 210 | if (nlen != 3 || memcmp((char *)r_netid, "tcp", 3)) { | ||
| 211 | dprintk("%s: ERROR: non ipv4 TCP r_netid\n", __func__); | ||
| 212 | goto out_err; | ||
| 213 | } | ||
| 214 | |||
| 215 | /* ipv6 length plus port is legal */ | ||
| 216 | if (rlen > INET6_ADDRSTRLEN + 8) { | ||
| 217 | dprintk("%s Invalid address, length %d\n", __func__, | ||
| 218 | rlen); | ||
| 219 | goto out_err; | ||
| 220 | } | ||
| 221 | buf = kmalloc(rlen + 1, GFP_KERNEL); | ||
| 222 | buf[rlen] = '\0'; | ||
| 223 | memcpy(buf, r_addr, rlen); | ||
| 224 | |||
| 225 | /* replace the port dots with dashes for the in4_pton() delimiter*/ | ||
| 226 | for (i = 0; i < 2; i++) { | ||
| 227 | char *res = strrchr(buf, '.'); | ||
| 228 | *res = '-'; | ||
| 229 | } | ||
| 230 | |||
| 231 | /* Currently only support ipv4 address */ | ||
| 232 | if (in4_pton(buf, rlen, (u8 *)&ip_addr, '-', &ipend) == 0) { | ||
| 233 | dprintk("%s: Only ipv4 addresses supported\n", __func__); | ||
| 234 | goto out_free; | ||
| 235 | } | ||
| 236 | |||
| 237 | /* port */ | ||
| 238 | pstr = ipend; | ||
| 239 | sscanf(pstr, "-%d-%d", &tmp[0], &tmp[1]); | ||
| 240 | port = htons((tmp[0] << 8) | (tmp[1])); | ||
| 241 | |||
| 242 | ds = nfs4_pnfs_ds_add(inode, ip_addr, port); | ||
| 243 | dprintk("%s Decoded address and port %s\n", __func__, buf); | ||
| 244 | out_free: | ||
| 245 | kfree(buf); | ||
| 246 | out_err: | ||
| 247 | return ds; | ||
| 248 | } | ||
| 249 | |||
| 250 | /* Decode opaque device data and return the result */ | ||
| 251 | static struct nfs4_file_layout_dsaddr* | ||
| 252 | decode_device(struct inode *ino, struct pnfs_device *pdev) | ||
| 253 | { | ||
| 254 | int i, dummy; | ||
| 255 | u32 cnt, num; | ||
| 256 | u8 *indexp; | ||
| 257 | __be32 *p = (__be32 *)pdev->area, *indicesp; | ||
| 258 | struct nfs4_file_layout_dsaddr *dsaddr; | ||
| 259 | |||
| 260 | /* Get the stripe count (number of stripe index) */ | ||
| 261 | cnt = be32_to_cpup(p++); | ||
| 262 | dprintk("%s stripe count %d\n", __func__, cnt); | ||
| 263 | if (cnt > NFS4_PNFS_MAX_STRIPE_CNT) { | ||
| 264 | printk(KERN_WARNING "%s: stripe count %d greater than " | ||
| 265 | "supported maximum %d\n", __func__, | ||
| 266 | cnt, NFS4_PNFS_MAX_STRIPE_CNT); | ||
| 267 | goto out_err; | ||
| 268 | } | ||
| 269 | |||
| 270 | /* Check the multipath list count */ | ||
| 271 | indicesp = p; | ||
| 272 | p += XDR_QUADLEN(cnt << 2); | ||
| 273 | num = be32_to_cpup(p++); | ||
| 274 | dprintk("%s ds_num %u\n", __func__, num); | ||
| 275 | if (num > NFS4_PNFS_MAX_MULTI_CNT) { | ||
| 276 | printk(KERN_WARNING "%s: multipath count %d greater than " | ||
| 277 | "supported maximum %d\n", __func__, | ||
| 278 | num, NFS4_PNFS_MAX_MULTI_CNT); | ||
| 279 | goto out_err; | ||
| 280 | } | ||
| 281 | dsaddr = kzalloc(sizeof(*dsaddr) + | ||
| 282 | (sizeof(struct nfs4_pnfs_ds *) * (num - 1)), | ||
| 283 | GFP_KERNEL); | ||
| 284 | if (!dsaddr) | ||
| 285 | goto out_err; | ||
| 286 | |||
| 287 | dsaddr->stripe_indices = kzalloc(sizeof(u8) * cnt, GFP_KERNEL); | ||
| 288 | if (!dsaddr->stripe_indices) | ||
| 289 | goto out_err_free; | ||
| 290 | |||
| 291 | dsaddr->stripe_count = cnt; | ||
| 292 | dsaddr->ds_num = num; | ||
| 293 | |||
| 294 | memcpy(&dsaddr->deviceid.de_id, &pdev->dev_id, sizeof(pdev->dev_id)); | ||
| 295 | |||
| 296 | /* Go back an read stripe indices */ | ||
| 297 | p = indicesp; | ||
| 298 | indexp = &dsaddr->stripe_indices[0]; | ||
| 299 | for (i = 0; i < dsaddr->stripe_count; i++) { | ||
| 300 | *indexp = be32_to_cpup(p++); | ||
| 301 | if (*indexp >= num) | ||
| 302 | goto out_err_free; | ||
| 303 | indexp++; | ||
| 304 | } | ||
| 305 | /* Skip already read multipath list count */ | ||
| 306 | p++; | ||
| 307 | |||
| 308 | for (i = 0; i < dsaddr->ds_num; i++) { | ||
| 309 | int j; | ||
| 310 | |||
| 311 | dummy = be32_to_cpup(p++); /* multipath count */ | ||
| 312 | if (dummy > 1) { | ||
| 313 | printk(KERN_WARNING | ||
| 314 | "%s: Multipath count %d not supported, " | ||
| 315 | "skipping all greater than 1\n", __func__, | ||
| 316 | dummy); | ||
| 317 | } | ||
| 318 | for (j = 0; j < dummy; j++) { | ||
| 319 | if (j == 0) { | ||
| 320 | dsaddr->ds_list[i] = decode_and_add_ds(&p, ino); | ||
| 321 | if (dsaddr->ds_list[i] == NULL) | ||
| 322 | goto out_err_free; | ||
| 323 | } else { | ||
| 324 | u32 len; | ||
| 325 | /* skip extra multipath */ | ||
| 326 | len = be32_to_cpup(p++); | ||
| 327 | p += XDR_QUADLEN(len); | ||
| 328 | len = be32_to_cpup(p++); | ||
| 329 | p += XDR_QUADLEN(len); | ||
| 330 | continue; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | return dsaddr; | ||
| 335 | |||
| 336 | out_err_free: | ||
| 337 | nfs4_fl_free_deviceid(dsaddr); | ||
| 338 | out_err: | ||
| 339 | dprintk("%s ERROR: returning NULL\n", __func__); | ||
| 340 | return NULL; | ||
| 341 | } | ||
| 342 | |||
| 343 | /* | ||
| 344 | * Decode the opaque device specified in 'dev' | ||
| 345 | * and add it to the list of available devices. | ||
| 346 | * If the deviceid is already cached, nfs4_add_deviceid will return | ||
| 347 | * a pointer to the cached struct and throw away the new. | ||
| 348 | */ | ||
| 349 | static struct nfs4_file_layout_dsaddr* | ||
| 350 | decode_and_add_device(struct inode *inode, struct pnfs_device *dev) | ||
| 351 | { | ||
| 352 | struct nfs4_file_layout_dsaddr *dsaddr; | ||
| 353 | struct pnfs_deviceid_node *d; | ||
| 354 | |||
| 355 | dsaddr = decode_device(inode, dev); | ||
| 356 | if (!dsaddr) { | ||
| 357 | printk(KERN_WARNING "%s: Could not decode or add device\n", | ||
| 358 | __func__); | ||
| 359 | return NULL; | ||
| 360 | } | ||
| 361 | |||
| 362 | d = pnfs_add_deviceid(NFS_SERVER(inode)->nfs_client->cl_devid_cache, | ||
| 363 | &dsaddr->deviceid); | ||
| 364 | |||
| 365 | return container_of(d, struct nfs4_file_layout_dsaddr, deviceid); | ||
| 366 | } | ||
| 367 | |||
| 368 | /* | ||
| 369 | * Retrieve the information for dev_id, add it to the list | ||
| 370 | * of available devices, and return it. | ||
| 371 | */ | ||
| 372 | struct nfs4_file_layout_dsaddr * | ||
| 373 | get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id) | ||
| 374 | { | ||
| 375 | struct pnfs_device *pdev = NULL; | ||
| 376 | u32 max_resp_sz; | ||
| 377 | int max_pages; | ||
| 378 | struct page **pages = NULL; | ||
| 379 | struct nfs4_file_layout_dsaddr *dsaddr = NULL; | ||
| 380 | int rc, i; | ||
| 381 | struct nfs_server *server = NFS_SERVER(inode); | ||
| 382 | |||
| 383 | /* | ||
| 384 | * Use the session max response size as the basis for setting | ||
| 385 | * GETDEVICEINFO's maxcount | ||
| 386 | */ | ||
| 387 | max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; | ||
| 388 | max_pages = max_resp_sz >> PAGE_SHIFT; | ||
| 389 | dprintk("%s inode %p max_resp_sz %u max_pages %d\n", | ||
| 390 | __func__, inode, max_resp_sz, max_pages); | ||
| 391 | |||
| 392 | pdev = kzalloc(sizeof(struct pnfs_device), GFP_KERNEL); | ||
| 393 | if (pdev == NULL) | ||
| 394 | return NULL; | ||
| 395 | |||
| 396 | pages = kzalloc(max_pages * sizeof(struct page *), GFP_KERNEL); | ||
| 397 | if (pages == NULL) { | ||
| 398 | kfree(pdev); | ||
| 399 | return NULL; | ||
| 400 | } | ||
| 401 | for (i = 0; i < max_pages; i++) { | ||
| 402 | pages[i] = alloc_page(GFP_KERNEL); | ||
| 403 | if (!pages[i]) | ||
| 404 | goto out_free; | ||
| 405 | } | ||
| 406 | |||
| 407 | /* set pdev->area */ | ||
| 408 | pdev->area = vmap(pages, max_pages, VM_MAP, PAGE_KERNEL); | ||
| 409 | if (!pdev->area) | ||
| 410 | goto out_free; | ||
| 411 | |||
| 412 | memcpy(&pdev->dev_id, dev_id, sizeof(*dev_id)); | ||
| 413 | pdev->layout_type = LAYOUT_NFSV4_1_FILES; | ||
| 414 | pdev->pages = pages; | ||
| 415 | pdev->pgbase = 0; | ||
| 416 | pdev->pglen = PAGE_SIZE * max_pages; | ||
| 417 | pdev->mincount = 0; | ||
| 418 | |||
| 419 | rc = nfs4_proc_getdeviceinfo(server, pdev); | ||
| 420 | dprintk("%s getdevice info returns %d\n", __func__, rc); | ||
| 421 | if (rc) | ||
| 422 | goto out_free; | ||
| 423 | |||
| 424 | /* | ||
| 425 | * Found new device, need to decode it and then add it to the | ||
| 426 | * list of known devices for this mountpoint. | ||
| 427 | */ | ||
| 428 | dsaddr = decode_and_add_device(inode, pdev); | ||
| 429 | out_free: | ||
| 430 | if (pdev->area != NULL) | ||
| 431 | vunmap(pdev->area); | ||
| 432 | for (i = 0; i < max_pages; i++) | ||
| 433 | __free_page(pages[i]); | ||
| 434 | kfree(pages); | ||
| 435 | kfree(pdev); | ||
| 436 | dprintk("<-- %s dsaddr %p\n", __func__, dsaddr); | ||
| 437 | return dsaddr; | ||
| 438 | } | ||
| 439 | |||
| 440 | struct nfs4_file_layout_dsaddr * | ||
| 441 | nfs4_fl_find_get_deviceid(struct nfs_client *clp, struct nfs4_deviceid *id) | ||
| 442 | { | ||
| 443 | struct pnfs_deviceid_node *d; | ||
| 444 | |||
| 445 | d = pnfs_find_get_deviceid(clp->cl_devid_cache, id); | ||
| 446 | return (d == NULL) ? NULL : | ||
| 447 | container_of(d, struct nfs4_file_layout_dsaddr, deviceid); | ||
| 448 | } | ||
