]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] tmpfs: shmem_file_write update
authorAndrew Morton <akpm@digeo.com>
Tue, 29 Oct 2002 08:54:47 +0000 (00:54 -0800)
committerChristoph Hellwig <hch@lst.de>
Tue, 29 Oct 2002 08:54:47 +0000 (00:54 -0800)
Patch from Hugh Dickins

Checked shmem_file_write against recent filemap source, and against
2.4 and 2.4-ac: folded in missing fixes, mostly related to far file
positions.  Plus the new kmap_atomic copying technique.  But for now,
as before, no mark_page_accessed or SetPageReferenced in shmem.c: add
those, or whatever, later on when akpm has reviewed usage elsewhere.

mm/shmem.c

index 82d77ab31a2d073fd9c95af8eca40f45b49aa349..8d3dbfffe51c884542999deb2f8f4bb2c9b06ded 100644 (file)
@@ -1062,12 +1062,46 @@ shmem_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
                        send_sig(SIGXFSZ, current, 0);
                        goto out;
                }
-               if (count > limit - pos) {
+               if (pos > 0xFFFFFFFFULL || count > limit - (u32)pos) {
+                       /* send_sig(SIGXFSZ, current, 0); */
+                       count = limit - (u32)pos;
+               }
+       }
+
+       /*
+        *      LFS rule
+        */
+       if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) {
+               if (pos >= MAX_NON_LFS) {
                        send_sig(SIGXFSZ, current, 0);
-                       count = limit - pos;
+                       goto out;
+               }
+               if (count > MAX_NON_LFS - (u32)pos) {
+                       /* send_sig(SIGXFSZ, current, 0); */
+                       count = MAX_NON_LFS - (u32)pos;
                }
        }
 
+       /*
+        *      Are we about to exceed the fs block limit ?
+        *
+        *      If we have written data it becomes a short write
+        *      If we have exceeded without writing data we send
+        *      a signal and give them an EFBIG.
+        *
+        *      Linus frestrict idea will clean these up nicely..
+        */
+       if (pos >= SHMEM_MAX_BYTES) {
+               if (count || pos > SHMEM_MAX_BYTES) {
+                       send_sig(SIGXFSZ, current, 0);
+                       err = -EFBIG;
+                       goto out;
+               }
+               /* zero-length writes at ->s_maxbytes are OK */
+       }
+       if (pos + count > SHMEM_MAX_BYTES)
+               count = SHMEM_MAX_BYTES - pos;
+
        status  = 0;
        if (count) {
                remove_suid(file->f_dentry);
@@ -1077,51 +1111,62 @@ shmem_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
        while (count) {
                unsigned long bytes, index, offset;
                char *kaddr;
+               int left;
 
-               /*
-                * Try to find the page in the cache. If it isn't there,
-                * allocate a free page.
-                */
                offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
                index = pos >> PAGE_CACHE_SHIFT;
                bytes = PAGE_CACHE_SIZE - offset;
-               if (bytes > count) {
+               if (bytes > count)
                        bytes = count;
-               }
 
                /*
                 * We don't hold page lock across copy from user -
                 * what would it guard against? - so no deadlock here.
+                * But it still may be a good idea to prefault below.
                 */
 
                status = shmem_getpage(inode, index, &page, SGP_WRITE);
                if (status)
                        break;
 
-               kaddr = kmap(page);
-               status = __copy_from_user(kaddr+offset, buf, bytes);
-               kunmap(page);
-               if (status)
-                       goto fail_write;
+               left = bytes;
+               if (PageHighMem(page)) {
+                       volatile unsigned char dummy;
+                       __get_user(dummy, buf);
+                       __get_user(dummy, buf + bytes - 1);
 
+                       kaddr = kmap_atomic(page, KM_USER0);
+                       left = __copy_from_user(kaddr + offset, buf, bytes);
+                       kunmap_atomic(kaddr, KM_USER0);
+               }
+               if (left) {
+                       kaddr = kmap(page);
+                       left = __copy_from_user(kaddr + offset, buf, bytes);
+                       kunmap(page);
+               }
                flush_dcache_page(page);
-               if (bytes > 0) {
-                       set_page_dirty(page);
-                       written += bytes;
-                       count -= bytes;
-                       pos += bytes;
-                       buf += bytes;
-                       if (pos > inode->i_size)
-                               inode->i_size = pos;
+               if (left) {
+                       page_cache_release(page);
+                       status = -EFAULT;
+                       break;
                }
-release:
+
+               set_page_dirty(page);
                page_cache_release(page);
 
-               if (status < 0)
-                       break;
+               /*
+                * Balance dirty pages??
+                */
+
+               written += bytes;
+               count -= bytes;
+               pos += bytes;
+               buf += bytes;
+               if (pos > inode->i_size)
+                       inode->i_size = pos;
        }
-       *ppos = pos;
 
+       *ppos = pos;
        err = written ? written : status;
 out:
        /* Short writes give back address space */
@@ -1130,10 +1175,6 @@ out:
 out_nc:
        up(&inode->i_sem);
        return err;
-fail_write:
-       status = -EFAULT;
-       ClearPageUptodate(page);
-       goto release;
 }
 
 static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_t *desc, read_actor_t actor)
@@ -1407,9 +1448,9 @@ static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *s
                spin_lock(&shmem_ilock);
                list_add_tail(&info->list, &shmem_inodes);
                spin_unlock(&shmem_ilock);
-               kaddr = kmap(page);
+               kaddr = kmap_atomic(page, KM_USER0);
                memcpy(kaddr, symname, len);
-               kunmap(page);
+               kunmap_atomic(kaddr, KM_USER0);
                set_page_dirty(page);
                page_cache_release(page);
        }