//#include <tourist>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <string>
#include <iomanip>
#include <limits>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <list>
#include <queue>
#include <tuple>
#include <deque>
#include <algorithm>
#include <numeric>
#include <iomanip>
#include <utility>
#include <complex>
#include <functional>
using namespace std;
const int MOD = 1000000007;
typedef long long ll;
typedef pair<ll, ll> p;
const int INF = (1 << 28);
const int dx[4] = {0, 1, 0, -1}, dy[4] = {-1, 0, 1, 0};
const int Dx[8] = {0, 1, 1, 1, 0, -1, -1, -1}, Dy[8] = {-1, -1, 0, 1, 1, 1, 0, -1};
#define yes cout << "Yes" << endl
#define YES cout << "YES" << endl
#define no cout << "No" << endl
#define NO cout << "NO" << endl
#define rep(i, n) for (int i = 0; i < n; i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define INF 2e9
#define ALL(v) v.begin(), v.end()
//setprecision(15)有効数字15桁
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a, ll b)
{
    return a * b / gcd(a, b);
}
struct edge {
    int to, cost;
};
int max_len;
struct DirectedGraph
{
    int V;

    vector<vector<int> > to;
    vector<vector<int> > from;

    vector<int> vs;
    vector<int> depth;
    vector<bool> used;
    vector<int> cmp; //cmp[点]=(トポソ後のその点の順位)

    void dfs(int v)
    {
        used[v] = true;
        for (auto t : to[v])
            if (!used[t])
                dfs(t);
        vs.push_back(v); //帰りがけ
    }
    //k:始点,rdfsがkに戻る→強連結成分
    int r_dfs(int v, int k)
    {
        int ret = 0;
        used[v] = true;
        cmp[v] = k;
        for (const auto t : from[v])
        {
            if (!used[t])
                r_dfs(t, k);
            depth[v] = max(depth[v], depth[t] + 1);
        }
    }
    DirectedGraph(int V) : V(V),
                           to(vector<vector<int> >(V)),
                           from(vector<vector<int> >(V)),
                           cmp(vector<int>(V)),
                           depth(vector<int>(V))
    {
    }

    void add_edge(int from_, int to_)
    {
        to[from_].push_back(to_);
        from[to_].push_back(from_);
    }

    //強連結成分:任意の2頂点を行き来できる部分グラフ
    //          1点だけのも競連結成分
    //順方向帰りがけdfsと逆方向行き止まりまでdfs
    //強連結成分分解O(|V|+|E|)
    int scc_num = -1;
    //scc_num:強連結成分の個数を返す
    int scc()
    {
        used = vector<bool>(V, false);
        vs.clear();
        for (int v = 0; v < V; v++)
            if (!used[v])
                dfs(v);
        used = vector<bool>(V, false);
        scc_num = 0;
        for (int i = vs.size() - 1; i >= 0; i--)
            if (!used[vs[i]])
            {
                max_len = max(max_len, r_dfs(vs[i], scc_num++));
            }
        return scc_num;
    }

    bool isCyclic()
    {
        //強連結成分が全部1点だけのものであれば
        //強連結成分の数は点数と一致し、閉路はない
        return (scc_num != V);
        //true:Dagじゃない
        //false:Dag
    }

    //sccの後潰してトポロジカル順序のグラフを作る
    DirectedGraph makeTopologicalGraph()
    {
        DirectedGraph TG(scc_num);
        rep(s, V) for (auto t : to[s])
        {
            if (cmp[s] != cmp[t])
                TG.add_edge(cmp[s], cmp[t]);
        }
        return TG;
    }
};
int main()
{
    cin.tie(0);
    ios::sync_with_stdio(false);
    int n, m;
    cin >> n>>m;
     DirectedGraph g(n);
    for (int i = 0; i < m; i++)
    {
        int a, b;
        cin >> a>>b;
        a--;
        b--;
        g.add_edge(a,b);
    }
    g.scc();
    int ans=0;
    for(int i=0;i<n;i++){
        ans=max(ans,g.depth[i]);
    }
    cout << ans << "\n";
}



Measured memory_bytes: 13696