aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSripathi Kodi <sripathik@in.ibm.com>2010-06-04 09:41:26 -0400
committerEric Van Hensbergen <ericvh@gmail.com>2010-08-02 15:25:07 -0400
commit7751bdb3a095ad32dd4fcff3443cf8dd4cb1e748 (patch)
tree6c7a93a546e34d86e433a1be05e3ebfcc4f77754
parent97e8442b0971ea6be9a495b3d03402985cfe5d6a (diff)
9p: readdir implementation for 9p2000.L
This patch implements the kernel part of readdir() implementation for 9p2000.L Change from V3: Instead of inode, server now sends qids for each dirent SYNOPSIS size[4] Treaddir tag[2] fid[4] offset[8] count[4] size[4] Rreaddir tag[2] count[4] data[count] DESCRIPTION The readdir request asks the server to read the directory specified by 'fid' at an offset specified by 'offset' and return as many dirent structures as possible that fit into count bytes. Each dirent structure is laid out as follows. qid.type[1] the type of the file (directory, etc.), represented as a bit vector corresponding to the high 8 bits of the file's mode word. qid.vers[4] version number for given path qid.path[8] the file server's unique identification for the file offset[8] offset into the next dirent. type[1] type of this directory entry. name[256] name of this directory entry. This patch adds v9fs_dir_readdir_dotl() as the readdir() call for 9p2000.L. This function sends P9_TREADDIR command to the server. In response the server sends a buffer filled with dirent structures. This is different from the existing v9fs_dir_readdir() call which receives stat structures from the server. This results in significant speedup of readdir() on large directories. For example, doing 'ls >/dev/null' on a directory with 10000 files on my laptop takes 1.088 seconds with the existing code, but only takes 0.339 seconds with the new readdir. Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com> Reviewed-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
-rw-r--r--fs/9p/vfs_dir.c134
-rw-r--r--include/net/9p/9p.h17
-rw-r--r--include/net/9p/client.h18
-rw-r--r--net/9p/client.c47
-rw-r--r--net/9p/protocol.c27
5 files changed, 227 insertions, 16 deletions
diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c
index 36d961f342af..16c8a2a98c1b 100644
--- a/fs/9p/vfs_dir.c
+++ b/fs/9p/vfs_dir.c
@@ -87,29 +87,19 @@ static void p9stat_init(struct p9_wstat *stbuf)
87} 87}
88 88
89/** 89/**
90 * v9fs_dir_readdir - read a directory 90 * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir
91 * @filp: opened file structure 91 * @filp: opened file structure
92 * @dirent: directory structure ??? 92 * @buflen: Length in bytes of buffer to allocate
93 * @filldir: function to populate directory structure ???
94 * 93 *
95 */ 94 */
96 95
97static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) 96static int v9fs_alloc_rdir_buf(struct file *filp, int buflen)
98{ 97{
99 int over;
100 struct p9_wstat st;
101 int err = 0;
102 struct p9_fid *fid;
103 int buflen;
104 int reclen = 0;
105 struct p9_rdir *rdir; 98 struct p9_rdir *rdir;
99 struct p9_fid *fid;
100 int err = 0;
106 101
107 P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
108 fid = filp->private_data; 102 fid = filp->private_data;
109
110 buflen = fid->clnt->msize - P9_IOHDRSZ;
111
112 /* allocate rdir on demand */
113 if (!fid->rdir) { 103 if (!fid->rdir) {
114 rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); 104 rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
115 105
@@ -128,6 +118,36 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
128 spin_unlock(&filp->f_dentry->d_lock); 118 spin_unlock(&filp->f_dentry->d_lock);
129 kfree(rdir); 119 kfree(rdir);
130 } 120 }
121exit:
122 return err;
123}
124
125/**
126 * v9fs_dir_readdir - read a directory
127 * @filp: opened file structure
128 * @dirent: directory structure ???
129 * @filldir: function to populate directory structure ???
130 *
131 */
132
133static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
134{
135 int over;
136 struct p9_wstat st;
137 int err = 0;
138 struct p9_fid *fid;
139 int buflen;
140 int reclen = 0;
141 struct p9_rdir *rdir;
142
143 P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
144 fid = filp->private_data;
145
146 buflen = fid->clnt->msize - P9_IOHDRSZ;
147
148 err = v9fs_alloc_rdir_buf(filp, buflen);
149 if (err)
150 goto exit;
131 rdir = (struct p9_rdir *) fid->rdir; 151 rdir = (struct p9_rdir *) fid->rdir;
132 152
133 err = mutex_lock_interruptible(&rdir->mutex); 153 err = mutex_lock_interruptible(&rdir->mutex);
@@ -176,6 +196,88 @@ exit:
176 return err; 196 return err;
177} 197}
178 198
199/**
200 * v9fs_dir_readdir_dotl - read a directory
201 * @filp: opened file structure
202 * @dirent: buffer to fill dirent structures
203 * @filldir: function to populate dirent structures
204 *
205 */
206static int v9fs_dir_readdir_dotl(struct file *filp, void *dirent,
207 filldir_t filldir)
208{
209 int over;
210 int err = 0;
211 struct p9_fid *fid;
212 int buflen;
213 struct p9_rdir *rdir;
214 struct p9_dirent curdirent;
215 u64 oldoffset = 0;
216
217 P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
218 fid = filp->private_data;
219
220 buflen = fid->clnt->msize - P9_READDIRHDRSZ;
221
222 err = v9fs_alloc_rdir_buf(filp, buflen);
223 if (err)
224 goto exit;
225 rdir = (struct p9_rdir *) fid->rdir;
226
227 err = mutex_lock_interruptible(&rdir->mutex);
228 if (err)
229 return err;
230
231 while (err == 0) {
232 if (rdir->tail == rdir->head) {
233 err = p9_client_readdir(fid, rdir->buf, buflen,
234 filp->f_pos);
235 if (err <= 0)
236 goto unlock_and_exit;
237
238 rdir->head = 0;
239 rdir->tail = err;
240 }
241
242 while (rdir->head < rdir->tail) {
243
244 err = p9dirent_read(rdir->buf + rdir->head,
245 buflen - rdir->head, &curdirent,
246 fid->clnt->proto_version);
247 if (err < 0) {
248 P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
249 err = -EIO;
250 goto unlock_and_exit;
251 }
252
253 /* d_off in dirent structure tracks the offset into
254 * the next dirent in the dir. However, filldir()
255 * expects offset into the current dirent. Hence
256 * while calling filldir send the offset from the
257 * previous dirent structure.
258 */
259 over = filldir(dirent, curdirent.d_name,
260 strlen(curdirent.d_name),
261 oldoffset, v9fs_qid2ino(&curdirent.qid),
262 curdirent.d_type);
263 oldoffset = curdirent.d_off;
264
265 if (over) {
266 err = 0;
267 goto unlock_and_exit;
268 }
269
270 filp->f_pos = curdirent.d_off;
271 rdir->head += err;
272 }
273 }
274
275unlock_and_exit:
276 mutex_unlock(&rdir->mutex);
277exit:
278 return err;
279}
280
179 281
180/** 282/**
181 * v9fs_dir_release - close a directory 283 * v9fs_dir_release - close a directory
@@ -207,7 +309,7 @@ const struct file_operations v9fs_dir_operations = {
207const struct file_operations v9fs_dir_operations_dotl = { 309const struct file_operations v9fs_dir_operations_dotl = {
208 .read = generic_read_dir, 310 .read = generic_read_dir,
209 .llseek = generic_file_llseek, 311 .llseek = generic_file_llseek,
210 .readdir = v9fs_dir_readdir, 312 .readdir = v9fs_dir_readdir_dotl,
211 .open = v9fs_file_open, 313 .open = v9fs_file_open,
212 .release = v9fs_dir_release, 314 .release = v9fs_dir_release,
213}; 315};
diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h
index 156c26bb8bd7..f1b0b310265d 100644
--- a/include/net/9p/9p.h
+++ b/include/net/9p/9p.h
@@ -133,6 +133,8 @@ enum p9_msg_t {
133 P9_RSTATFS, 133 P9_RSTATFS,
134 P9_TRENAME = 20, 134 P9_TRENAME = 20,
135 P9_RRENAME, 135 P9_RRENAME,
136 P9_TREADDIR = 40,
137 P9_RREADDIR,
136 P9_TVERSION = 100, 138 P9_TVERSION = 100,
137 P9_RVERSION, 139 P9_RVERSION,
138 P9_TAUTH = 102, 140 P9_TAUTH = 102,
@@ -275,6 +277,9 @@ enum p9_qid_t {
275/* ample room for Twrite/Rread header */ 277/* ample room for Twrite/Rread header */
276#define P9_IOHDRSZ 24 278#define P9_IOHDRSZ 24
277 279
280/* Room for readdir header */
281#define P9_READDIRHDRSZ 24
282
278/** 283/**
279 * struct p9_str - length prefixed string type 284 * struct p9_str - length prefixed string type
280 * @len: length of the string 285 * @len: length of the string
@@ -485,6 +490,18 @@ struct p9_rwrite {
485 u32 count; 490 u32 count;
486}; 491};
487 492
493struct p9_treaddir {
494 u32 fid;
495 u64 offset;
496 u32 count;
497};
498
499struct p9_rreaddir {
500 u32 count;
501 u8 *data;
502};
503
504
488struct p9_tclunk { 505struct p9_tclunk {
489 u32 fid; 506 u32 fid;
490}; 507};
diff --git a/include/net/9p/client.h b/include/net/9p/client.h
index 7dd3ed85c782..2ec93685e6db 100644
--- a/include/net/9p/client.h
+++ b/include/net/9p/client.h
@@ -195,6 +195,21 @@ struct p9_fid {
195 struct list_head dlist; /* list of all fids attached to a dentry */ 195 struct list_head dlist; /* list of all fids attached to a dentry */
196}; 196};
197 197
198/**
199 * struct p9_dirent - directory entry structure
200 * @qid: The p9 server qid for this dirent
201 * @d_off: offset to the next dirent
202 * @d_type: type of file
203 * @d_name: file name
204 */
205
206struct p9_dirent {
207 struct p9_qid qid;
208 u64 d_off;
209 unsigned char d_type;
210 char d_name[256];
211};
212
198int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb); 213int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb);
199int p9_client_rename(struct p9_fid *fid, struct p9_fid *newdirfid, char *name); 214int p9_client_rename(struct p9_fid *fid, struct p9_fid *newdirfid, char *name);
200int p9_client_version(struct p9_client *); 215int p9_client_version(struct p9_client *);
@@ -217,6 +232,9 @@ int p9_client_read(struct p9_fid *fid, char *data, char __user *udata,
217 u64 offset, u32 count); 232 u64 offset, u32 count);
218int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, 233int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata,
219 u64 offset, u32 count); 234 u64 offset, u32 count);
235int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset);
236int p9dirent_read(char *buf, int len, struct p9_dirent *dirent,
237 int proto_version);
220struct p9_wstat *p9_client_stat(struct p9_fid *fid); 238struct p9_wstat *p9_client_stat(struct p9_fid *fid);
221int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst); 239int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst);
222 240
diff --git a/net/9p/client.c b/net/9p/client.c
index 37c8da07a80b..a80357483a47 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -1432,3 +1432,50 @@ error:
1432} 1432}
1433EXPORT_SYMBOL(p9_client_rename); 1433EXPORT_SYMBOL(p9_client_rename);
1434 1434
1435int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
1436{
1437 int err, rsize, total;
1438 struct p9_client *clnt;
1439 struct p9_req_t *req;
1440 char *dataptr;
1441
1442 P9_DPRINTK(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n",
1443 fid->fid, (long long unsigned) offset, count);
1444
1445 err = 0;
1446 clnt = fid->clnt;
1447 total = 0;
1448
1449 rsize = fid->iounit;
1450 if (!rsize || rsize > clnt->msize-P9_READDIRHDRSZ)
1451 rsize = clnt->msize - P9_READDIRHDRSZ;
1452
1453 if (count < rsize)
1454 rsize = count;
1455
1456 req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, offset, rsize);
1457 if (IS_ERR(req)) {
1458 err = PTR_ERR(req);
1459 goto error;
1460 }
1461
1462 err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr);
1463 if (err) {
1464 p9pdu_dump(1, req->rc);
1465 goto free_and_error;
1466 }
1467
1468 P9_DPRINTK(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count);
1469
1470 if (data)
1471 memmove(data, dataptr, count);
1472
1473 p9_free_req(clnt, req);
1474 return count;
1475
1476free_and_error:
1477 p9_free_req(clnt, req);
1478error:
1479 return err;
1480}
1481EXPORT_SYMBOL(p9_client_readdir);
diff --git a/net/9p/protocol.c b/net/9p/protocol.c
index 149f82160130..b645c8263538 100644
--- a/net/9p/protocol.c
+++ b/net/9p/protocol.c
@@ -580,3 +580,30 @@ void p9pdu_reset(struct p9_fcall *pdu)
580 pdu->offset = 0; 580 pdu->offset = 0;
581 pdu->size = 0; 581 pdu->size = 0;
582} 582}
583
584int p9dirent_read(char *buf, int len, struct p9_dirent *dirent,
585 int proto_version)
586{
587 struct p9_fcall fake_pdu;
588 int ret;
589 char *nameptr;
590
591 fake_pdu.size = len;
592 fake_pdu.capacity = len;
593 fake_pdu.sdata = buf;
594 fake_pdu.offset = 0;
595
596 ret = p9pdu_readf(&fake_pdu, proto_version, "Qqbs", &dirent->qid,
597 &dirent->d_off, &dirent->d_type, &nameptr);
598 if (ret) {
599 P9_DPRINTK(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret);
600 p9pdu_dump(1, &fake_pdu);
601 goto out;
602 }
603
604 strcpy(dirent->d_name, nameptr);
605
606out:
607 return fake_pdu.offset;
608}
609EXPORT_SYMBOL(p9dirent_read);