自学内容网 自学内容网

Linux:NFS 无法挂载异常案例 (1)

1. 前言

限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 问题

由于开发的嵌入式板的 NOR FLASH 剩余空间过小,仅剩 65 MB,也没有接口外挂存储设备,且无法通过删减来增大空间,于是打算通过 NFS 远程挂载,于是执行下面命令进行挂载:

# mount -t nfs -o nolock 192.168.0.36:/home/XXX/nfs-share /test/nfs-remote
mount: /test/nfs-remote: bad option; for several filesystems (e.g. nfs, cifs) you might need a /sbin/mount.<type> helper program.

可以看到,mount 程序报错了。笔者有点懵逼,因为这条挂载命令,在一款 Linux 4.19 内核的设备上,曾使用无数次,一直没有任何问题。现在测试的环境,内核版本为 Linux 5.10

3. 分析和解决

遇到问题,愁眉苦脸也没啥用,总要解决。mount 命令本身,不是笔者的第一怀疑对象,毕竟是个老牌应用,在常规操作上出错的可能性不高;更大的可能是 NFS 在高版本内核上的实现差异,先用 strace 大概跟了一下,看有没有什么地方出错了:

# strace mount -t nfs -o nolock 192.168.0.36:/home/XXX/nfs-share /test/nfs-remote
[...]
mount("192.168.0.36:/home/XXX/nfs-share", "/test/nfs-remote", "nfs", 0, "nolock") = -1 EINVAL (Invalid argument)
[...]

可以看到,mount() 系统调用报错,错误码为 EINVAL。再用 ftrace 跟一下 do_mount() 函数(系统调用 mount() 调用了 do_mount()),看哪里出错了(追踪内容有删减,只展示了出错调用的主干流程):

# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 2)               |  do_mount() {
 2)               |    user_path_at_empty() {
 2)               |      getname_flags() {
 2)               |        kmem_cache_alloc() {
 2)   4.667 us    |          should_failslab();
 2) + 14.875 us   |        }
 2) + 23.917 us   |      }
 2)               |      filename_lookup() {
 2)   4.375 us    |        set_nameidata();
 2)               |        path_lookupat() {
 2)               |          path_init() {
 2)   5.250 us    |            __rcu_read_lock();
 2)               |            nd_jump_root() {
 2)   4.375 us    |              set_root();
 2) + 14.000 us   |            }
 2) + 32.667 us   |          }
 2)               |          link_path_walk() {
 2)               |            inode_permission() {
 2)   4.375 us    |              generic_permission();
 2) + 14.000 us   |            }
 2)               |            walk_component() {
 2)               |              lookup_fast() {
 2)   5.833 us    |                __d_lookup_rcu();
 2) + 14.291 us   |              }
 2)   4.667 us    |              step_into();
 2) + 31.791 us   |            }
 2)               |            inode_permission() {
 2)   4.083 us    |              generic_permission();
 2) + 14.292 us   |            }
 2) + 77.875 us   |          }
 2)               |          walk_component() {
 2)               |            lookup_fast() {
 2)   5.833 us    |              __d_lookup_rcu();
 2) + 14.292 us   |            }
 2)   4.375 us    |            step_into();
 2) + 35.583 us   |          }
 2)               |          complete_walk() {
 2)               |            try_to_unlazy() {
 2)   4.375 us    |              legitimize_links();
 2)               |              __legitimize_path() {
 2)   5.834 us    |                __legitimize_mnt();
 2) + 14.584 us   |              }
 2)   4.375 us    |              legitimize_root();
 2)   4.083 us    |              __rcu_read_unlock();
 2) + 52.500 us   |            }
 2)   4.083 us    |            success_walk_trace();
 2) + 71.750 us   |          }
 2)               |          terminate_walk() {
 2)   4.084 us    |            drop_links();
 2)               |            path_put() {
 2)   4.375 us    |              dput();
 2)   4.375 us    |              mntput();
 2) + 23.625 us   |            }
 2) + 40.834 us   |          }
 2) ! 285.833 us  |        }
 2)   4.375 us    |        restore_nameidata();
 2)               |        putname() {
 2)   4.958 us    |          kmem_cache_free();
 2) + 14.875 us   |        }
 2) ! 331.041 us  |      }
 2) ! 368.084 us  |    }
 2)               |    path_mount() {
 2)               |      ns_capable() {
 2)               |        ns_capable_common() {
 2)   4.375 us    |          cap_capable();
 2) + 13.125 us   |        }
 2) + 23.333 us   |      }
 2)               |      get_fs_type() {
 2)               |        __get_fs_type() {
 2)               |          _raw_read_lock() {
 2)   4.375 us    |            preempt_count_add();
 2) + 14.583 us   |          }
 2) + 18.083 us   |          find_filesystem();
 2)   4.958 us    |          try_module_get();
 2)               |          _raw_read_unlock() {
 2)   4.375 us    |            preempt_count_sub();
 2) + 13.125 us   |          }
 2) + 73.208 us   |        }
 2) + 84.292 us   |      }
 2)               |      fs_context_for_mount() {
 2)               |        alloc_fs_context() {
 2)               |          kmem_cache_alloc_trace() {
 2)   4.084 us    |            should_failslab();
 2) + 15.167 us   |          }
 2)               |          get_filesystem() {
 2)   ==========> |
 2)               |            gic_handle_irq() {
 2)               |              __handle_domain_irq() {
 2)   1.750 us    |                irq_find_mapping();
 2)   5.833 us    |                irq_to_desc();
 2)               |                irq_enter() {
 2)               |                  irq_enter_rcu() {
 2)   1.750 us    |                    preempt_count_add();
 2)   5.250 us    |                  }
 2)   8.167 us    |                }
 2)               |                handle_percpu_devid_irq() {
 2)               |                  arch_timer_handler_phys() {
 2)               |                    hrtimer_interrupt() {
 2)               |                      _raw_spin_lock_irqsave() {
 2)   1.750 us    |                        preempt_count_add();
 2)   4.666 us    |                      }
 2)               |                      ktime_get_update_offsets_now() {
 2)   1.459 us    |                        arch_counter_read();
 2)   4.958 us    |                      }
 2)               |                      __hrtimer_run_queues() {
 2)   1.750 us    |                        __remove_hrtimer();
 2)               |                        _raw_spin_unlock_irqrestore() {
 2)   1.459 us    |                          preempt_count_sub();
 2)   4.667 us    |                        }
 2)               |                        tick_sched_timer() {
 2)               |                          ktime_get() {
 2)   1.459 us    |                            arch_counter_read();
 2)   4.667 us    |                          }
 2)               |                          tick_sched_do_timer() {
 2)               |                            tick_do_update_jiffies64() {
 2)               |                              _raw_spin_lock() {
 2)   1.459 us    |                                preempt_count_add();
 2)   4.667 us    |                              }
 2)               |                              do_timer() {
 2)   2.042 us    |                                calc_global_load();
 2)   4.667 us    |                              }
 2)               |                              _raw_spin_unlock() {
 2)   1.458 us    |                                preempt_count_sub();
 2)   4.666 us    |                              }
 2)               |                              update_wall_time() {
 2)               |                                timekeeping_advance() {
 2)               |                                  _raw_spin_lock_irqsave() {
 2)   1.750 us    |                                    preempt_count_add();
 2)   4.667 us    |                                  }
 2)   1.750 us    |                                  arch_counter_read();
 2)   1.458 us    |                                  ntp_tick_length();
 2)   1.458 us    |                                  ntp_tick_length();
 2)               |                                  timekeeping_update() {
 2)   1.750 us    |                                    ntp_get_next_leap();
 2)   1.750 us    |                                    update_vsyscall();
 2)               |                                    raw_notifier_call_chain() {
 2)   1.750 us    |                                      notifier_call_chain();
 2)   4.667 us    |                                    }
 2)   1.750 us    |                                    update_fast_timekeeper();
 2)   1.459 us    |                                    update_fast_timekeeper();
 2) + 21.000 us   |                                  }
 2)               |                                  _raw_spin_unlock_irqrestore() {
 2)   1.458 us    |                                    preempt_count_sub();
 2)   4.666 us    |                                  }
 2) + 46.083 us   |                                }
 2) + 49.000 us   |                              }
 2) + 70.875 us   |                            }
 2) + 73.792 us   |                          }
 2)               |                          tick_sched_handle() {
 2)               |                            update_process_times() {
 2)               |                              account_process_tick() {
 2)               |                                account_system_time() {
 2)               |                                  account_system_index_time() {
 2)   1.458 us    |                                    __rcu_read_lock();
 2)   1.750 us    |                                    __rcu_read_unlock();
 2)               |                                    cpufreq_acct_update_power() {
 2)               |                                      _raw_spin_lock_irqsave() {
 2)   1.459 us    |                                        preempt_count_add();
 2)   4.667 us    |                                      }
 2)               |                                      _raw_spin_unlock_irqrestore() {
 2)   1.459 us    |                                        preempt_count_sub();
 2)   4.375 us    |                                      }
 2) + 14.292 us   |                                    }
 2) + 24.500 us   |                                  }
 2) + 27.417 us   |                                }
 2) + 30.625 us   |                              }
 2)               |                              run_local_timers() {
 2)   1.750 us    |                                hrtimer_run_queues();
 2)   4.958 us    |                              }
 2)               |                              rcu_sched_clock_irq() {
 2)   1.750 us    |                                rcu_is_cpu_rrupt_from_idle();
 2)   1.458 us    |                                rcu_preempt_need_deferred_qs();
 2)   1.459 us    |                                rcu_qs();
 2)   2.042 us    |                                rcu_stall_kick_kthreads();
 2)   1.750 us    |                                rcu_is_cpu_rrupt_from_idle();
 2)   3.208 us    |                                rcu_segcblist_ready_cbs();
 2) + 23.333 us   |                              }
 2)               |                              scheduler_tick() {
 2)   1.750 us    |                                topology_scale_freq_tick();
 2)               |                                _raw_spin_lock() {
 2)   1.750 us    |                                  preempt_count_add();
 2)   4.666 us    |                                }
 2)   1.750 us    |                                update_rq_clock();
 2)               |                                update_thermal_load_avg() {
 2)   1.750 us    |                                  decay_load();
 2)   1.750 us    |                                  decay_load();
 2)   1.458 us    |                                  decay_load();
 2) + 11.083 us   |                                }
 2)               |                                task_tick_fair() {
 2)               |                                  update_curr() {
 2)   1.750 us    |                                    update_min_vruntime();
 2)   1.459 us    |                                    __rcu_read_lock();
 2)   1.458 us    |                                    __rcu_read_unlock();
 2) + 10.792 us   |                                  }
 2)               |                                  __update_load_avg_se() {
 2)   1.458 us    |                                    decay_load();
 2)   1.750 us    |                                    decay_load();
 2)   1.750 us    |                                    decay_load();
 2)               |                                    __accumulate_pelt_segments() {
 2)   1.750 us    |                                      decay_load();
 2)   1.459 us    |                                      decay_load();
 2)   7.583 us    |                                    }
 2) + 20.125 us   |                                  }
 2)               |                                  __update_load_avg_cfs_rq() {
 2)   1.750 us    |                                    decay_load();
 2)   1.458 us    |                                    decay_load();
 2)   1.750 us    |                                    decay_load();
 2)               |                                    __accumulate_pelt_segments() {
 2)   1.458 us    |                                      decay_load();
 2)   1.750 us    |                                      decay_load();
 2)   7.875 us    |                                    }
 2) + 20.417 us   |                                  }
 2)   1.750 us    |                                  update_cfs_group();
 2)   1.750 us    |                                  hrtimer_active();
 2)               |                                  update_curr() {
 2)   2.042 us    |                                    __calc_delta();
 2)   1.459 us    |                                    update_min_vruntime();
 2)   7.875 us    |                                  }
 2)               |                                  __update_load_avg_se() {
 2)   1.459 us    |                                    decay_load();
 2)   1.458 us    |                                    decay_load();
 2)   1.750 us    |                                    decay_load();
 2)               |                                    __accumulate_pelt_segments() {
 2)   1.459 us    |                                      decay_load();
 2)   1.750 us    |                                      decay_load();
 2)   7.583 us    |                                    }
 2) + 19.834 us   |                                  }
 2)               |                                  __update_load_avg_cfs_rq() {
 2)   1.750 us    |                                    decay_load();
 2)   1.458 us    |                                    decay_load();
 2)   1.459 us    |                                    decay_load();
 2)               |                                    __accumulate_pelt_segments() {
 2)   1.459 us    |                                      decay_load();
 2)   1.750 us    |                                      decay_load();
 2)   7.292 us    |                                    }
 2) + 19.542 us   |                                  }
 2)               |                                  update_cfs_group() {
 2)               |                                    reweight_entity() {
 2)   1.458 us    |                                      update_curr();
 2)   4.958 us    |                                    }
 2)   7.584 us    |                                  }
 2)   1.750 us    |                                  hrtimer_active();
 2)   1.750 us    |                                  capacity_of();
 2) ! 131.250 us  |                                }
 2)   1.458 us    |                                calc_global_load_tick();
 2)               |                                _raw_spin_unlock() {
 2)   1.750 us    |                                  preempt_count_sub();
 2)   4.667 us    |                                }
 2)   1.459 us    |                                idle_cpu();
 2)               |                                trigger_load_balance() {
 2)   1.458 us    |                                  nohz_balance_exit_idle();
 2)   1.458 us    |                                  __rcu_read_lock();
 2)   1.458 us    |                                  __rcu_read_unlock();
 2) + 11.375 us   |                                }
 2) ! 185.792 us  |                              }
 2)   1.750 us    |                              run_posix_cpu_timers();
 2) ! 255.500 us  |                            }
 2)   1.458 us    |                            profile_tick();
 2) ! 261.625 us  |                          }
 2)               |                          hrtimer_forward() {
 2)   1.458 us    |                            ktime_add_safe();
 2)   1.459 us    |                            ktime_add_safe();
 2)   7.875 us    |                          }
 2) ! 356.125 us  |                        }
 2)               |                        _raw_spin_lock_irq() {
 2)   1.458 us    |                          preempt_count_add();
 2)   4.666 us    |                        }
 2)   1.459 us    |                        enqueue_hrtimer();
 2) ! 378.000 us  |                      }
 2)               |                      hrtimer_update_next_event() {
 2)               |                        __hrtimer_get_next_event() {
 2)   1.750 us    |                          __hrtimer_next_event_base();
 2)   4.667 us    |                        }
 2)               |                        __hrtimer_get_next_event() {
 2)   1.750 us    |                          __hrtimer_next_event_base();
 2)   4.375 us    |                        }
 2) + 13.709 us   |                      }
 2)               |                      _raw_spin_unlock_irqrestore() {
 2)   1.458 us    |                        preempt_count_sub();
 2)   4.667 us    |                      }
 2)               |                      tick_program_event() {
 2)               |                        clockevents_program_event() {
 2)               |                          ktime_get() {
 2)   1.459 us    |                            arch_counter_read();
 2)   4.667 us    |                          }
 2)   1.458 us    |                          arch_timer_set_next_event_phys();
 2) + 10.792 us   |                        }
 2) + 14.000 us   |                      }
 2) ! 431.083 us  |                    }
 2) ! 434.292 us  |                  }
 2)   1.750 us    |                  gic_eoimode1_eoi_irq();
 2) ! 441.000 us  |                }
 2)               |                irq_exit() {
 2)   1.750 us    |                  preempt_count_sub();
 2)   1.750 us    |                  idle_cpu();
 2)   8.458 us    |                }
 2) ! 475.125 us  |              }
 2) ! 478.625 us  |            }
 2)   <========== |
 2)   5.250 us    |            __module_get();
 2) ! 498.458 us  |          }
 2)   4.375 us    |          __mutex_init();
 2)               |          nfs_init_fs_context() {
 2)               |            kmem_cache_alloc_trace() {
 2)   5.542 us    |              should_failslab();
 2) + 16.625 us   |            }
 2)               |            nfs_alloc_fhandle() {
 2)               |              kmem_cache_alloc_trace() {
 2)   4.083 us    |                should_failslab();
 2) + 14.584 us   |              }
 2) + 22.750 us   |            }
 2) + 53.083 us   |          }
 2) ! 598.208 us  |        }
 2) ! 606.958 us  |      }
 2)               |      put_filesystem() {
 2)   4.375 us    |        module_put();
 2) + 13.125 us   |      }
 2)               |      vfs_parse_fs_string() {
 2)               |        kmemdup_nul() {
 2)               |          __kmalloc_track_caller() {
 2)   4.083 us    |            kmalloc_slab();
 2)   4.375 us    |            should_failslab();
 2) + 21.875 us   |          }
 2) + 30.625 us   |        }
 2)               |        vfs_parse_fs_param() {
 2)   6.125 us    |          lookup_constant();
 2)   5.250 us    |          lookup_constant();
 2)               |          nfs_fs_context_parse_param() {
 2)               |            __fs_parse() {
 2)   4.375 us    |              fs_param_is_string();
 2) + 19.833 us   |            }
 2) + 30.917 us   |          }
 2) + 62.417 us   |        }
 2)   5.250 us    |        kfree();
 2) ! 116.959 us  |      }
 2)               |      parse_monolithic_mount_data() {
 2)               |        nfs_fs_context_parse_monolithic() {
 2)               |          generic_parse_monolithic() {
 2)               |            vfs_parse_fs_string() {
 2)               |              vfs_parse_fs_param() {
 2)   4.375 us    |                lookup_constant();
 2)   4.375 us    |                lookup_constant();
 2)               |                nfs_fs_context_parse_param() {
 2) + 10.209 us   |                  __fs_parse();
 2) + 19.250 us   |                }
 2) + 48.125 us   |              }
 2)   4.667 us    |              kfree();
 2) + 66.500 us   |            }
 2) + 77.875 us   |          }
 2) + 87.209 us   |        }
 2) + 95.667 us   |      }
 2)               |      mount_capable() {
 2)               |        capable() {
 2)               |          ns_capable() {
 2)               |            ns_capable_common() {
 2)   5.541 us    |              cap_capable();
 2) + 14.000 us   |            }
 2) + 22.750 us   |          }
 2) + 31.209 us   |        }
 2) + 41.125 us   |      }
 2)               |      vfs_get_tree() { // error
 2)               |        nfs_get_tree() {
 2)   4.375 us    |          nfs_verify_server_address();
 2) + 14.000 us   |        }
 2) + 22.750 us   |      }
 2)               |      put_fs_context() {
 2)               |      }

