Skip to contentSkip to conversation

Dataloss on NFS-clients who modify an mmap()'d area after closing the file-descriptor

RFC In review Niels de Vos 4 participants 7 messages May 3, 2012 – May 7, 2012 4 messages not in the archive

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

RFC 1/1 Dataloss on NFS-clients who modify an mmap()'d area after closing the file-descriptor

When an application on an NFS-client (tested with NFSv3) executes the following steps, data written after the close() is never flushed to the server:

1. open()
2. mmap()
3. close()
4. <modify data in the mmap'ed area>
5. munmap()

Dropping the caches (via /proc/sys/vm/drop_caches) or unmounting does not result in the data being sent to the server.

The man-page for mmap (man 2 mmap) does mention that closing the file- descriptor does not munmap() the area. Using the mmap'ed area after a close() sound valid to me (even if it may be bad practice).

Investigation and checking showed that the NFS-client does not handle munmap(), and only flushes on close(). To solve this problem, least two solutions can be proposed:

a. f_ops->release() is called on munmap() as well as on close(),
therefore release() can be used to flush data as well.
b. In the 'struct vm_operations_struct' add a .close to the
'struct vm_area_struct' on calling mmap()/nfs_file_mmap() and flush the data in the new close() function.

Solution a. contains currently very few code changes:

fs/nfs/inode.c +2 -0
@@ -713,6 +713,8 @@ int nfs_open(struct inode *inode, struct file *filp)
 
 int nfs_release(struct inode *inode, struct file *filp)
 {
+       if (S_ISREG(inode->i_mode) && inode->i_mapping->nrpages != 0) {
+               nfs_sync_mapping(inode->i_mapping);
        nfs_file_clear_open_context(filp);
        return 0;
 }

The disadvantage is, that nfs_release() is called on close() too. That means this causes a flushing of dirty pages, and just after that the nfs_file_clear_open_context() might flush again. The advantage is that it is possible (though not done at the moment) to return an error in case flushing failed.

Solution b. does not provide an option to return an error, but does not get called on each close():

fs/nfs/file.c +8 -0
@@ -547,9 +547,17 @@ out:
 	return ret;
 }
 
+static void nfs_vm_close(struct vm_area_struct * vma)
+{
+	struct file *filp = vma->vm_file;
+
+	nfs_file_flush(filp, (fl_owner_t)filp);
+}
+
 static const struct vm_operations_struct nfs_file_vm_ops = {
 	.fault = filemap_fault,
 	.page_mkwrite = nfs_vm_page_mkwrite,
+	.close = nfs_vm_close,
 };
 
 static int nfs_need_sync_write(struct file *filp, struct inode *inode)

I would like some feedback on what solution is most acceptable, or any other suggestions.

Many thanks,
Niels
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): [email protected]

#2 lore raw

