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

9p: Use a slab for allocating requests

Replace the custom batch allocation with a slab. Use an IDR to store
pointers to the active requests instead of an array. We don't try to
handle P9_NOTAG specially; the IDR will happily shrink all the way back
once the TVERSION call has completed.

Link: http://lkml.kernel.org/r/20180711210225.19730-6-willy@infradead.org
Signed-off-by: Matthew Wilcox <willy@infradead.org>
Cc: Eric Van Hensbergen <ericvh@gmail.com>
Cc: Ron Minnich <rminnich@sandia.gov>
Cc: Latchesar Ionkov <lucho@ionkov.net>
Signed-off-by: Dominique Martinet <dominique.martinet@cea.fr>

authored by

Matthew Wilcox and committed by
Dominique Martinet
996d5b4d 62e39417

+102 -196
+9 -42
include/net/9p/client.h
··· 64 64 65 65 /** 66 66 * enum p9_req_status_t - status of a request 67 - * @REQ_STATUS_IDLE: request slot unused 68 67 * @REQ_STATUS_ALLOC: request has been allocated but not sent 69 68 * @REQ_STATUS_UNSENT: request waiting to be sent 70 69 * @REQ_STATUS_SENT: request sent to server 71 70 * @REQ_STATUS_RCVD: response received from server 72 71 * @REQ_STATUS_FLSHD: request has been flushed 73 72 * @REQ_STATUS_ERROR: request encountered an error on the client side 74 - * 75 - * The @REQ_STATUS_IDLE state is used to mark a request slot as unused 76 - * but use is actually tracked by the idpool structure which handles tag 77 - * id allocation. 78 - * 79 73 */ 80 74 81 75 enum p9_req_status_t { 82 - REQ_STATUS_IDLE, 83 76 REQ_STATUS_ALLOC, 84 77 REQ_STATUS_UNSENT, 85 78 REQ_STATUS_SENT, ··· 85 92 * struct p9_req_t - request slots 86 93 * @status: status of this request slot 87 94 * @t_err: transport error 88 - * @flush_tag: tag of request being flushed (for flush requests) 89 95 * @wq: wait_queue for the client to block on for this request 90 96 * @tc: the request fcall structure 91 97 * @rc: the response fcall structure 92 98 * @aux: transport specific data (provided for trans_fd migration) 93 99 * @req_list: link for higher level objects to chain requests 94 - * 95 - * Transport use an array to track outstanding requests 96 - * instead of a list. While this may incurr overhead during initial 97 - * allocation or expansion, it makes request lookup much easier as the 98 - * tag id is a index into an array. (We use tag+1 so that we can accommodate 99 - * the -1 tag for the T_VERSION request). 100 - * This also has the nice effect of only having to allocate wait_queues 101 - * once, instead of constantly allocating and freeing them. Its possible 102 - * other resources could benefit from this scheme as well. 103 - * 104 100 */ 105 - 106 101 struct p9_req_t { 107 102 int status; 108 103 int t_err; ··· 98 117 struct p9_fcall *tc; 99 118 struct p9_fcall *rc; 100 119 void *aux; 101 - 102 120 struct list_head req_list; 103 121 }; 104 122 105 123 /** 106 124 * struct p9_client - per client instance state 107 - * @lock: protect @fidlist 125 + * @lock: protect @fids and @reqs 108 126 * @msize: maximum data size negotiated by protocol 109 - * @dotu: extension flags negotiated by protocol 110 127 * @proto_version: 9P protocol version to use 111 128 * @trans_mod: module API instantiated with this client 129 + * @status: connection state 112 130 * @trans: tranport instance state and API 113 131 * @fids: All active FID handles 114 - * @tagpool - transaction id accounting for session 115 - * @reqs - 2D array of requests 116 - * @max_tag - current maximum tag id allocated 117 - * @name - node name used as client id 132 + * @reqs: All active requests. 133 + * @name: node name used as client id 118 134 * 119 135 * The client structure is used to keep track of various per-client 120 136 * state that has been instantiated. 121 - * In order to minimize per-transaction overhead we use a 122 - * simple array to lookup requests instead of a hash table 123 - * or linked list. In order to support larger number of 124 - * transactions, we make this a 2D array, allocating new rows 125 - * when we need to grow the total number of the transactions. 126 - * 127 - * Each row is 256 requests and we'll support up to 256 rows for 128 - * a total of 64k concurrent requests per session. 129 - * 130 - * Bugs: duplicated data and potentially unnecessary elements. 131 137 */ 132 - 133 138 struct p9_client { 134 - spinlock_t lock; /* protect client structure */ 139 + spinlock_t lock; 135 140 unsigned int msize; 136 141 unsigned char proto_version; 137 142 struct p9_trans_module *trans_mod; ··· 137 170 } trans_opts; 138 171 139 172 struct idr fids; 140 - 141 - struct p9_idpool *tagpool; 142 - struct p9_req_t *reqs[P9_ROW_MAXTAG]; 143 - int max_tag; 173 + struct idr reqs; 144 174 145 175 char name[__NEW_UTS_LEN + 1]; 146 176 }; ··· 242 278 struct p9_fid *p9_client_xattrwalk(struct p9_fid *, const char *, u64 *); 243 279 int p9_client_xattrcreate(struct p9_fid *, const char *, u64, int); 244 280 int p9_client_readlink(struct p9_fid *fid, char **target); 281 + 282 + int p9_client_init(void); 283 + void p9_client_exit(void); 245 284 246 285 #endif /* NET_9P_CLIENT_H */
+85 -153
net/9p/client.c
··· 242 242 return fc; 243 243 } 244 244 245 + static struct kmem_cache *p9_req_cache; 246 + 245 247 /** 246 - * p9_tag_alloc - lookup/allocate a request by tag 247 - * @c: client session to lookup tag within 248 - * @tag: numeric id for transaction 248 + * p9_req_alloc - Allocate a new request. 249 + * @c: Client session. 250 + * @type: Transaction type. 251 + * @max_size: Maximum packet size for this request. 249 252 * 250 - * this is a simple array lookup, but will grow the 251 - * request_slots as necessary to accommodate transaction 252 - * ids which did not previously have a slot. 253 - * 254 - * this code relies on the client spinlock to manage locks, its 255 - * possible we should switch to something else, but I'd rather 256 - * stick with something low-overhead for the common case. 257 - * 253 + * Context: Process context. 254 + * Return: Pointer to new request. 258 255 */ 259 - 260 256 static struct p9_req_t * 261 - p9_tag_alloc(struct p9_client *c, u16 tag, unsigned int max_size) 257 + p9_tag_alloc(struct p9_client *c, int8_t type, unsigned int max_size) 262 258 { 263 - unsigned long flags; 264 - int row, col; 265 - struct p9_req_t *req; 259 + struct p9_req_t *req = kmem_cache_alloc(p9_req_cache, GFP_NOFS); 266 260 int alloc_msize = min(c->msize, max_size); 261 + int tag; 267 262 268 - /* This looks up the original request by tag so we know which 269 - * buffer to read the data into */ 270 - tag++; 263 + if (!req) 264 + return NULL; 271 265 272 - if (tag >= c->max_tag) { 273 - spin_lock_irqsave(&c->lock, flags); 274 - /* check again since original check was outside of lock */ 275 - while (tag >= c->max_tag) { 276 - row = (tag / P9_ROW_MAXTAG); 277 - c->reqs[row] = kcalloc(P9_ROW_MAXTAG, 278 - sizeof(struct p9_req_t), GFP_ATOMIC); 279 - 280 - if (!c->reqs[row]) { 281 - pr_err("Couldn't grow tag array\n"); 282 - spin_unlock_irqrestore(&c->lock, flags); 283 - return ERR_PTR(-ENOMEM); 284 - } 285 - for (col = 0; col < P9_ROW_MAXTAG; col++) { 286 - req = &c->reqs[row][col]; 287 - req->status = REQ_STATUS_IDLE; 288 - init_waitqueue_head(&req->wq); 289 - } 290 - c->max_tag += P9_ROW_MAXTAG; 291 - } 292 - spin_unlock_irqrestore(&c->lock, flags); 293 - } 294 - row = tag / P9_ROW_MAXTAG; 295 - col = tag % P9_ROW_MAXTAG; 296 - 297 - req = &c->reqs[row][col]; 298 - if (!req->tc) 299 - req->tc = p9_fcall_alloc(alloc_msize); 300 - if (!req->rc) 301 - req->rc = p9_fcall_alloc(alloc_msize); 266 + req->tc = p9_fcall_alloc(alloc_msize); 267 + req->rc = p9_fcall_alloc(alloc_msize); 302 268 if (!req->tc || !req->rc) 303 - goto grow_failed; 269 + goto free; 304 270 305 271 p9pdu_reset(req->tc); 306 272 p9pdu_reset(req->rc); 307 - 308 - req->tc->tag = tag-1; 309 273 req->status = REQ_STATUS_ALLOC; 274 + init_waitqueue_head(&req->wq); 275 + INIT_LIST_HEAD(&req->req_list); 276 + 277 + idr_preload(GFP_NOFS); 278 + spin_lock_irq(&c->lock); 279 + if (type == P9_TVERSION) 280 + tag = idr_alloc(&c->reqs, req, P9_NOTAG, P9_NOTAG + 1, 281 + GFP_NOWAIT); 282 + else 283 + tag = idr_alloc(&c->reqs, req, 0, P9_NOTAG, GFP_NOWAIT); 284 + req->tc->tag = tag; 285 + spin_unlock_irq(&c->lock); 286 + idr_preload_end(); 287 + if (tag < 0) 288 + goto free; 310 289 311 290 return req; 312 291 313 - grow_failed: 314 - pr_err("Couldn't grow tag array\n"); 292 + free: 315 293 kfree(req->tc); 316 294 kfree(req->rc); 317 - req->tc = req->rc = NULL; 295 + kmem_cache_free(p9_req_cache, req); 318 296 return ERR_PTR(-ENOMEM); 319 297 } 320 298 321 299 /** 322 - * p9_tag_lookup - lookup a request by tag 323 - * @c: client session to lookup tag within 324 - * @tag: numeric id for transaction 300 + * p9_tag_lookup - Look up a request by tag. 301 + * @c: Client session. 302 + * @tag: Transaction ID. 325 303 * 304 + * Context: Any context. 305 + * Return: A request, or %NULL if there is no request with that tag. 326 306 */ 327 - 328 307 struct p9_req_t *p9_tag_lookup(struct p9_client *c, u16 tag) 329 308 { 330 - int row, col; 309 + struct p9_req_t *req; 331 310 332 - /* This looks up the original request by tag so we know which 333 - * buffer to read the data into */ 334 - tag++; 311 + rcu_read_lock(); 312 + req = idr_find(&c->reqs, tag); 313 + /* There's no refcount on the req; a malicious server could cause 314 + * us to dereference a NULL pointer 315 + */ 316 + rcu_read_unlock(); 335 317 336 - if (tag >= c->max_tag) 337 - return NULL; 338 - 339 - row = tag / P9_ROW_MAXTAG; 340 - col = tag % P9_ROW_MAXTAG; 341 - 342 - return &c->reqs[row][col]; 318 + return req; 343 319 } 344 320 EXPORT_SYMBOL(p9_tag_lookup); 345 321 346 322 /** 347 - * p9_tag_init - setup tags structure and contents 348 - * @c: v9fs client struct 323 + * p9_free_req - Free a request. 324 + * @c: Client session. 325 + * @r: Request to free. 349 326 * 350 - * This initializes the tags structure for each client instance. 351 - * 327 + * Context: Any context. 352 328 */ 353 - 354 - static int p9_tag_init(struct p9_client *c) 329 + static void p9_free_req(struct p9_client *c, struct p9_req_t *r) 355 330 { 356 - int err = 0; 331 + unsigned long flags; 332 + u16 tag = r->tc->tag; 357 333 358 - c->tagpool = p9_idpool_create(); 359 - if (IS_ERR(c->tagpool)) { 360 - err = PTR_ERR(c->tagpool); 361 - goto error; 362 - } 363 - err = p9_idpool_get(c->tagpool); /* reserve tag 0 */ 364 - if (err < 0) { 365 - p9_idpool_destroy(c->tagpool); 366 - goto error; 367 - } 368 - c->max_tag = 0; 369 - error: 370 - return err; 334 + p9_debug(P9_DEBUG_MUX, "clnt %p req %p tag: %d\n", c, r, tag); 335 + spin_lock_irqsave(&c->lock, flags); 336 + idr_remove(&c->reqs, tag); 337 + spin_unlock_irqrestore(&c->lock, flags); 338 + kfree(r->tc); 339 + kfree(r->rc); 340 + kmem_cache_free(p9_req_cache, r); 371 341 } 372 342 373 343 /** ··· 349 379 */ 350 380 static void p9_tag_cleanup(struct p9_client *c) 351 381 { 352 - int row, col; 382 + struct p9_req_t *req; 383 + int id; 353 384 354 - /* check to insure all requests are idle */ 355 - for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) { 356 - for (col = 0; col < P9_ROW_MAXTAG; col++) { 357 - if (c->reqs[row][col].status != REQ_STATUS_IDLE) { 358 - p9_debug(P9_DEBUG_MUX, 359 - "Attempting to cleanup non-free tag %d,%d\n", 360 - row, col); 361 - /* TODO: delay execution of cleanup */ 362 - return; 363 - } 364 - } 385 + rcu_read_lock(); 386 + idr_for_each_entry(&c->reqs, req, id) { 387 + pr_info("Tag %d still in use\n", id); 388 + p9_free_req(c, req); 365 389 } 366 - 367 - if (c->tagpool) { 368 - p9_idpool_put(0, c->tagpool); /* free reserved tag 0 */ 369 - p9_idpool_destroy(c->tagpool); 370 - } 371 - 372 - /* free requests associated with tags */ 373 - for (row = 0; row < (c->max_tag/P9_ROW_MAXTAG); row++) { 374 - for (col = 0; col < P9_ROW_MAXTAG; col++) { 375 - kfree(c->reqs[row][col].tc); 376 - kfree(c->reqs[row][col].rc); 377 - } 378 - kfree(c->reqs[row]); 379 - } 380 - c->max_tag = 0; 381 - } 382 - 383 - /** 384 - * p9_free_req - free a request and clean-up as necessary 385 - * c: client state 386 - * r: request to release 387 - * 388 - */ 389 - 390 - static void p9_free_req(struct p9_client *c, struct p9_req_t *r) 391 - { 392 - int tag = r->tc->tag; 393 - p9_debug(P9_DEBUG_MUX, "clnt %p req %p tag: %d\n", c, r, tag); 394 - 395 - r->status = REQ_STATUS_IDLE; 396 - if (tag != P9_NOTAG && p9_idpool_check(tag, c->tagpool)) 397 - p9_idpool_put(tag, c->tagpool); 390 + rcu_read_unlock(); 398 391 } 399 392 400 393 /** ··· 631 698 int8_t type, int req_size, 632 699 const char *fmt, va_list ap) 633 700 { 634 - int tag, err; 701 + int err; 635 702 struct p9_req_t *req; 636 703 637 704 p9_debug(P9_DEBUG_MUX, "client %p op %d\n", c, type); ··· 644 711 if ((c->status == BeginDisconnect) && (type != P9_TCLUNK)) 645 712 return ERR_PTR(-EIO); 646 713 647 - tag = P9_NOTAG; 648 - if (type != P9_TVERSION) { 649 - tag = p9_idpool_get(c->tagpool); 650 - if (tag < 0) 651 - return ERR_PTR(-ENOMEM); 652 - } 653 - 654 - req = p9_tag_alloc(c, tag, req_size); 714 + req = p9_tag_alloc(c, type, req_size); 655 715 if (IS_ERR(req)) 656 716 return req; 657 717 658 718 /* marshall the data */ 659 - p9pdu_prepare(req->tc, tag, type); 719 + p9pdu_prepare(req->tc, req->tc->tag, type); 660 720 err = p9pdu_vwritef(req->tc, c->proto_version, fmt, ap); 661 721 if (err) 662 722 goto reterr; 663 723 p9pdu_finalize(c, req->tc); 664 - trace_9p_client_req(c, type, tag); 724 + trace_9p_client_req(c, type, req->tc->tag); 665 725 return req; 666 726 reterr: 667 727 p9_free_req(c, req); ··· 952 1026 953 1027 spin_lock_init(&clnt->lock); 954 1028 idr_init(&clnt->fids); 955 - 956 - err = p9_tag_init(clnt); 957 - if (err < 0) 958 - goto free_client; 1029 + idr_init(&clnt->reqs); 959 1030 960 1031 err = parse_opts(options, clnt); 961 1032 if (err < 0) 962 - goto destroy_tagpool; 1033 + goto free_client; 963 1034 964 1035 if (!clnt->trans_mod) 965 1036 clnt->trans_mod = v9fs_get_default_trans(); ··· 965 1042 err = -EPROTONOSUPPORT; 966 1043 p9_debug(P9_DEBUG_ERROR, 967 1044 "No transport defined or default transport\n"); 968 - goto destroy_tagpool; 1045 + goto free_client; 969 1046 } 970 1047 971 1048 p9_debug(P9_DEBUG_MUX, "clnt %p trans %p msize %d protocol %d\n", ··· 988 1065 clnt->trans_mod->close(clnt); 989 1066 put_trans: 990 1067 v9fs_put_trans(clnt->trans_mod); 991 - destroy_tagpool: 992 - p9_idpool_destroy(clnt->tagpool); 993 1068 free_client: 994 1069 kfree(clnt); 995 1070 return ERR_PTR(err); ··· 2203 2282 return err; 2204 2283 } 2205 2284 EXPORT_SYMBOL(p9_client_readlink); 2285 + 2286 + int __init p9_client_init(void) 2287 + { 2288 + p9_req_cache = KMEM_CACHE(p9_req_t, 0); 2289 + return p9_req_cache ? 0 : -ENOMEM; 2290 + } 2291 + 2292 + void __exit p9_client_exit(void) 2293 + { 2294 + kmem_cache_destroy(p9_req_cache); 2295 + }
+8 -1
net/9p/mod.c
··· 171 171 */ 172 172 static int __init init_p9(void) 173 173 { 174 + int ret; 175 + 176 + ret = p9_client_init(); 177 + if (ret) 178 + return ret; 179 + 174 180 p9_error_init(); 175 181 pr_info("Installing 9P2000 support\n"); 176 182 p9_trans_fd_init(); 177 183 178 - return 0; 184 + return ret; 179 185 } 180 186 181 187 /** ··· 194 188 pr_info("Unloading 9P2000 support\n"); 195 189 196 190 p9_trans_fd_exit(); 191 + p9_client_exit(); 197 192 } 198 193 199 194 module_init(init_p9)