[PATCH] tmpfs 6/9 shmem_file_write update

From: Hugh Dickins (hugh@veritas.com)
Date: Thu Oct 17 2002 - 18:56:55 EST


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.

--- tmpfs5/mm/shmem.c Thu Oct 17 22:01:29 2002
+++ tmpfs6/mm/shmem.c Thu Oct 17 22:01:39 2002
@@ -1062,12 +1062,46 @@
                         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 @@
         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_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 @@
                 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);
         }

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Wed Oct 23 2002 - 22:00:38 EST