T24gVGh1LCAyMDEyLTA1LTAzIGF0IDE3OjM0ICswMjAwLCBOaWVscyBkZSBWb3Mgd3JvdGU6DQo+ IFdoZW4gYW4gYXBwbGljYXRpb24gb24gYW4gTkZTLWNsaWVudCAodGVzdGVkIHdpdGggTkZTdjMp IGV4ZWN1dGVzIHRoZQ0KPiBmb2xsb3dpbmcgc3RlcHMsIGRhdGEgd3JpdHRlbiBhZnRlciB0aGUg Y2xvc2UoKSBpcyBuZXZlciBmbHVzaGVkIHRvIHRoZQ0KPiBzZXJ2ZXI6DQo+IA0KPiAxLiBvcGVu KCkNCj4gMi4gbW1hcCgpDQo+IDMuIGNsb3NlKCkNCj4gNC4gPG1vZGlmeSBkYXRhIGluIHRoZSBt bWFwJ2VkIGFyZWE+DQo+IDUuIG11bm1hcCgpDQo+IA0KPiBEcm9wcGluZyB0aGUgY2FjaGVzICh2 aWEgL3Byb2Mvc3lzL3ZtL2Ryb3BfY2FjaGVzKSBvciB1bm1vdW50aW5nIGRvZXMgbm90DQo+IHJl c3VsdCBpbiB0aGUgZGF0YSBiZWluZyBzZW50IHRvIHRoZSBzZXJ2ZXIuDQo+IA0KPiBUaGUgbWFu LXBhZ2UgZm9yIG1tYXAgKG1hbiAyIG1tYXApIGRvZXMgbWVudGlvbiB0aGF0IGNsb3NpbmcgdGhl IGZpbGUtDQo+IGRlc2NyaXB0b3IgZG9lcyBub3QgbXVubWFwKCkgdGhlIGFyZWEuIFVzaW5nIHRo ZSBtbWFwJ2VkIGFyZWEgYWZ0ZXIgYQ0KPiBjbG9zZSgpIHNvdW5kIHZhbGlkIHRvIG1lIChldmVu IGlmIGl0IG1heSBiZSBiYWQgcHJhY3RpY2UpLg0KPiANCj4gSW52ZXN0aWdhdGlvbiBhbmQgY2hl Y2tpbmcgc2hvd2VkIHRoYXQgdGhlIE5GUy1jbGllbnQgZG9lcyBub3QgaGFuZGxlDQo+IG11bm1h cCgpLCBhbmQgb25seSBmbHVzaGVzIG9uIGNsb3NlKCkuIFRvIHNvbHZlIHRoaXMgcHJvYmxlbSwg bGVhc3QgdHdvDQo+IHNvbHV0aW9ucyBjYW4gYmUgcHJvcG9zZWQ6DQo+IA0KPiBhLiBmX29wcy0+ cmVsZWFzZSgpIGlzIGNhbGxlZCBvbiBtdW5tYXAoKSBhcyB3ZWxsIGFzIG9uIGNsb3NlKCksDQo+ ICAgICB0aGVyZWZvcmUgcmVsZWFzZSgpIGNhbiBiZSB1c2VkIHRvIGZsdXNoIGRhdGEgYXMgd2Vs bC4NCj4gYi4gSW4gdGhlICdzdHJ1Y3Qgdm1fb3BlcmF0aW9uc19zdHJ1Y3QnIGFkZCBhIC5jbG9z ZSB0byB0aGUNCj4gICAgICdzdHJ1Y3Qgdm1fYXJlYV9zdHJ1Y3QnIG9uIGNhbGxpbmcgbW1hcCgp L25mc19maWxlX21tYXAoKSBhbmQgZmx1c2gNCj4gICAgIHRoZSBkYXRhIGluIHRoZSBuZXcgY2xv c2UoKSBmdW5jdGlvbi4NCj4gDQo+IFNvbHV0aW9uIGEuIGNvbnRhaW5zIGN1cnJlbnRseSB2ZXJ5 IGZldyBjb2RlIGNoYW5nZXM6DQo+IA0KPiAtLS0gYS9mcy9uZnMvaW5vZGUuYw0KPiArKysgYi9m cy9uZnMvaW5vZGUuYw0KPiBAQCAtNzEzLDYgKzcxMyw4IEBAIGludCBuZnNfb3BlbihzdHJ1Y3Qg aW5vZGUgKmlub2RlLCBzdHJ1Y3QgZmlsZSAqZmlscCkNCj4gDQo+ICAgaW50IG5mc19yZWxlYXNl KHN0cnVjdCBpbm9kZSAqaW5vZGUsIHN0cnVjdCBmaWxlICpmaWxwKQ0KPiAgIHsNCj4gKyAgICAg ICBpZiAoU19JU1JFRyhpbm9kZS0+aV9tb2RlKSAmJiBpbm9kZS0+aV9tYXBwaW5nLT5ucnBhZ2Vz ICE9IDApIHsNCj4gKyAgICAgICAgICAgICAgIG5mc19zeW5jX21hcHBpbmcoaW5vZGUtPmlfbWFw cGluZyk7DQo+ICAgICAgICAgIG5mc19maWxlX2NsZWFyX29wZW5fY29udGV4dChmaWxwKTsNCj4g ICAgICAgICAgcmV0dXJuIDA7DQo+ICAgfQ0KPiANCj4gVGhlIGRpc2FkdmFudGFnZSBpcywgdGhh dCBuZnNfcmVsZWFzZSgpIGlzIGNhbGxlZCBvbiBjbG9zZSgpIHRvby4gVGhhdA0KPiBtZWFucyB0 aGlzIGNhdXNlcyBhIGZsdXNoaW5nIG9mIGRpcnR5IHBhZ2VzLCBhbmQganVzdCBhZnRlciB0aGF0 IHRoZQ0KPiBuZnNfZmlsZV9jbGVhcl9vcGVuX2NvbnRleHQoKSBtaWdodCBmbHVzaCBhZ2Fpbi4g VGhlIGFkdmFudGFnZSBpcyB0aGF0DQo+IGl0IGlzIHBvc3NpYmxlICh0aG91Z2ggbm90IGRvbmUg YXQgdGhlIG1vbWVudCkgdG8gcmV0dXJuIGFuIGVycm9yIGluDQo+IGNhc2UgZmx1c2hpbmcgZmFp bGVkLg0KPiANCj4gU29sdXRpb24gYi4gZG9lcyBub3QgcHJvdmlkZSBhbiBvcHRpb24gdG8gcmV0 dXJuIGFuIGVycm9yLCBidXQgZG9lcyBub3QNCj4gZ2V0IGNhbGxlZCBvbiBlYWNoIGNsb3NlKCk6 DQo+IA0KPiAtLS0gYS9mcy9uZnMvZmlsZS5jDQo+ICsrKyBiL2ZzL25mcy9maWxlLmMNCj4gQEAg LTU0Nyw5ICs1NDcsMTcgQEAgb3V0Og0KPiAgIAlyZXR1cm4gcmV0Ow0KPiAgIH0NCj4gDQo+ICtz dGF0aWMgdm9pZCBuZnNfdm1fY2xvc2Uoc3RydWN0IHZtX2FyZWFfc3RydWN0ICogdm1hKQ0KPiAr ew0KPiArCXN0cnVjdCBmaWxlICpmaWxwID0gdm1hLT52bV9maWxlOw0KPiArDQo+ICsJbmZzX2Zp bGVfZmx1c2goZmlscCwgKGZsX293bmVyX3QpZmlscCk7DQo+ICt9DQo+ICsNCj4gICBzdGF0aWMg Y29uc3Qgc3RydWN0IHZtX29wZXJhdGlvbnNfc3RydWN0IG5mc19maWxlX3ZtX29wcyA9IHsNCj4g ICAJLmZhdWx0ID0gZmlsZW1hcF9mYXVsdCwNCj4gICAJLnBhZ2VfbWt3cml0ZSA9IG5mc192bV9w YWdlX21rd3JpdGUsDQo+ICsJLmNsb3NlID0gbmZzX3ZtX2Nsb3NlLA0KPiAgIH07DQo+IA0KPiAg IHN0YXRpYyBpbnQgbmZzX25lZWRfc3luY193cml0ZShzdHJ1Y3QgZmlsZSAqZmlscCwgc3RydWN0 IGlub2RlICppbm9kZSkNCj4gDQo+IEkgd291bGQgbGlrZSBzb21lIGZlZWRiYWNrIG9uIHdoYXQg c29sdXRpb24gaXMgbW9zdCBhY2NlcHRhYmxlLCBvciBhbnkNCj4gb3RoZXIgc3VnZ2VzdGlvbnMu DQoNCk5laXRoZXIgc29sdXRpb24gaXMgYWNjZXB0YWJsZS4gVGhpcyBpc24ndCBhIGNsb3NlLXRv LW9wZW4gY2FjaGUNCmNvbnNpc3RlbmN5IGlzc3VlLg0KDQpUaGUgc3ludGF4IG9mIG1tYXAoKSBm b3IgYm90aCBibG9jayBhbmQgTkZTIG1vdW50cyBpcyB0aGUgc2FtZTogd3JpdGVzDQphcmUgbm90 IGd1YXJhbnRlZWQgdG8gaGl0IHRoZSBkaXNrIHVudGlsIHlvdXIgYXBwbGljYXRpb24gZXhwbGlj aXRseQ0KY2FsbHMgbXN5bmMoKS4NCg0KLS0gDQpUcm9uZCBNeWtsZWJ1c3QNCkxpbnV4IE5GUyBj bGllbnQgbWFpbnRhaW5lcg0KDQpOZXRBcHANClRyb25kLk15a2xlYnVzdEBuZXRhcHAuY29tDQp3 d3cubmV0YXBwLmNvbQ0KDQo=
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

