Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'nfsd-7.0-2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux

Pull nfsd fixes from Chuck Lever:

- Fix cache_request leak in cache_release()

- Fix heap overflow in the NFSv4.0 LOCK replay cache

- Hold net reference for the lifetime of /proc/fs/nfs/exports fd

- Defer sub-object cleanup in export "put" callbacks

* tag 'nfsd-7.0-2' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux:
nfsd: fix heap overflow in NFSv4.0 LOCK replay cache
sunrpc: fix cache_request leak in cache_release
NFSD: Hold net reference for the lifetime of /proc/fs/nfs/exports fd
NFSD: Defer sub-object cleanup in export put callbacks

+118 -26
+54 -9
fs/nfsd/export.c
··· 36 36 * second map contains a reference to the entry in the first map. 37 37 */ 38 38 39 + static struct workqueue_struct *nfsd_export_wq; 40 + 39 41 #define EXPKEY_HASHBITS 8 40 42 #define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS) 41 43 #define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1) 42 44 43 - static void expkey_put(struct kref *ref) 45 + static void expkey_release(struct work_struct *work) 44 46 { 45 - struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); 47 + struct svc_expkey *key = container_of(to_rcu_work(work), 48 + struct svc_expkey, ek_rwork); 46 49 47 50 if (test_bit(CACHE_VALID, &key->h.flags) && 48 51 !test_bit(CACHE_NEGATIVE, &key->h.flags)) 49 52 path_put(&key->ek_path); 50 53 auth_domain_put(key->ek_client); 51 - kfree_rcu(key, ek_rcu); 54 + kfree(key); 55 + } 56 + 57 + static void expkey_put(struct kref *ref) 58 + { 59 + struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref); 60 + 61 + INIT_RCU_WORK(&key->ek_rwork, expkey_release); 62 + queue_rcu_work(nfsd_export_wq, &key->ek_rwork); 52 63 } 53 64 54 65 static int expkey_upcall(struct cache_detail *cd, struct cache_head *h) ··· 364 353 EXP_STATS_COUNTERS_NUM); 365 354 } 366 355 367 - static void svc_export_release(struct rcu_head *rcu_head) 356 + static void svc_export_release(struct work_struct *work) 368 357 { 369 - struct svc_export *exp = container_of(rcu_head, struct svc_export, 370 - ex_rcu); 358 + struct svc_export *exp = container_of(to_rcu_work(work), 359 + struct svc_export, ex_rwork); 371 360 361 + path_put(&exp->ex_path); 362 + auth_domain_put(exp->ex_client); 372 363 nfsd4_fslocs_free(&exp->ex_fslocs); 373 364 export_stats_destroy(exp->ex_stats); 374 365 kfree(exp->ex_stats); ··· 382 369 { 383 370 struct svc_export *exp = container_of(ref, struct svc_export, h.ref); 384 371 385 - path_put(&exp->ex_path); 386 - auth_domain_put(exp->ex_client); 387 - call_rcu(&exp->ex_rcu, svc_export_release); 372 + INIT_RCU_WORK(&exp->ex_rwork, svc_export_release); 373 + queue_rcu_work(nfsd_export_wq, &exp->ex_rwork); 388 374 } 389 375 390 376 static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h) ··· 1491 1479 .show = e_show, 1492 1480 }; 1493 1481 1482 + /** 1483 + * nfsd_export_wq_init - allocate the export release workqueue 1484 + * 1485 + * Called once at module load. The workqueue runs deferred svc_export and 1486 + * svc_expkey release work scheduled by queue_rcu_work() in the cache put 1487 + * callbacks. 1488 + * 1489 + * Return values: 1490 + * %0: workqueue allocated 1491 + * %-ENOMEM: allocation failed 1492 + */ 1493 + int nfsd_export_wq_init(void) 1494 + { 1495 + nfsd_export_wq = alloc_workqueue("nfsd_export", WQ_UNBOUND, 0); 1496 + if (!nfsd_export_wq) 1497 + return -ENOMEM; 1498 + return 0; 1499 + } 1500 + 1501 + /** 1502 + * nfsd_export_wq_shutdown - drain and free the export release workqueue 1503 + * 1504 + * Called once at module unload. Per-namespace teardown in 1505 + * nfsd_export_shutdown() has already drained all deferred work. 1506 + */ 1507 + void nfsd_export_wq_shutdown(void) 1508 + { 1509 + destroy_workqueue(nfsd_export_wq); 1510 + } 1511 + 1494 1512 /* 1495 1513 * Initialize the exports module. 1496 1514 */ ··· 1582 1540 1583 1541 cache_unregister_net(nn->svc_expkey_cache, net); 1584 1542 cache_unregister_net(nn->svc_export_cache, net); 1543 + /* Drain deferred export and expkey release work. */ 1544 + rcu_barrier(); 1545 + flush_workqueue(nfsd_export_wq); 1585 1546 cache_destroy_net(nn->svc_expkey_cache, net); 1586 1547 cache_destroy_net(nn->svc_export_cache, net); 1587 1548 svcauth_unix_purge(net);
+5 -2
fs/nfsd/export.h
··· 7 7 8 8 #include <linux/sunrpc/cache.h> 9 9 #include <linux/percpu_counter.h> 10 + #include <linux/workqueue.h> 10 11 #include <uapi/linux/nfsd/export.h> 11 12 #include <linux/nfs4.h> 12 13 ··· 76 75 u32 ex_layout_types; 77 76 struct nfsd4_deviceid_map *ex_devid_map; 78 77 struct cache_detail *cd; 79 - struct rcu_head ex_rcu; 78 + struct rcu_work ex_rwork; 80 79 unsigned long ex_xprtsec_modes; 81 80 struct export_stats *ex_stats; 82 81 }; ··· 93 92 u32 ek_fsid[6]; 94 93 95 94 struct path ek_path; 96 - struct rcu_head ek_rcu; 95 + struct rcu_work ek_rwork; 97 96 }; 98 97 99 98 #define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC)) ··· 111 110 /* 112 111 * Function declarations 113 112 */ 113 + int nfsd_export_wq_init(void); 114 + void nfsd_export_wq_shutdown(void); 114 115 int nfsd_export_init(struct net *); 115 116 void nfsd_export_shutdown(struct net *); 116 117 void nfsd_export_flush(struct net *);
+7 -2
fs/nfsd/nfs4xdr.c
··· 6281 6281 int len = xdr->buf->len - (op_status_offset + XDR_UNIT); 6282 6282 6283 6283 so->so_replay.rp_status = op->status; 6284 - so->so_replay.rp_buflen = len; 6285 - read_bytes_from_xdr_buf(xdr->buf, op_status_offset + XDR_UNIT, 6284 + if (len <= NFSD4_REPLAY_ISIZE) { 6285 + so->so_replay.rp_buflen = len; 6286 + read_bytes_from_xdr_buf(xdr->buf, 6287 + op_status_offset + XDR_UNIT, 6286 6288 so->so_replay.rp_buf, len); 6289 + } else { 6290 + so->so_replay.rp_buflen = 0; 6291 + } 6287 6292 } 6288 6293 status: 6289 6294 op->status = nfsd4_map_status(op->status,
+19 -3
fs/nfsd/nfsctl.c
··· 149 149 150 150 seq = file->private_data; 151 151 seq->private = nn->svc_export_cache; 152 + get_net(net); 152 153 return 0; 154 + } 155 + 156 + static int exports_release(struct inode *inode, struct file *file) 157 + { 158 + struct seq_file *seq = file->private_data; 159 + struct cache_detail *cd = seq->private; 160 + 161 + put_net(cd->net); 162 + return seq_release(inode, file); 153 163 } 154 164 155 165 static int exports_nfsd_open(struct inode *inode, struct file *file) ··· 171 161 .open = exports_nfsd_open, 172 162 .read = seq_read, 173 163 .llseek = seq_lseek, 174 - .release = seq_release, 164 + .release = exports_release, 175 165 }; 176 166 177 167 static int export_features_show(struct seq_file *m, void *v) ··· 1386 1376 .proc_open = exports_proc_open, 1387 1377 .proc_read = seq_read, 1388 1378 .proc_lseek = seq_lseek, 1389 - .proc_release = seq_release, 1379 + .proc_release = exports_release, 1390 1380 }; 1391 1381 1392 1382 static int create_proc_exports_entry(void) ··· 2269 2259 if (retval) 2270 2260 goto out_free_pnfs; 2271 2261 nfsd_lockd_init(); /* lockd->nfsd callbacks */ 2262 + retval = nfsd_export_wq_init(); 2263 + if (retval) 2264 + goto out_free_lockd; 2272 2265 retval = register_pernet_subsys(&nfsd_net_ops); 2273 2266 if (retval < 0) 2274 - goto out_free_lockd; 2267 + goto out_free_export_wq; 2275 2268 retval = register_cld_notifier(); 2276 2269 if (retval) 2277 2270 goto out_free_subsys; ··· 2303 2290 unregister_cld_notifier(); 2304 2291 out_free_subsys: 2305 2292 unregister_pernet_subsys(&nfsd_net_ops); 2293 + out_free_export_wq: 2294 + nfsd_export_wq_shutdown(); 2306 2295 out_free_lockd: 2307 2296 nfsd_lockd_shutdown(); 2308 2297 nfsd_drc_slab_free(); ··· 2325 2310 nfsd4_destroy_laundry_wq(); 2326 2311 unregister_cld_notifier(); 2327 2312 unregister_pernet_subsys(&nfsd_net_ops); 2313 + nfsd_export_wq_shutdown(); 2328 2314 nfsd_drc_slab_free(); 2329 2315 nfsd_lockd_shutdown(); 2330 2316 nfsd4_free_slabs();
+12 -5
fs/nfsd/state.h
··· 541 541 struct xdr_netobj cr_princhash; 542 542 }; 543 543 544 - /* A reasonable value for REPLAY_ISIZE was estimated as follows: 545 - * The OPEN response, typically the largest, requires 546 - * 4(status) + 8(stateid) + 20(changeinfo) + 4(rflags) + 8(verifier) + 547 - * 4(deleg. type) + 8(deleg. stateid) + 4(deleg. recall flag) + 548 - * 20(deleg. space limit) + ~32(deleg. ace) = 112 bytes 544 + /* 545 + * REPLAY_ISIZE is sized for an OPEN response with delegation: 546 + * 4(status) + 8(stateid) + 20(changeinfo) + 4(rflags) + 547 + * 8(verifier) + 4(deleg. type) + 8(deleg. stateid) + 548 + * 4(deleg. recall flag) + 20(deleg. space limit) + 549 + * ~32(deleg. ace) = 112 bytes 550 + * 551 + * Some responses can exceed this. A LOCK denial includes the conflicting 552 + * lock owner, which can be up to 1024 bytes (NFS4_OPAQUE_LIMIT). Responses 553 + * larger than REPLAY_ISIZE are not cached in rp_ibuf; only rp_status is 554 + * saved. Enlarging this constant increases the size of every 555 + * nfs4_stateowner. 549 556 */ 550 557 551 558 #define NFSD4_REPLAY_ISIZE 112
+21 -5
net/sunrpc/cache.c
··· 1062 1062 struct cache_reader *rp = filp->private_data; 1063 1063 1064 1064 if (rp) { 1065 + struct cache_request *rq = NULL; 1066 + 1065 1067 spin_lock(&queue_lock); 1066 1068 if (rp->offset) { 1067 1069 struct cache_queue *cq; 1068 - for (cq= &rp->q; &cq->list != &cd->queue; 1069 - cq = list_entry(cq->list.next, struct cache_queue, list)) 1070 + for (cq = &rp->q; &cq->list != &cd->queue; 1071 + cq = list_entry(cq->list.next, 1072 + struct cache_queue, list)) 1070 1073 if (!cq->reader) { 1071 - container_of(cq, struct cache_request, q) 1072 - ->readers--; 1074 + struct cache_request *cr = 1075 + container_of(cq, 1076 + struct cache_request, q); 1077 + cr->readers--; 1078 + if (cr->readers == 0 && 1079 + !test_bit(CACHE_PENDING, 1080 + &cr->item->flags)) { 1081 + list_del(&cr->q.list); 1082 + rq = cr; 1083 + } 1073 1084 break; 1074 1085 } 1075 1086 rp->offset = 0; ··· 1088 1077 list_del(&rp->q.list); 1089 1078 spin_unlock(&queue_lock); 1090 1079 1080 + if (rq) { 1081 + cache_put(rq->item, cd); 1082 + kfree(rq->buf); 1083 + kfree(rq); 1084 + } 1085 + 1091 1086 filp->private_data = NULL; 1092 1087 kfree(rp); 1093 - 1094 1088 } 1095 1089 if (filp->f_mode & FMODE_WRITE) { 1096 1090 atomic_dec(&cd->writers);