结合 mount() 系统调用代码路径:

sys_mount()
do_mount()
path_mount()
do_new_mount()

static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
int mnt_flags, const char *name, void *data)
{
...
type = get_fs_type(fstype);
...
fc = fs_context_for_mount(type, sb_flags);
put_filesystem(type);
...
if (!err && name)
err = vfs_parse_fs_string(fc, "source", name, strlen(name));
if (!err)
err = parse_monolithic_mount_data(fc, data);
...
if (!err && !mount_capable(fc))
err = -EPERM;
if (!err)
err = vfs_get_tree(fc);
if (!err)
err = do_new_mount_fc(fc, path, mnt_flags);

put_fs_context(fc);
return err;
}

mount() 系统调用代码路径了解到,函数 vfs_get_tree() 调用后,没出错的情况应该调用 do_new_mount_fc() ,但 ftrace 跟踪到的流程是在 vfs_get_tree() 后接着调用了 put_fs_context(),这表示 vfs_get_tree() 调用出错了。为什么?看一下 vfs_get_tree() 的实现:

int vfs_get_tree(struct fs_context *fc)
{
...
error = fc->ops->get_tree(fc); /* NFS: nfs_get_tree() */
...
}
static int nfs_get_tree(struct fs_context *fc)
{
struct nfs_fs_context *ctx = nfs_fc2context(fc);
int err = nfs_fs_context_validate(fc);
...
}