#3 lore raw
Quoted 78 lines · Trond
Quoted 71 lines · Niels de Vos

When an application on an NFS-client (tested with NFSv3) executes the following steps, data written after the close() is never flushed to the server:

1. open()
2. mmap()
3. close()
4.<modify data in the mmap'ed area>
5. munmap()

Dropping the caches (via /proc/sys/vm/drop_caches) or unmounting does not result in the data being sent to the server.

The man-page for mmap (man 2 mmap) does mention that closing the file- descriptor does not munmap() the area. Using the mmap'ed area after a close() sound valid to me (even if it may be bad practice).

Investigation and checking showed that the NFS-client does not handle munmap(), and only flushes on close(). To solve this problem, least two solutions can be proposed:

a. f_ops->release() is called on munmap() as well as on close(),
therefore release() can be used to flush data as well.
b. In the 'struct vm_operations_struct' add a .close to the
'struct vm_area_struct' on calling mmap()/nfs_file_mmap() and flush the data in the new close() function.

Solution a. contains currently very few code changes:

fs/nfs/inode.c +2 -0
@@ -713,6 +713,8 @@ int nfs_open(struct inode *inode, struct file *filp)
 
   int nfs_release(struct inode *inode, struct file *filp)
   {
+       if (S_ISREG(inode->i_mode)&&  inode->i_mapping->nrpages != 0) {
+               nfs_sync_mapping(inode->i_mapping);
          nfs_file_clear_open_context(filp);
          return 0;
   }

The disadvantage is, that nfs_release() is called on close() too. That means this causes a flushing of dirty pages, and just after that the nfs_file_clear_open_context() might flush again. The advantage is that it is possible (though not done at the moment) to return an error in case flushing failed.

Solution b. does not provide an option to return an error, but does not get called on each close():

fs/nfs/file.c +8 -0
@@ -547,9 +547,17 @@ out:
   	return ret;
   }
 
+static void nfs_vm_close(struct vm_area_struct * vma)
+{
+	struct file *filp = vma->vm_file;
+
+	nfs_file_flush(filp, (fl_owner_t)filp);
+}
+
   static const struct vm_operations_struct nfs_file_vm_ops = {
   	.fault = filemap_fault,
   	.page_mkwrite = nfs_vm_page_mkwrite,
+	.close = nfs_vm_close,
   };
 
   static int nfs_need_sync_write(struct file *filp, struct inode *inode)
Quoted 2 lines

I would like some feedback on what solution is most acceptable, or any other suggestions.

Neither solution is acceptable. This isn't a close-to-open cache consistency issue.

Quoted 4 lines

The syntax of mmap() for both block and NFS mounts is the same: writes are not guaranteed to hit the disk until your application explicitly calls msync().

