in image/resources/knfsd-agent/mounts.go [93:228]
func readMountStats(proc procfs.Proc, nfsRoot string) (*client.MountStatsResponse, error) {
info, err := proc.MountInfo()
if err != nil {
return nil, err
}
stats, err := proc.MountStats()
if err != nil {
return nil, err
}
type InfoKey struct {
Device string
Mount string
}
// NFS stats only includes super options and is missing some of the
// per-mount options such as noatime. So fetch all the of the options from
// /proc/self/mountinfo.
// This avoids a client needing to query both /mounts and /mountstats and
// having to merge the results locally to get the complete information.
options := make(map[InfoKey]map[string]string)
for _, m := range info {
if !isNFS(m.FSType) {
continue
}
if !strings.HasPrefix(m.MountPoint, nfsRoot) {
continue
}
key := InfoKey{
Device: m.Source,
Mount: m.MountPoint,
}
options[key] = combineMountOptions(m.Options, m.SuperOptions)
}
var mounts []client.MountStats
for _, e := range stats {
if !isNFS(e.Type) {
continue
}
if !strings.HasPrefix(e.Mount, nfsRoot) {
continue
}
m := client.MountStats{
Device: e.Device,
Mount: e.Mount,
Export: e.Mount[len(nfsRoot)-1:],
// lookup the options from mountinfo
Options: options[InfoKey{e.Device, e.Mount}],
}
if s, ok := e.Stats.(*procfs.MountStatsNFS); ok {
// combine the options from the stats with options from mountinfo
m.Options = combineMountOptions(m.Options, s.Opts)
ops := make([]client.NFSOperationStats, len(s.Operations))
for i, o := range s.Operations {
retries := uint64(0)
if o.Transmissions > o.Requests {
retries = o.Transmissions - o.Requests
}
ops[i] = client.NFSOperationStats{
Operation: o.Operation,
Requests: o.Requests,
Transmissions: o.Transmissions,
Retries: retries,
MajorTimeouts: o.MajorTimeouts,
BytesSent: o.BytesSent,
BytesReceived: o.BytesReceived,
QueueMilliseconds: o.CumulativeQueueMilliseconds,
RTTMilliseconds: o.CumulativeTotalResponseMilliseconds,
ExecutionMilliseconds: o.CumulativeTotalRequestMilliseconds,
Errors: o.Errors,
}
}
m.Stats = client.NFSMountStats{
Age: client.Duration(s.Age),
Bytes: client.NFSByteStats{
NormalRead: s.Bytes.Read,
NormalWrite: s.Bytes.Write,
DirectRead: s.Bytes.DirectRead,
DirectWrite: s.Bytes.DirectWrite,
ServerRead: s.Bytes.ReadTotal,
ServerWrite: s.Bytes.WriteTotal,
ReadPages: s.Bytes.ReadPages,
WritePages: s.Bytes.WritePages,
},
Events: client.NFSEventStats{
InodeRevalidate: s.Events.InodeRevalidate,
DnodeRevalidate: s.Events.DnodeRevalidate,
DataInvalidate: s.Events.DataInvalidate,
AttributeInvalidate: s.Events.AttributeInvalidate,
VFSOpen: s.Events.VFSOpen,
VFSLookup: s.Events.VFSLookup,
VFSAccess: s.Events.VFSAccess,
VFSUpdatePage: s.Events.VFSUpdatePage,
VFSReadPage: s.Events.VFSReadPage,
VFSReadPages: s.Events.VFSReadPages,
VFSWritePage: s.Events.VFSWritePage,
VFSWritePages: s.Events.VFSWritePages,
VFSGetdents: s.Events.VFSGetdents,
VFSSetattr: s.Events.VFSSetattr,
VFSFlush: s.Events.VFSFlush,
VFSFsync: s.Events.VFSFsync,
VFSLock: s.Events.VFSLock,
VFSFileRelease: s.Events.VFSFileRelease,
// CongestionWait is not used by the kernel
Truncation: s.Events.Truncation,
WriteExtension: s.Events.WriteExtension,
SillyRename: s.Events.SillyRename,
ShortRead: s.Events.ShortRead,
ShortWrite: s.Events.ShortWrite,
Delay: s.Events.JukeboxDelay,
PNFSRead: s.Events.PNFSRead,
PNFSWrite: s.Events.PNFSWrite,
},
Operations: ops,
}
}
mounts = append(mounts, m)
}
sort.Slice(mounts, func(i, j int) bool {
a := mounts[i].Mount
b := mounts[j].Mount
return a < b
})
return &client.MountStatsResponse{Mounts: mounts}, nil
}