static int nfs_fs_context_validate(struct fs_context *fc)
{
struct nfs_fs_context *ctx = nfs_fc2context(fc);
...
struct sockaddr *sap = (struct sockaddr *)&ctx->nfs_server.address;
...

if (!nfs_verify_server_address(sap))
goto out_no_address;

...
out_no_address:
return nfs_invalf(fc, "NFS: mount program didn't pass remote address");// 返回 EINVAL 错误码
...
}

static int nfs_verify_server_address(struct sockaddr *addr)
{
switch (addr->sa_family) {
case AF_INET: {
struct sockaddr_in *sa = (struct sockaddr_in *)addr;
return sa->sin_addr.s_addr != htonl(INADDR_ANY);
}
case AF_INET6: {
struct in6_addr *sa = &((struct sockaddr_in6 *)addr)->sin6_addr;
return !ipv6_addr_any(sa);
}
}

dfprintk(MOUNT, "NFS: Invalid IP address specified\n");
return 0;
}

原因是 nfs_verify_server_address() 没有检查到合法的地址协议簇,导致出错,返回错误码 EINVAL 。那谁设置了 addr->sa_family ?在挂载期间,nfs_init_fs_context()addr->sa_family 的初始值为 0

sys_mount()
do_mount()
path_mount()
do_new_mount()
fs_context_for_mount()
alloc_fs_context()
init_fs_context = fc->fs_type->init_fs_context; /* NFS: nfs_init_fs_context() */
nfs_init_fs_context()
static int nfs_init_fs_context(struct fs_context *fc)
{
struct nfs_fs_context *ctx;

/*
 * 整个 nfs_fs_context 初始为 0,包括
 * nfs_fs_context::nfs_server::address::sa_family,
 * 即 nfs_verify_server_address() 调用中的 addr->sa_family 。
 */
ctx = kzalloc(sizeof(struct nfs_fs_context), GFP_KERNEL);
...
}