Okay, that makes sense. But if the application never calls msync(), and just munmap()'s the area, when should the changes be written? I did not expect that unmounting just disregards the data.

Any suggestions on correcting this (if it should be corrected) are welcome.

Thanks again,
Niels

A message that is not in the archive (sent off-list or not yet received): [email protected]

#5 lore raw

T24gVGh1LCAyMDEyLTA1LTAzIGF0IDE5OjA3ICswMjAwLCBOaWVscyBkZSBWb3Mgd3JvdGU6DQo+ IE9uIDA1LzAzLzIwMTIgMDU6NDMgUE0sIE15a2xlYnVzdCwgVHJvbmQgd3JvdGU6DQo+ICA+IE9u IFRodSwgMjAxMi0wNS0wMyBhdCAxNzozNCArMDIwMCwgTmllbHMgZGUgVm9zIHdyb3RlOg0KPiAg Pj4gV2hlbiBhbiBhcHBsaWNhdGlvbiBvbiBhbiBORlMtY2xpZW50ICh0ZXN0ZWQgd2l0aCBORlN2 MykgZXhlY3V0ZXMgdGhlDQo+ICA+PiBmb2xsb3dpbmcgc3RlcHMsIGRhdGEgd3JpdHRlbiBhZnRl ciB0aGUgY2xvc2UoKSBpcyBuZXZlciBmbHVzaGVkIHRvIHRoZQ0KPiAgPj4gc2VydmVyOg0KPiAg Pj4NCj4gID4+IDEuIG9wZW4oKQ0KPiAgPj4gMi4gbW1hcCgpDQo+ICA+PiAzLiBjbG9zZSgpDQo+ ICA+PiA0Ljxtb2RpZnkgZGF0YSBpbiB0aGUgbW1hcCdlZCBhcmVhPg0KPiAgPj4gNS4gbXVubWFw KCkNCj4gID4+DQo+ICA+PiBEcm9wcGluZyB0aGUgY2FjaGVzICh2aWEgL3Byb2Mvc3lzL3ZtL2Ry b3BfY2FjaGVzKSBvciB1bm1vdW50aW5nIGRvZXMgbm90DQo+ICA+PiByZXN1bHQgaW4gdGhlIGRh dGEgYmVpbmcgc2VudCB0byB0aGUgc2VydmVyLg0KPiAgPj4NCj4gID4+IFRoZSBtYW4tcGFnZSBm b3IgbW1hcCAobWFuIDIgbW1hcCkgZG9lcyBtZW50aW9uIHRoYXQgY2xvc2luZyB0aGUgZmlsZS0N Cj4gID4+IGRlc2NyaXB0b3IgZG9lcyBub3QgbXVubWFwKCkgdGhlIGFyZWEuIFVzaW5nIHRoZSBt bWFwJ2VkIGFyZWEgYWZ0ZXIgYQ0KPiAgPj4gY2xvc2UoKSBzb3VuZCB2YWxpZCB0byBtZSAoZXZl biBpZiBpdCBtYXkgYmUgYmFkIHByYWN0aWNlKS4NCj4gID4+DQo+ICA+PiBJbnZlc3RpZ2F0aW9u IGFuZCBjaGVja2luZyBzaG93ZWQgdGhhdCB0aGUgTkZTLWNsaWVudCBkb2VzIG5vdCBoYW5kbGUN Cj4gID4+IG11bm1hcCgpLCBhbmQgb25seSBmbHVzaGVzIG9uIGNsb3NlKCkuIFRvIHNvbHZlIHRo aXMgcHJvYmxlbSwgbGVhc3QgdHdvDQo+ICA+PiBzb2x1dGlvbnMgY2FuIGJlIHByb3Bvc2VkOg0K PiAgPj4NCj4gID4+IGEuIGZfb3BzLT5yZWxlYXNlKCkgaXMgY2FsbGVkIG9uIG11bm1hcCgpIGFz IHdlbGwgYXMgb24gY2xvc2UoKSwNCj4gID4+ICAgICAgdGhlcmVmb3JlIHJlbGVhc2UoKSBjYW4g YmUgdXNlZCB0byBmbHVzaCBkYXRhIGFzIHdlbGwuDQo+ICA+PiBiLiBJbiB0aGUgJ3N0cnVjdCB2 bV9vcGVyYXRpb25zX3N0cnVjdCcgYWRkIGEgLmNsb3NlIHRvIHRoZQ0KPiAgPj4gICAgICAnc3Ry dWN0IHZtX2FyZWFfc3RydWN0JyBvbiBjYWxsaW5nIG1tYXAoKS9uZnNfZmlsZV9tbWFwKCkgYW5k IGZsdXNoDQo+ICA+PiAgICAgIHRoZSBkYXRhIGluIHRoZSBuZXcgY2xvc2UoKSBmdW5jdGlvbi4N Cj4gID4+DQo+ICA+PiBTb2x1dGlvbiBhLiBjb250YWlucyBjdXJyZW50bHkgdmVyeSBmZXcgY29k ZSBjaGFuZ2VzOg0KPiAgPj4NCj4gID4+IC0tLSBhL2ZzL25mcy9pbm9kZS5jDQo+ICA+PiArKysg Yi9mcy9uZnMvaW5vZGUuYw0KPiAgPj4gQEAgLTcxMyw2ICs3MTMsOCBAQCBpbnQgbmZzX29wZW4o c3RydWN0IGlub2RlICppbm9kZSwgc3RydWN0IGZpbGUgKmZpbHApDQo+ICA+Pg0KPiAgPj4gICAg aW50IG5mc19yZWxlYXNlKHN0cnVjdCBpbm9kZSAqaW5vZGUsIHN0cnVjdCBmaWxlICpmaWxwKQ0K PiAgPj4gICAgew0KPiAgPj4gKyAgICAgICBpZiAoU19JU1JFRyhpbm9kZS0+aV9tb2RlKSYmICBp bm9kZS0+aV9tYXBwaW5nLT5ucnBhZ2VzICE9IDApIHsNCj4gID4+ICsgICAgICAgICAgICAgICBu ZnNfc3luY19tYXBwaW5nKGlub2RlLT5pX21hcHBpbmcpOw0KPiAgPj4gICAgICAgICAgIG5mc19m aWxlX2NsZWFyX29wZW5fY29udGV4dChmaWxwKTsNCj4gID4+ICAgICAgICAgICByZXR1cm4gMDsN Cj4gID4+ICAgIH0NCj4gID4+DQo+ICA+PiBUaGUgZGlzYWR2YW50YWdlIGlzLCB0aGF0IG5mc19y ZWxlYXNlKCkgaXMgY2FsbGVkIG9uIGNsb3NlKCkgdG9vLiBUaGF0DQo+ICA+PiBtZWFucyB0aGlz IGNhdXNlcyBhIGZsdXNoaW5nIG9mIGRpcnR5IHBhZ2VzLCBhbmQganVzdCBhZnRlciB0aGF0IHRo ZQ0KPiAgPj4gbmZzX2ZpbGVfY2xlYXJfb3Blbl9jb250ZXh0KCkgbWlnaHQgZmx1c2ggYWdhaW4u IFRoZSBhZHZhbnRhZ2UgaXMgdGhhdA0KPiAgPj4gaXQgaXMgcG9zc2libGUgKHRob3VnaCBub3Qg ZG9uZSBhdCB0aGUgbW9tZW50KSB0byByZXR1cm4gYW4gZXJyb3IgaW4NCj4gID4+IGNhc2UgZmx1 c2hpbmcgZmFpbGVkLg0KPiAgPj4NCj4gID4+IFNvbHV0aW9uIGIuIGRvZXMgbm90IHByb3ZpZGUg YW4gb3B0aW9uIHRvIHJldHVybiBhbiBlcnJvciwgYnV0IGRvZXMgbm90DQo+ICA+PiBnZXQgY2Fs bGVkIG9uIGVhY2ggY2xvc2UoKToNCj4gID4+DQo+ICA+PiAtLS0gYS9mcy9uZnMvZmlsZS5jDQo+ ICA+PiArKysgYi9mcy9uZnMvZmlsZS5jDQo+ICA+PiBAQCAtNTQ3LDkgKzU0NywxNyBAQCBvdXQ6 DQo+ICA+PiAgICAJcmV0dXJuIHJldDsNCj4gID4+ICAgIH0NCj4gID4+DQo+ICA+PiArc3RhdGlj IHZvaWQgbmZzX3ZtX2Nsb3NlKHN0cnVjdCB2bV9hcmVhX3N0cnVjdCAqIHZtYSkNCj4gID4+ICt7 DQo+ICA+PiArCXN0cnVjdCBmaWxlICpmaWxwID0gdm1hLT52bV9maWxlOw0KPiAgPj4gKw0KPiAg Pj4gKwluZnNfZmlsZV9mbHVzaChmaWxwLCAoZmxfb3duZXJfdClmaWxwKTsNCj4gID4+ICt9DQo+ ICA+PiArDQo+ICA+PiAgICBzdGF0aWMgY29uc3Qgc3RydWN0IHZtX29wZXJhdGlvbnNfc3RydWN0 IG5mc19maWxlX3ZtX29wcyA9IHsNCj4gID4+ICAgIAkuZmF1bHQgPSBmaWxlbWFwX2ZhdWx0LA0K PiAgPj4gICAgCS5wYWdlX21rd3JpdGUgPSBuZnNfdm1fcGFnZV9ta3dyaXRlLA0KPiAgPj4gKwku Y2xvc2UgPSBuZnNfdm1fY2xvc2UsDQo+ICA+PiAgICB9Ow0KPiAgPj4NCj4gID4+ICAgIHN0YXRp YyBpbnQgbmZzX25lZWRfc3luY193cml0ZShzdHJ1Y3QgZmlsZSAqZmlscCwgc3RydWN0IGlub2Rl ICppbm9kZSkNCj4gID4+DQo+ICA+PiBJIHdvdWxkIGxpa2Ugc29tZSBmZWVkYmFjayBvbiB3aGF0 IHNvbHV0aW9uIGlzIG1vc3QgYWNjZXB0YWJsZSwgb3IgYW55DQo+ICA+PiBvdGhlciBzdWdnZXN0 aW9ucy4NCj4gID4NCj4gID4gTmVpdGhlciBzb2x1dGlvbiBpcyBhY2NlcHRhYmxlLiBUaGlzIGlz bid0IGEgY2xvc2UtdG8tb3BlbiBjYWNoZQ0KPiAgPiBjb25zaXN0ZW5jeSBpc3N1ZS4NCj4gID4N Cj4gID4gVGhlIHN5bnRheCBvZiBtbWFwKCkgZm9yIGJvdGggYmxvY2sgYW5kIE5GUyBtb3VudHMg aXMgdGhlIHNhbWU6IHdyaXRlcw0KPiAgPiBhcmUgbm90IGd1YXJhbnRlZWQgdG8gaGl0IHRoZSBk aXNrIHVudGlsIHlvdXIgYXBwbGljYXRpb24gZXhwbGljaXRseQ0KPiAgPiBjYWxscyBtc3luYygp Lg0KPiAgPg0KPiANCj4gT2theSwgdGhhdCBtYWtlcyBzZW5zZS4gQnV0IGlmIHRoZSBhcHBsaWNh dGlvbiBuZXZlciBjYWxscyBtc3luYygpLCBhbmQNCj4ganVzdCBtdW5tYXAoKSdzIHRoZSBhcmVh LCB3aGVuIHNob3VsZCB0aGUgY2hhbmdlcyBiZSB3cml0dGVuPyBJIGRpZCBub3QNCj4gZXhwZWN0 IHRoYXQgdW5tb3VudGluZyBqdXN0IGRpc3JlZ2FyZHMgdGhlIGRhdGEuDQoNClRoYXQgc3VnZ2Vz dHMgdGhhdCB0aGUgVk0gaXMgZmFpbGluZyB0byBkaXJ0eSB0aGUgcGFnZXMgb24gbXVubWFwKCkN CmJlZm9yZSByZWxlYXNpbmcgdGhlIHZtYS0+dm1fZmlsZS4gSWYgc28sIHRoZW4gdGhhdCB3b3Vs ZCBiZSBhIFZNIGJ1Zy4uLg0KDQotLSANClRyb25kIE15a2xlYnVzdA0KTGludXggTkZTIGNsaWVu dCBtYWludGFpbmVyDQoNCk5ldEFwcA0KVHJvbmQuTXlrbGVidXN0QG5ldGFwcC5jb20NCnd3dy5u ZXRhcHAuY29tDQoNCg==
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): 1336066014.5385.41.camel-SyLVLa/[email protected]

