// 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.8229454480135217x):
#include<cstdio>

#include<cstring>

#include<iostream>

#include<algorithm>

#include<vector>

#include<queue>

using namespace std;

const int maxn = 1e5 + 5;

vector<int>G[maxn];

queue<int>q;

int in[maxn];

int main(){

	int  n,m;

	scanf("%d%d",&n,&m);

	int x,y;

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

		scanf("%d%d",&x,&y);

		G[x].push_back(y);

		in[y]++;

	}

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

		if(in[i] == 0) q.push(i);

	}

	int ans = 0;

	while(!q.empty()){

		int cnt = q.size();

		//cout << cnt << endl;

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

		int cur = q.front();

		q.pop();

		int len = G[cur].size();

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

			int tmp = G[cur][i];

			in[tmp]--;

			if(in[tmp] == 0) q.push(tmp);

		}

		}

		ans++;

	}

	ans--;

	printf("%d\n",ans);

	return 0;

}