mount 时想设置 addr->sa_family 参数,可以通过挂载选项 addr=<IP地址> 来完成:

static int nfs_fs_context_parse_param(struct fs_context *fc,
      struct fs_parameter *param)
{
...
opt = fs_parse(fc, nfs_fs_parameters, param, &result);
...
switch (opt) {
...
case Opt_addr:
len = rpc_pton(fc->net_ns, param->string, param->size,
       &ctx->nfs_server.address,
       sizeof(ctx->nfs_server._address));
if (len == 0)
goto out_invalid_address;
ctx->nfs_server.addrlen = len;
break;
...
}
...
}

按代码分析,按如下重新调整命令行选项:

# mount -t nfs -o addr=192.168.0.36,nolock 192.168.0.36:/home/XXX/nfs-share /test/nfs-remote

挂载成功。对比前面的挂载命令,这里增加了 addr=192.168.0.36 挂载选项,来显式的指定 NFS 服务端IPv4 地址。当然这个也可通过 addr= 选项指定 IPv6 风格地址。

最后,值得一提的是,可以通过内核配置项 CONFIG_SUNRPC_DEBUG,启用 NFS 的调试信息。需要通过操作 /proc/sys/sunrpc/nfs_debug 来指定输出的调试信息类型,如通过下面命令启用 NFS 挂载相关的调试信息:

echo 0x0400 > /proc/sys/sunrpc/nfs_debug

0x0400NFSDBG_MOUNT,这些值定义在 include/uapi/linux/nfs_fs.h 中:

/*
 * NFS debug flags
 */
#define NFSDBG_VFS0x0001
#define NFSDBG_DIRCACHE0x0002
#define NFSDBG_LOOKUPCACHE0x0004
#define NFSDBG_PAGECACHE0x0008
#define NFSDBG_PROC0x0010
#define NFSDBG_XDR0x0020
#define NFSDBG_FILE0x0040
#define NFSDBG_ROOT0x0080
#define NFSDBG_CALLBACK0x0100
#define NFSDBG_CLIENT0x0200
#define NFSDBG_MOUNT0x0400
#define NFSDBG_FSCACHE0x0800
#define NFSDBG_PNFS0x1000
#define NFSDBG_PNFS_LD0x2000
#define NFSDBG_STATE0x4000
#define NFSDBG_ALL0xFFFF

当然,自然也少不了 NFS 定义的 tracepoint 等一些其他调试接口,对这些细节感兴趣的读者可自行查阅相关资料和源码。


原文地址:https://blog.csdn.net/JiMoKuangXiangQu/article/details/143489460

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!