#7 lore raw
Quoted 85 lines · Trond
Quoted 84 lines · Niels de Vos
Quoted 82 lines · Trond
Quoted 69 lines · Niels de Vos

When an application on an NFS-client (tested with NFSv3) executes the following steps, data written after the close() is never flushed to the server:

1. open()
2. mmap()
3. close()
4.<modify data in the mmap'ed area>
5. munmap()

Dropping the caches (via /proc/sys/vm/drop_caches) or unmounting does not result in the data being sent to the server.

The man-page for mmap (man 2 mmap) does mention that closing the file- descriptor does not munmap() the area. Using the mmap'ed area after a close() sound valid to me (even if it may be bad practice).

Investigation and checking showed that the NFS-client does not handle munmap(), and only flushes on close(). To solve this problem, least two solutions can be proposed:

a. f_ops->release() is called on munmap() as well as on close(),
therefore release() can be used to flush data as well.
b. In the 'struct vm_operations_struct' add a .close to the
'struct vm_area_struct' on calling mmap()/nfs_file_mmap() and flush the data in the new close() function.

Solution a. contains currently very few code changes:

--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -713,6 +713,8 @@ int nfs_open(struct inode *inode, struct file *filp)

int nfs_release(struct inode *inode, struct file *filp) {
+ if (S_ISREG(inode->i_mode)&& inode->i_mapping->nrpages != 0) {

 +               nfs_sync_mapping(inode->i_mapping);
           nfs_file_clear_open_context(filp);
           return 0;
    }

The disadvantage is, that nfs_release() is called on close() too. That means this causes a flushing of dirty pages, and just after that the nfs_file_clear_open_context() might flush again. The advantage is that it is possible (though not done at the moment) to return an error in case flushing failed.

Solution b. does not provide an option to return an error, but does not get called on each close():

--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -547,9 +547,17 @@ out:

    	return ret;
    }

