Commit 3e2796a90cf349527e50b3bc4d0b2f4019b1ce7a

Authored by Eric Van Hensbergen
1 parent 2511cd0b3b

9p: fix readdir corner cases

The patch below also addresses a couple of other corner cases in readdir
seen with a large (e.g. 64k) msize.  I'm not sure what people think of
my co-opting of fid->aux here.  I'd be happy to rework if there's a better
way.

When the size of the user supplied buffer passed to readdir is smaller
than the data returned in one go by the 9P read request, v9fs_dir_readdir()
currently discards extra data so that, on the next call, a 9P read
request will be issued with offset < previous offset + bytes returned,
which voilates the constraint described in paragraph 3 of read(5) description.
This patch preseves the leftover data in fid->aux for use in the next call.

Signed-off-by: Jim Garlick <garlick@llnl.gov>
Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>

Showing 3 changed files with 72 additions and 34 deletions Side-by-side Diff

... ... @@ -40,6 +40,24 @@
40 40 #include "fid.h"
41 41  
42 42 /**
  43 + * struct p9_rdir - readdir accounting
  44 + * @mutex: mutex protecting readdir
  45 + * @head: start offset of current dirread buffer
  46 + * @tail: end offset of current dirread buffer
  47 + * @buf: dirread buffer
  48 + *
  49 + * private structure for keeping track of readdir
  50 + * allocated on demand
  51 + */
  52 +
  53 +struct p9_rdir {
  54 + struct mutex mutex;
  55 + int head;
  56 + int tail;
  57 + uint8_t *buf;
  58 +};
  59 +
  60 +/**
43 61 * dt_type - return file type
44 62 * @mistat: mistat structure
45 63 *
46 64  
47 65  
48 66  
49 67  
50 68  
51 69  
52 70  
53 71  
54 72  
55 73  
56 74  
... ... @@ -70,57 +88,79 @@
70 88 {
71 89 int over;
72 90 struct p9_wstat st;
73   - int err;
  91 + int err = 0;
74 92 struct p9_fid *fid;
75 93 int buflen;
76   - char *statbuf;
77   - int n, i = 0;
  94 + int reclen = 0;
  95 + struct p9_rdir *rdir;
78 96  
79 97 P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
80 98 fid = filp->private_data;
81 99  
82 100 buflen = fid->clnt->msize - P9_IOHDRSZ;
83   - statbuf = kmalloc(buflen, GFP_KERNEL);
84   - if (!statbuf)
85   - return -ENOMEM;
86 101  
87   - while (1) {
88   - err = v9fs_file_readn(filp, statbuf, NULL, buflen,
89   - fid->rdir_fpos);
90   - if (err <= 0)
91   - break;
  102 + /* allocate rdir on demand */
  103 + if (!fid->rdir) {
  104 + rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
92 105  
93   - i = 0;
94   - n = err;
95   - while (i < n) {
96   - err = p9stat_read(statbuf + i, buflen-i, &st,
97   - fid->clnt->dotu);
  106 + if (rdir == NULL) {
  107 + err = -ENOMEM;
  108 + goto exit;
  109 + }
  110 + spin_lock(&filp->f_dentry->d_lock);
  111 + if (!fid->rdir) {
  112 + rdir->buf = (uint8_t *)rdir + sizeof(struct p9_rdir);
  113 + mutex_init(&rdir->mutex);
  114 + rdir->head = rdir->tail = 0;
  115 + fid->rdir = (void *) rdir;
  116 + rdir = NULL;
  117 + }
  118 + spin_unlock(&filp->f_dentry->d_lock);
  119 + kfree(rdir);
  120 + }
  121 + rdir = (struct p9_rdir *) fid->rdir;
  122 +
  123 + err = mutex_lock_interruptible(&rdir->mutex);
  124 + while (err == 0) {
  125 + if (rdir->tail == rdir->head) {
  126 + err = v9fs_file_readn(filp, rdir->buf, NULL,
  127 + buflen, filp->f_pos);
  128 + if (err <= 0)
  129 + goto unlock_and_exit;
  130 +
  131 + rdir->head = 0;
  132 + rdir->tail = err;
  133 + }
  134 +
  135 + while (rdir->head < rdir->tail) {
  136 + err = p9stat_read(rdir->buf + rdir->head,
  137 + buflen - rdir->head, &st,
  138 + fid->clnt->dotu);
98 139 if (err) {
99 140 P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
100 141 err = -EIO;
101 142 p9stat_free(&st);
102   - goto free_and_exit;
  143 + goto unlock_and_exit;
103 144 }
  145 + reclen = st.size+2;
104 146  
105   - i += st.size+2;
106   - fid->rdir_fpos += st.size+2;
107   -
108 147 over = filldir(dirent, st.name, strlen(st.name),
109 148 filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st));
110 149  
111   - filp->f_pos += st.size+2;
112   -
113 150 p9stat_free(&st);
114 151  
115 152 if (over) {
116 153 err = 0;
117   - goto free_and_exit;
  154 + goto unlock_and_exit;
118 155 }
  156 + rdir->head += reclen;
  157 + filp->f_pos += reclen;
119 158 }
120 159 }
121 160  
122   -free_and_exit:
123   - kfree(statbuf);
  161 +unlock_and_exit:
  162 + mutex_unlock(&rdir->mutex);
  163 +exit:
124 164 return err;
125 165 }
126 166  
include/net/9p/client.h
... ... @@ -159,8 +159,7 @@
159 159 * @qid: the &p9_qid server identifier this handle points to
160 160 * @iounit: the server reported maximum transaction size for this file
161 161 * @uid: the numeric uid of the local user who owns this handle
162   - * @aux: transport specific information (unused?)
163   - * @rdir_fpos: tracks offset of file position when reading directory contents
  162 + * @rdir: readdir accounting structure (allocated on demand)
164 163 * @flist: per-client-instance fid tracking
165 164 * @dlist: per-dentry fid tracking
166 165 *
167 166  
... ... @@ -174,9 +173,9 @@
174 173 struct p9_qid qid;
175 174 u32 iounit;
176 175 uid_t uid;
177   - void *aux;
178 176  
179   - int rdir_fpos;
  177 + void *rdir;
  178 +
180 179 struct list_head flist;
181 180 struct list_head dlist; /* list of all fids attached to a dentry */
182 181 };
... ... @@ -582,11 +582,9 @@
582 582  
583 583 memset(&fid->qid, 0, sizeof(struct p9_qid));
584 584 fid->mode = -1;
585   - fid->rdir_fpos = 0;
586 585 fid->uid = current_fsuid();
587 586 fid->clnt = clnt;
588   - fid->aux = NULL;
589   -
  587 + fid->rdir = NULL;
590 588 spin_lock_irqsave(&clnt->lock, flags);
591 589 list_add(&fid->flist, &clnt->fidlist);
592 590 spin_unlock_irqrestore(&clnt->lock, flags);
... ... @@ -609,6 +607,7 @@
609 607 spin_lock_irqsave(&clnt->lock, flags);
610 608 list_del(&fid->flist);
611 609 spin_unlock_irqrestore(&clnt->lock, flags);
  610 + kfree(fid->rdir);
612 611 kfree(fid);
613 612 }
614 613