// Slower C++ solution:
//#pragma GCC target ("avx2,sse2")

//#pragma GCC optimization ("Ofast")

//#pragma GCC optimization ("unroll-loops")

#include <bits/stdc++.h>

#include <ext/pb_ds/assoc_container.hpp>

#include <ext/pb_ds/tree_policy.hpp>

using namespace __gnu_pbds;



#define ordered_set tree <pair <ll, int>, null_type, less <pair <ll, int> >, rb_tree_tag, tree_order_statistics_node_update>

#define ll long long

#define ull unsigned long long

#define db long double

#define pb push_back

#define pf push_front

#define ppb pop_back

#define ppf pop_front

#define fi first

#define se second

#define mp make_pair

#define up_b upper_bound

#define low_b lower_bound

#define sz(x) (int)(x).size()

#define all(x) (x).begin(), (x).end()

#define endl "\n"



#define left fsdsdfoisf

#define sum dpsdfioppsf

#define assign xcvjlkdjfio

#define trie fksdfkjkfnjuiv

#define next sidlfjsfkl

#define merge sdfksdkfsldf



//#define int long long



using namespace std;



void dout() {

    cerr << endl;

}

template <typename Head, typename... Tail>

void dout(Head H, Tail... T) {

    cerr << H << ' ';

    dout(T...);

}



//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

typedef pair <int, int> pii;



const int N = 1e5 + 7;

int n, m, dp[N];

vector <int> q[N], r[N], ord;

bool used[N];



void dfs(int v) {

    used[v] = true;

    for (auto i : q[v]) {

        if (!used[i]) {

            dfs(i);

        }

    }

    ord.pb(v);

}



void solve(int tc) {

    // check for (int i = 0; i < n; j++)

    cin >> n >> m;

    for (int i = 1; i <= m; i++) {

        int x, y;

        cin >> x >> y;

        q[x].pb(y);

        r[y].pb(x);

    }

    for (int i = 1; i <= n; i++) {

        if (!used[i]) {

            dfs(i);

        }

    }

    reverse(all(ord));

    int res = 0;

    for (int i = 1; i <= n; i++) {

        int v = ord[i - 1];

        for (auto j : r[v]) {

            dp[v] = max(dp[v], dp[j] + 1);

        }

        res = max(res, dp[v]);

    }

    cout << res;

}



signed main() {

    ios_base::sync_with_stdio(false);

    cin.tie(NULL);

//    freopen("input.txt", "r", stdin);

//    freopen("output.txt", "w", stdout);

    int tc = 1;

//    cin >> tc;

    for (int i = 0; i < tc; i++) {

        solve(i);

//        cleanup();

    }

}


// Faster C++ solution for the same problem (measured speedup: 1.6194066728922356x):
#include <iostream>

#include <vector>

#include <cmath>

#include <set>

#include <queue>

#include <map>

#include <cstdio>

#include <cstring>

#include <string>

#include <stack>

#include <algorithm>

#include <limits.h>

#include <bits/stdc++.h>

#define fw(x) freopen("x.txt","w",stdout)

#define For(i,a,b,c) for(int i = a;i < b;i+=c)

#define REP(i,n) for(int i = 0;i < n;++i)

#define REP1(i,n) for(int i = 1; i<=n;i++)

#define mem(a,s) memset(a,s,sizeof a)

#define fast ios_base::sync_with_stdio(0);cin.tie(NULL)

#define pf printf

#define sc scanf

#define ll long long

#define pii pair<int,int>

#define pll pair<ll,ll>

#define all(v) v.begin(),v.end()

#define vii vector<int>

#define vll vector<ll>

#define vss vector<string>

#define debug(x)  cout<<#x"="<<(x)<<endl

#define pb push_back





using namespace std;





// moves



//int dx[]= {0,0,1,-1};/*4 side move*/

//int dy[]= {-1,1,0,0};/*4 side move*/

//int dx[]= {1,1,0,-1,-1,-1,0,1};/*8 side move*/

//int dy[]= {0,1,1,1,0,-1,-1,-1};/*8 side move*/

//int dx[]={1,1,2,2,-1,-1,-2,-2};/*knight move*/

//int dy[]={2,-2,1,-1,2,-2,1,-1};/*knight move*/





//big_mod



//ll bigmod(ll a,ll b,ll m)

//{if(b == 0) return 1%m;ll x = bigmod(a,b/2,m);x = (x * x) % m;if(b % 2 == 1) x = (x * a)% m;return x;}

//ll BigMod(ll B,ll P,ll M){ ll R=1%M; while(P>0) {if(P%2==1){R=(R*B)%M;}P/=2;B=(B*B)%M;} return R;} /// (B^P)%M



//ll getBit(ll num, int idx) {return ((num >> idx) & 1ll) == 1ll;}

//ll setBit1(ll num, int idx) {return num or (1ll<<idx);}

//ll setBit0(ll num, int idx) {return num & ~(1ll<<idx);}

//ll flipBit(ll num, int idx) {return num ^ (1ll<<idx);}



const int mod = 1000000007;

int n, m, x, y;

vii gr[100001];

int len[100001];

int ans=0;

int dp(int cur){

  if(len[ cur ] == -1){

    int sz = gr[cur].size();

    if(sz>0){

      int mx = 0;

      for(int j=0; j<sz; j++){

        if(len[ gr[cur][j] ] == -1) {

          len[gr[cur][j]] = dp(gr[cur][j]);

          mx = max(mx, len[gr[cur][j]]);

        }

        else mx = max(mx, len[gr[cur][j]]);

      }

      len[cur] = mx+1;

      return len[cur];

    }

    else return 0;

  }

  else return len[cur];

}

int main()

{

    fast;

    mem(len, -1);

    cin>>n>>m;

    REP1(i, m){

      cin>>x>>y;

      gr[x].pb(y);

    }

    for(int i=1; i<=n; i++){

        int sz=gr[i].size();

        if(sz>0){

          int mx=0;

          for(int j=0; j<sz; j++){

            if(len[ gr[i][j] ] == -1) {

              len[gr[i][j]] = dp(gr[i][j]);

              mx = max(mx, len[gr[i][j]]);

            }

            else mx = max(mx, len[gr[i][j]]);

          }

          len[i] = mx+1;

          ans = max(ans, mx+1);

        }

        else len[i] = 0;

    }

    cout<<ans<<'\n';

    return 0;

}