+static void nfs_vm_close(struct vm_area_struct * vma) +{
+ struct file *filp = vma->vm_file;
+
+ nfs_file_flush(filp, (fl_owner_t)filp);
+}
+
static const struct vm_operations_struct nfs_file_vm_ops = {
.fault = filemap_fault,
.page_mkwrite = nfs_vm_page_mkwrite,
+ .close = nfs_vm_close,
};

Quoted 4 lines

static int nfs_need_sync_write(struct file *filp, struct inode *inode)

I would like some feedback on what solution is most acceptable, or any other suggestions.

Neither solution is acceptable. This isn't a close-to-open cache consistency issue.

The syntax of mmap() for both block and NFS mounts is the same: writes are not guaranteed to hit the disk until your application explicitly calls msync().

Quoted 7 lines
Quoted 3 lines

Okay, that makes sense. But if the application never calls msync(), and just munmap()'s the area, when should the changes be written? I did not expect that unmounting just disregards the data.

That suggests that the VM is failing to dirty the pages on munmap() before releasing the vma->vm_file. If so, then that would be a VM bug...

I've checked if the VM tags the pages as dirty:
- f_ops->release() is called on munmap(). An added printk there, shows that inode->i_state is set to I_DIRTY_PAGE.
- mapping_tagged(filp->f_mapping, PAGECACHE_TAG_DIRTY) also returns true

From my understanding this is what the VM is expected to do, and the pages are marked dirty correctly.

However, nfs_inode->ndirty and nfs_inode->ncommit are both 0. It is unclear to me how the VM is supposed to interact with the nfs_inode. Some clarification or suggestion what to look into would be much appreciated.

Cheers,
Niels
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

#8 lore raw
Quoted 99 lines · Niels de Vos
Quoted 85 lines · Trond
Quoted 84 lines · Niels de Vos
Quoted 82 lines · Trond
Quoted 69 lines · Niels de Vos

When an application on an NFS-client (tested with NFSv3) executes the following steps, data written after the close() is never flushed to the server:

1. open()
2. mmap()
3. close()
4.<modify data in the mmap'ed area>
5. munmap()

Dropping the caches (via /proc/sys/vm/drop_caches) or unmounting does not result in the data being sent to the server.

The man-page for mmap (man 2 mmap) does mention that closing the file- descriptor does not munmap() the area. Using the mmap'ed area after a close() sound valid to me (even if it may be bad practice).

Investigation and checking showed that the NFS-client does not handle munmap(), and only flushes on close(). To solve this problem, least two solutions can be proposed:

a. f_ops->release() is called on munmap() as well as on close(),
therefore release() can be used to flush data as well.
b. In the 'struct vm_operations_struct' add a .close to the
'struct vm_area_struct' on calling mmap()/nfs_file_mmap() and flush the data in the new close() function.

Solution a. contains currently very few code changes:

--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -713,6 +713,8 @@ int nfs_open(struct inode *inode, struct file *filp)

int nfs_release(struct inode *inode, struct file *filp) {
+ if (S_ISREG(inode->i_mode)&& inode->i_mapping->nrpages != 0) {

 +               nfs_sync_mapping(inode->i_mapping);
           nfs_file_clear_open_context(filp);
           return 0;
    }

The disadvantage is, that nfs_release() is called on close() too. That means this causes a flushing of dirty pages, and just after that the nfs_file_clear_open_context() might flush again. The advantage is that it is possible (though not done at the moment) to return an error in case flushing failed.

Solution b. does not provide an option to return an error, but does not get called on each close():

--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -547,9 +547,17 @@ out:

    	return ret;
    }

+static void nfs_vm_close(struct vm_area_struct * vma) +{
+ struct file *filp = vma->vm_file;
+
+ nfs_file_flush(filp, (fl_owner_t)filp);
+}
+
static const struct vm_operations_struct nfs_file_vm_ops = {
.fault = filemap_fault,
.page_mkwrite = nfs_vm_page_mkwrite,
+ .close = nfs_vm_close,
};

Quoted 4 lines

static int nfs_need_sync_write(struct file *filp, struct inode *inode)

I would like some feedback on what solution is most acceptable, or any other suggestions.

Neither solution is acceptable. This isn't a close-to-open cache consistency issue.

The syntax of mmap() for both block and NFS mounts is the same: writes are not guaranteed to hit the disk until your application explicitly calls msync().

Quoted 7 lines
Quoted 3 lines

Okay, that makes sense. But if the application never calls msync(), and just munmap()'s the area, when should the changes be written? I did not expect that unmounting just disregards the data.

That suggests that the VM is failing to dirty the pages on munmap() before releasing the vma->vm_file. If so, then that would be a VM bug...

I've checked if the VM tags the pages as dirty:
- f_ops->release() is called on munmap(). An added printk there, shows that inode->i_state is set to I_DIRTY_PAGE.
- mapping_tagged(filp->f_mapping, PAGECACHE_TAG_DIRTY) also returns true

Quoted 7 lines

From my understanding this is what the VM is expected to do, and the
pages are marked dirty correctly.

However, nfs_inode->ndirty and nfs_inode->ncommit are both 0. It is unclear to me how the VM is supposed to interact with the nfs_inode. Some clarification or suggestion what to look into would be much appreciated.

The first time the page is touched, it will to trigger a ->pg_mkwrite(), which in the case of NFS will set up the necessary tracking structures to ensure that the page is written out using the correct credentials etc. In the case of NFSv4, it will also ensure that the file doesn't get closed on the server until the page is written out to disk.

When the page is cleaned (i.e. something calls clear_page_dirty_for_io() as part of a write to disk), the call to page_mkclean() is supposed to re-write-protect the pte, ensuring that any future changes will re-trigger pg_mkwrite().

You should be able to check if/when nfs_vm_page_mkwrite() is triggered using 'rpcdebug -m nfs -s pagecache' to turn on the NFS page cache debugging printks.

Signature · 6 lines
Trond Myklebust
Linux NFS client maintainer

NetApp
[email protected]
www.netapp.com

A message that is not in the archive (sent off-list or not yet received): 1336156178.2582.15.camel-SyLVLa/[email protected]

#10 lore raw
Quoted 114 lines · Trond
Quoted 99 lines · Niels de Vos
Quoted 85 lines · Trond
Quoted 84 lines · Niels de Vos
Quoted 82 lines · Trond
Quoted 69 lines · Niels de Vos

When an application on an NFS-client (tested with NFSv3) executes the following steps, data written after the close() is never flushed to the server:

1. open()
2. mmap()
3. close()
4.<modify data in the mmap'ed area>
5. munmap()

Dropping the caches (via /proc/sys/vm/drop_caches) or unmounting does not result in the data being sent to the server.

The man-page for mmap (man 2 mmap) does mention that closing the file- descriptor does not munmap() the area. Using the mmap'ed area after a close() sound valid to me (even if it may be bad practice).

Investigation and checking showed that the NFS-client does not handle munmap(), and only flushes on close(). To solve this problem, least two solutions can be proposed:

a. f_ops->release() is called on munmap() as well as on close(),
therefore release() can be used to flush data as well.
b. In the 'struct vm_operations_struct' add a .close to the
'struct vm_area_struct' on calling mmap()/nfs_file_mmap() and flush the data in the new close() function.

Solution a. contains currently very few code changes:

--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -713,6 +713,8 @@ int nfs_open(struct inode *inode, struct file *filp)

int nfs_release(struct inode *inode, struct file *filp) {
+ if (S_ISREG(inode->i_mode)&& inode->i_mapping->nrpages != 0) {

  +               nfs_sync_mapping(inode->i_mapping);
            nfs_file_clear_open_context(filp);
            return 0;
     }

The disadvantage is, that nfs_release() is called on close() too. That means this causes a flushing of dirty pages, and just after that the nfs_file_clear_open_context() might flush again. The advantage is that it is possible (though not done at the moment) to return an error in case flushing failed.

Solution b. does not provide an option to return an error, but does not get called on each close():

--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -547,9 +547,17 @@ out:

     	return ret;
     }
  +static void nfs_vm_close(struct vm_area_struct * vma)
  +{
  +	struct file *filp = vma->vm_file;
  +
  +	nfs_file_flush(filp, (fl_owner_t)filp);
  +}
  +
     static const struct vm_operations_struct nfs_file_vm_ops = {
     	.fault = filemap_fault,
     	.page_mkwrite = nfs_vm_page_mkwrite,
  +	.close = nfs_vm_close,
     };
Quoted 4 lines

static int nfs_need_sync_write(struct file *filp, struct inode *inode)

I would like some feedback on what solution is most acceptable, or any other suggestions.

Neither solution is acceptable. This isn't a close-to-open cache consistency issue.

The syntax of mmap() for both block and NFS mounts is the same: writes are not guaranteed to hit the disk until your application explicitly calls msync().

Quoted 7 lines
Quoted 3 lines

Okay, that makes sense. But if the application never calls msync(), and just munmap()'s the area, when should the changes be written? I did not expect that unmounting just disregards the data.

That suggests that the VM is failing to dirty the pages on munmap() before releasing the vma->vm_file. If so, then that would be a VM bug...

I've checked if the VM tags the pages as dirty:
- f_ops->release() is called on munmap(). An added printk there, shows

    that inode->i_state is set to I_DIRTY_PAGE.

- mapping_tagged(filp->f_mapping, PAGECACHE_TAG_DIRTY) also returns true

Quoted 7 lines

From my understanding this is what the VM is expected to do, and the
pages are marked dirty correctly.

However, nfs_inode->ndirty and nfs_inode->ncommit are both 0. It is unclear to me how the VM is supposed to interact with the nfs_inode. Some clarification or suggestion what to look into would be much appreciated.

The first time the page is touched, it will to trigger a ->pg_mkwrite(), which in the case of NFS will set up the necessary tracking structures to ensure that the page is written out using the correct credentials etc. In the case of NFSv4, it will also ensure that the file doesn't get closed on the server until the page is written out to disk.

Quoted 9 lines

When the page is cleaned (i.e. something calls clear_page_dirty_for_io() as part of a write to disk), the call to page_mkclean() is supposed to re-write-protect the pte, ensuring that any future changes will re-trigger pg_mkwrite().

You should be able to check if/when nfs_vm_page_mkwrite() is triggered using 'rpcdebug -m nfs -s pagecache' to turn on the NFS page cache debugging printks.

Many thanks for the explanation! At the moment I'm a little uncertain where the problem lays, as the problem does not occur with more recent kernels anymore. There likely was some invalid testing on my side :-/

I think I have to hunt down what changes were made and how this affects the writing to mmap()'d files. The explanation you gave helps a lot in understanding how NFS handles this all.

Thanks again, and sorry for any confusion,
Niels
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html